I have created a linked list of 5 nodes of type:
typedef struct node
{
int i;
struct node* link;
}node;
node* head = NULL;
When printing out, this gives:
4 3 2 1 0
The head pointer is set to point at 4. I have then written a function to bubble sort the linked list as follows:
void sort(void)
{
node* cur = head;
node* next = cur->link;
node* prev = NULL;
while(cur->i > next->i)
{
printf("cur is greater than next\n");
while(prev != head)
{
cur->link = next->link;
next->link = cur;
head = next;
next = cur->link;
prev = head;
}
while(next != NULL)
{
prev->link = next;
cur->link = next->link;
next->link = cur;
prev = next;
next = cur->link;
}
printf("second while loop exited\n");
for (node* ptr = head; ptr != NULL; ptr = ptr->link)
{
printf("%d", ptr->i);
}
cur = head;
next = cur->link;
}
}
There are various printf statements to check that the program is working. What I find is that after the first run-through, 4 is successfully bubbled up as follows:
3 2 1 0 4
However, after re-setting the cur pointer to 3 and next to 2, the next run-through provides the following:
2 1 0 4 3
Ultimately, we finish with
0 4 3 2 1
So as can be seen "3", "2" and "1" are being bubbled up too far. I have tried various conditonals in place of the third while loop to correct this but in the majority of cases this leads to seg faults. Of course, the other thing here is that my logic could be completely wrong and there may be a better way to implement this. Could you get away with just swapping the contents of nodes and not pointers themselves? Any help would be much appreciated. Thanks in advance
Ordinary bubble-sort implementations for sorting arrays make use of the direct addressing and a known size of the array: they naturally use indices, that is ordinal numbers of items, so they can easily shrink the area sorted as the work progresses, because they know how many items are already on their final places.
A linked list is processed purely sequentially, so it doesn't allow such simple optimization without adding artificial 'index', incremented along the list iteration. That's why it's easiest to iterate always through the whole list and terminate when no more items were swapped, hence the list is sorted:
void sort(void)
{
int swapped = 1;
while(swapped)
{
node **prev = &head, *curr, *next;
swapped = 0;
for(curr = head; curr; prev = & curr->link, curr = curr->link)
{
next = curr->link;
if(next && curr->i > next->i)
{
curr->link = next->link;
next->link = curr;
*prev = next;
swapped = 1;
}
}
}
}
EDIT – some explanations in reply to questions in Matthew2015 comments.
Logical conditions in C expect a numeric or pointer expression which are considered 'true' if they are different from zero or different from NULL, respectively. That means while(swapped) is essentially equivalent to while(swapped != 0) and next && ... is equivalent to next != NULL && .... The condition in while(swapped != 0) means the loop will terminate when some execution of internal for does not set swapped to 1, which happens when no item in the list is greater than its successor – that is, when the list is sorted.
The for loop condition expression is curr alone, equivalent to curr != NULL. That makes the for loop iterate along the list until there is no 'current' node.
The node **prev variable points to a pointer, which points to the current node. When the 'current' and the 'next' node need to be swapped, then the 'previous' link should no longer point to the 'current' node but to the 'next' node instead. Of course one might keep the pointer to the 'previous node' and assign a new value to the (previous node)->link — but that would not work in case of the first node in a list, which has no 'previous node' but is pointed to by the head variable. One must use additional condition to verify if the current node is the first node to resolve this inconsistency. Having a pointer to pointer, which originally points to head and then to 'previous node'.link makes the whole code much simpler, shorter and also a bit faster.
I would look at the third while
while(next != NULL)
{
prev->link = next;
cur->link = next->link;
next->link = cur;
prev = next;
next = cur->link;
}
Here you're always moving elements without testing whether they have to be moved - i.e. cur->i > next->i.
By the way, if it's guard is true, the second while gets executed only once and so it's the same as an if so I would use an if, at least for clarity reasons.
Related
Welp I'm back. I am now trying to do a bubble sort with a node swap. There is a problem in my algorithm that i cannot determine. When I remove the bool condition to determine if the list is sorted, the program produces some output, indicating the loop condition is failing to a problem in the algorithm. i have diagrammed out what happens on paper, with each pointer as it is reassigned. I envisioned nodes a b c d, where b and c are swapped. the sort function is below. i have made notes for every action in an attempt to figure what i am doing wrong. no dice, any guidance would be greatly appreciated, thank you.
void sortList(void)
{
struct Node *before, *after;
struct Node * temp;
bool sorted = false;
while (sorted == false)
{
//test completion
sorted = true; //if this isn't changed by the below statement the list is sorted
temp = head; //reinitializes where there list sorts from
while (temp->next != NULL) //stops at the end of the list
{
if (temp->data > temp->next->data)
{
//test the value of the nodes to see if they need a sort
before = temp->prev; //"back" pointer for subject (b)
after = temp->next; //"forward" pointer for subject
if (before != NULL)
{
before->next = after; //covers swap being made at beginning
}
temp->next = after->next; //points to after next
after->next->prev = temp; //sets prev pointer for "d"
temp->prev = after; //points to what after pointed
after->next = temp; //places node after next one
after->prev = before; //ditto
sorted = false; //flagged, the list is not sorted
}
temp = temp->next; //moves through the list
}
}
}
There are three main problems in the original sortList function:
It doesn't update head when moving the first node in the list. (Fix: add an else part to update head.)
after->next->prev dereferences a null pointer at the end of the list. (Fix: check after->next != NULL first.)
temp = temp->next dereferences a null pointer at the end of the list and skips a position when not at the end of the list. The code that swaps the temp node with the following node already advances temp one position through the list, so it should not be done again. (Fix: move temp = temp->next; into an else part executed only when the temp node does not need to be swapped.)
Here is an updated version of sortList() with the changes marked:
void sortList(void)
{
struct Node *before, *after;
struct Node * temp;
bool sorted = false;
while (sorted == false)
{
//test completion
sorted = true; //if this isn't changed by the below statement the list is sorted
temp = head; //reinitializes where there list sorts from
while (temp->next != NULL) //stops at the end of the list
{
if (temp->data > temp->next->data)
{
//test the value of the nodes to see if they need a sort
before = temp->prev; //"back" pointer for subject (b)
after = temp->next; //"forward" pointer for subject
if (before != NULL)
{
// temp is not at beginning of list
before->next = after;
}
else
{
// *** Fix 1 ***
// temp is at beginning of list
head = after;
}
temp->next = after->next; //points to after next
if (after->next != NULL) // *** Fix 2 ***
{
after->next->prev = temp; //sets prev pointer for "d"
}
temp->prev = after; //points to what after pointed
after->next = temp; //places node after next one
after->prev = before; //ditto
sorted = false; //flagged, the list is not sorted
}
else // *** Fix 3 ***
{
temp = temp->next; //moves through the list
}
}
}
}
EDIT
The above version of sortList fails when head is NULL (an empty list) due to temp->next dereferencing a null pointer. One way to fix it is to mark an empty list as already sorted by changing the initialization of sorted as follows:
bool sorted = (head == NULL);
The amount of work done by sortList can be halved (approximately) by reducing the number of iterations of the inner loop by one after for each iteration of the outer loop. This can be done as follows:
Add a new variable done to mark the start of the already sorted portion at the end of the list. Initialize it to NULL near the top of the function (before the outer loop):
struct Node *done = NULL;
Change the condition for the inner loop to terminate at the end of the unsorted portion of the list:
while (sorted == false)
{
//test completion
sorted = true; //if this isn't changed by the below statement the list is sorted
temp = head; //reinitializes where there list sorts from
while (temp->next != done) // stops at the sorted portion of the list
{
When the inner loop has terminated (at the end of the outer loop), the temp node and everything beyond it is now sorted, so make done point to this node:
}
done = temp; // everything from here is already sorted
}
}
i have a linked list like this :
1,jhon,19
2,sara,18
3,tom,20
4,jack,22
i have been trying forever to delete an element based on their id (witch is the first number). But in order to do that i need to delete this element from any position. so i came up with this code and i was wondering if it's correct :
temp1=head;
if(head!=NULL && head->id==givenID) // if the element is in the first position
{
temp = head;
head = head->next;
free(temp);
}
else if(head!=NULL && head->id!=givenID){// search for the element in the middle
do{
temp2=head;
head = head->next;
}while(head->id !=givenID && head->next !=NULL);
if(head->next !=NULL && head->id==givenID){// if the element is in the middle
temp2->next=head->next;
free(head);
head=temp1;
}
else if(head->next ==NULL && head->id==givenID){// if the element is in the last position
temp->next=NULL;
free(head);
head=temp1;
}
}
Thank you
This code is too complex, because it has unnecessary branches. You can unify your code by using a pointer to pointer.
The idea is to point your pointer to pointer to the head of the list, then to the next pointer of the initial element of the list, then to the next pointer of the second element of the list, and so on. The beauty of this approach is that no matter where you are in your list, the operation on a pointer to pointer remains the same!
Here is how it looks in code:
// Point your pointer to pointer to the head of the list
struct node **pptr = &head;
while (*pptr != NULL) {
// Dereference pptr to get the pointer to current node
node *current = *pptr;
// Check if the id of this node matches what we're looking for
if (current->id == givenID) {
// Here is the "magic": assign the next pointer of the current node
// to whatever is pointed to by pptr.
// It could be a head, or a next of some node.
*pptr = current->next;
free(current);
break;
}
pptr = &(current->next);
}
That's it! Since the pointer to pointer does not differentiate between head and other nodes, there is no additional checking going on.
Consider using a sentry node. All special cases disappear when you do.
This is how node erasure look in a linked list with sentry:
Iterator Erase( List* lst, Iterator here )
{
Iterator nxt = here->next;
Link( here->prev, here->next );
free( here );
lst->size -= 1;
return nxt;
}
with Link being no more complicated than
void Link( Iterator n1, Iterator n2 )
{
n1->next = n2;
n2->prev = n1;
}
All the other core functions, like insert etc. are similarly trivial.
I'm having trouble figuring out how to insert an element into a sorted list. I am new to linked lists and I'm still having trouble.The following function takes an predefined list and an element as arguments. I have white boarded the whole thing but I still can't figure it out. Thank you for your help.
/*
* function: lst_insert_sorted
*
* description: assumes given list is already in sorted order
* and inserts x into the appropriate position
* retaining sorted-ness.
* Note 1: duplicates are allowed.
*
* Note 2: if given list not sorted, behavior is undefined/implementation
* dependent. We blame the caller.
* So... you don't need to check ahead of time if it is sorted.
*/
void lst_insert_sorted(LIST *l, ElemType x) {
NODE *p = l->front;
NODE *temp;
NODE *current = p;
NODE *prev;
NODE *next;
if (p->val >= x) { // Base Case if
p->val = x;
}
while (p !=NULL) {
prev = current;
temp = prev->next;
next = current->next;
if (next->val >= x) {
temp->val = x;
}
}
return 0;
}
You did not show how NODE is defined. So I suppose that the list is a single-linked list. In this case the function can look like
void lst_insert_sorted( LIST *l, ElemType x )
{
NODE *current = l->front;
NODE *prev = NULL;
while ( ( current != NULL ) && !( x < current->val ) )
{
prev = current;
current = current->next;
}
NODE *node = malloc( sizeof( NODE ) );
node->val = x;
node->next = current;
if ( prev != NULL )
{
prev->next = node;
}
else
{
l->front = node;
}
}
Generally speaking, a linked list consists of "nodes" joined together in sequence by links pointing from each node to the next one (or to the previous one, or both). The nodes each point also (perhaps trivially) to one of the actual elements of the list.
To insert an element into a linked list at a given position, you just create a node pointing to that element, and update the other pointers as needed. With a doubly-linked list such as yours (where each node points both to the next one and to the previous one), you must
update the next pointer of the node immediately preceding the insertion position to point at the new node,
update the prev pointer of the node immediately following the insertion position to point at the new node, and
set the prev and next pointers of the new node to point to these other nodes.
There are typically special cases for inserting at the beginning or end of the list; details depend on your list and node implementation.
In your case, you must also find an appropriate insertion point. Since the list is sorted, you can just traverse it from the beginning, comparing each node's element to the one to be inserted, until you find the right spot. Such a "linear search" is not terribly efficient if the list is long, but you cannot do better with a generic linked list.
if (p->val >= x) { // Base Case if
p->val = x;
}
there is a loss data so you wrote the x with overwriting in to first data in the list.
İf ı understood the problem you should create a node and insert this to list.
the following is a function that reverses elements of a link list k elements at a time,
My question is whether the function could crash if i were to pass null as head, because, next is never initialized to any value in this case and since it may be pointing to a garbage value, the if(next!= null) may be satisfied, so the statement head->next can be executed, when head is actually null, causing the program to crash?
struct node *reverse (struct node *head, int k)
{
struct node* current = head;
struct node* next;
struct node* prev = NULL;
int count = 0;
/*reverse first k nodes of the linked list */
while (current != NULL && count < k)
{
next = current->next;
current->next = prev;
prev = current;
current = next;
count++;
}
/* next is now a pointer to (k+1)th node
Recursively call for the list starting from current.
And make rest of the list as next of first node */
if(next != NULL)
{ head->next = reverse(next, k); }
/* prev is new head of the input list */
return prev;
}
If you pass head as NULL, you skip the while loop, and the statement if(next != NULL)would compare an uninitialized pointer to NULL, which is undefined behavior.
So yes, your program could crash.
What an unitialized pointer contains is not specified, and is implementation dependent.
The strict answer to your quesion is: no, not likely.
Given head == 0 and k > 0, you'll end up at line 21 in your function with next != 0 (most likely) but with some garbage random value (as you pointed out) that will be used for your recursive call.
Your code may very well not crash the first time through, but it's very likely to crash at some point, or continue to recurse for some unknown time. In any event, you won't get the results you expect.
I'm writing a hashtable as an array of linked lists.Currently I'm trying to have a simple hash table where the key is the index of the array and value is a singly linked list for implementing chaining.
This is my code to delete a node:
Basic Struct:
struct Node
{
int value;
int page;
struct Node *next;
};
int searchAndDelete(int frame,int page,int delete)
{
struct Node** iter;
iter=&hashtable[(page-1)%7];
struct Node** prev=iter;
for(;*iter;iter=&(*iter)->next)
{
if(page==((*iter)->page))
{
if(frame==((*iter)->value))
{
if(delete)
{
(*prev)->next=(*iter)->next;
free(*iter);
}
return 1;
}
}
prev=iter;
}
return 0;
}
For insertion please take a look here, AddNode
When I'm deleting a node, the value for that changes to 0. When I search for the node it gives back that node is not preset aka 0 as output from the function.
Are there any mistakes in my code which I haven't thought about?Am I leaving any memory leaks or any other problems?
Edit
Added this piece of code to the delete function:
int searchAndDelete(int frame,int page,int delete)
{
struct Node** iter;
iter=&hashtable[(page-1)%7];
struct Node** prev=iter;
struct Node** curr=iter;
for(;*curr;curr=&(*curr)->next)
{
if(page==((*curr)->page))
{
if(frame==((*curr)->value))
{
if(delete)
{
if(curr==iter)
{
iter=(*curr)->next;
free(*curr);
}
else
{
(*prev)->next=(*curr)->next;
free(*curr);
}
}
return 1;
}
}
prev=curr;
}
return 0;
}
Problem I'm seeing is that when I delete the first time, the element is not freed, it's value is set to 0, but it still says in the linked list. In the second deletion the value of the last elements goes to some garbage and hence that element will never be deleted in my comparison checks. Can someone shed light on what I might be doing here?
If the hash table you're using is seven elements wide (i.e. 0..6 for indexes), and from your AddNode code, it appears it is, then the arithmetic you're using is suspect for the initial iterator find.
iter=&hashtable[page-1%7];
should likely be:
struct Node** iter = hashtable + (page % 7);
This will give you the address of the element in your hash table at the page location modulus 7, i.e. [0..6].
Also, your delete from your hash table head node doesn't account for clearing the table element itself. You may need to (a) set it to null, or (b) chain in the next ptr. Do that as well. You have the ability to since the hash table and the initial node pointer are both available.
EDIT: OP asked for sample. This is just a quick jot of how this can be done. I'm sure there are plenty of better ways, maybe even ones that compile. This assumes both the page AND frame must match EXACTLY for a node to be considered delete'able.
void searchAndDelete(int frame, int page, int del)
{
struct Node** head = hashtable + (page % hashtable_size);
struct Node* curr = *head;
struct Node* prev = NULL;
while (curr)
{
// if they match, setup for delete.
if ((curr->page == page) && (curr->value == frame) && del)
{
// so long as the header pointer is the active node prev
// will be NULL. move head along if this is the case
if (prev == NULL)
*head = curr->next;
// otherwise, the previous pointer needs it next set to
// reference the next of our vicitm node (curr)
else
prev->next = curr->next;
// victim is safe to delete now.
free(curr);
// set to the new head node if we just deleted the
// old one, otherwise the one following prev.
curr = (prev == NULL) ? *head : prev->next;
}
else
{ // no match. remember prev from here on out.
prev = curr;
curr = curr->next;
}
}
}
Eh, close enough =P
I see couple of issues:
mod operator % needs parenthesis. So change iter=&hashtable[page-1%7]; to iter=&hashtable[(page-1)%7];
Handle the case when you will delete 1st element in linked list. In such cases prev will be same as iter so (*prev)->next=(*iter)->next; will not make any different. You need to update the array to store next element aka (*iter)->next.