Sorting linked list by manipulating pointers - c

I'm trying to implement Shell sort on a linked list. I divide my original linked list into sub linked list, which contain nodes that have 'k' gap regarding the shell sort algorithm. I want to sort the sub linked list by manipulating the 'next' pointers instead of changing its data field. So I have a sortList function that traverses the linked list and swaps the nodes with swapNodes if it encounters any unordered nodes.
When I pass an unordered linked list with two elements to the sortList I keep loosing one of the nodes in my list. For example, I have 50 and -84 in my list, I pass it to sortList. After the sortList figures that they're unordered it calls swapNodes, but once swapNodes terminates, the resulting list only has 50.
I tried to gdb and found out that when I'm in swapNodes scope the list gets sorted successfully without losing a node, but when it terminates and turns back to sortList scope, both the head and curr points only to 50 and their 'next' field is NULL.
My functions:
void sortList(Node * head, long * n_comp) {
Node * curr;
int didSwap = 1;
while(didSwap) {
didSwap = 0;
for(curr = head; curr -> next != NULL; ) {
*n_comp += 1; //number of comparison
if(curr->value > curr->next->value) {
swapNodes(curr, curr->next, &head);
didSwap = 1;
}
curr = curr -> next;
if (!curr) break;
}
}
}
void swapNodes(Node * p1, Node * p2, Node ** start)
{
Node *p1pre = NULL;
Node *p1curr = *start;
while (p1curr && p1curr!=p1)
{
p1pre = p1curr;
p1curr = p1curr->next;
}
Node *p2pre = NULL;
Node *p2curr = *start;
while (p2curr && p2curr != p2)
{
p2pre = p2curr;
p2curr = p2curr->next;
}
if (p1pre != NULL)
{
p1pre->next = p2curr;
}
else
{
*start = p2curr;
}
if (p2pre != NULL)
{
p2pre->next = p1curr;
}
else
{
*start = p1curr;
}
Node *temp = p2curr->next;
p2curr->next = p1curr->next;
p1curr->next = temp;
return;
}
``

It's happening for the same reason why you gave start type Node** in the first place: your node [50] is being moved and head moves with it instead of staying at the start of the list. You need to change your sortList method so the parameter head also has type Node**:
void sortList(Node ** head, long * n_comp) {
Node * curr;
int didSwap = 1;
while(didSwap) {
didSwap = 0;
for(curr = *head; curr -> next != NULL; ) {
std::cout<< "It: " << *n_comp<< std::endl;
*n_comp += 1; //number of comparison
if(curr->value > curr->next->value) {
swapNodes(curr, curr->next, head);
didSwap = 1;
}
curr = curr -> next;
if (!curr) break;
}
}
}
Now your head will stay at the start of the list.
Note: this is one of the reasons why sentinel nodes are a thing.

Related

quicksort with partition using linked list

I try to implement quicksort with partition using linked list. I did it few times with regular array and I got the idea well, but with linked list it is really different. I
did the first part and than I got stuck.
This is the point I stick let say I have an array { 5, 3, 7, 1, 9, 8, 2, 5, 6 }
so the pivot is always the first number - 5;
I build new 2 array one with numbers lower than five (include 5) and another with number bigger than 5 and after that rebuild the main list like that { 3, 1, 2, 5, 5, 7, 9, 8, 6 };
the best result I got until now after 3 runs is 1 -> 2 -> 3 -> 5 -> 5 -> 7 -> 9 -> 8 -> 6;
but because I keeping calling 1 as the partition I get the same result.
I have 2 function partition and quicksort - quicksort need to be recursive function and it my main problem to implement it.
the flag == 1 if array (big/small) isn't empty
my code:
void partition(list **lst, list **pivot, list **small, list **big) {
list *temp = *lst;
list *pv = *lst;
*pivot = pv;
int big_flag = 0;
int small_flag = 0;
*small = (list *)(malloc(sizeof(list)));
list *n_small = *small;
list *prev_small = NULL;
*big = (list *)(malloc(sizeof(list)));
list *n_big = *big;
list *prev_big = NULL;
int p = pv->data;
pv = pv->next;
while (pv) {
if (pv->data <= p) {
n_small->data = pv->data;
prev_small = n_small;
n_small->next = (list *)(malloc(sizeof(list)));
n_small = n_small->next;
small_flag = 1;
} else {
n_big->data = pv->data;
prev_big = n_big;
n_big->next = (list *)(malloc(sizeof(list)));
n_big = n_big->next;
big_flag = 1;
}
pv = pv->next;
}
pv = *lst; // Move the pointer back to the start;
if (small_flag == 1) {
n_small = prev_small;
n_small->next = NULL;
prev_small = *small;
// Built the new lst by order
while (prev_small) {
pv->data = prev_small->data;
pv = pv->next;
prev_small = prev_small->next;
}
}
// add the pivot to the array
pv->data = p;
pv = pv->next;
if (big_flag == 1) {
n_big = prev_big;
n_big->next = NULL;
prev_big = *big;
while (prev_big) {
pv->data = prev_big->data;
pv = pv->next;
prev_big = prev_big->next;
}
}
}
Here I'm really not sure what need to be my stop condition
void quickSortList(list **lst) {
list *temp = *lst;
list *big;
list *small;
list *pivot;
while (temp->next != NULL) {
partition(lst, &pivot, &small, &big);
quickSortList(&small);
quickSortList(&big);
}
}
Quicksort on an array works like this:
do nothing if the array has no or only one element. Otherwise:
pick a pivot and move it out of the way;
partition the array: elements that are less than the pivot go to the left;
put the pivot between the two partitions; this is its final position in the sorted array;
sort the left and right partitions.
This algorithm does not need extra memory. It works in place and all movements are element swaps.
Quicksort on a linked list works like this:
do nothing if the array has no or only one element. Otherwise:
pick a pivot and move it out of the way;
partition the array by moving the elements to one of two new lists, depending on whether the element is less than the pivot or not;
sort the left and right partitions;
concatenate the lists: elements less + pivot + other elements
This algorithm does not need extra memory. It works in place and all movements are adjustments to the head pointers and next field. Only the links change. The nodes stay in their place.
The major difference between these two variants is when you sort the partitions. Array elements are indexed by position, so the position of the partition must be known before sorting, so we place the pivot first.
Linked-list elements are indexed by following the links. The head and tail nodes must be known before sorting, so we must sort first. Then we can concatenate the lists.
So let's write our function:
void quicksort(Node **head);
Wait a moment: Later we need to concatenate linked lists, which means that we need to find the tail of the list. It's a singly linked list, so we must traverse it. We can save some cycles by returning the tail of the sorted list, so:
Node *quicksort(Node **head);
Okay, the base cases first:
if (*head == NULL) return NULL;
if ((*head)->next == NULL) return *head;
We need two lists:
Node *lt = NULL; // less than pivot
Node *ge = NULL; // greater than or equal to pivot
The pivot is the first node, so the *head. The pivot will not be partitioned, so we start partitioning at the node after that:
Node *node = (*head)->next;
We walk the linked list and remove each node. Then we insert that node at the head of one of the two partition lists. (It's easier and faster to insert at the front, but doing so will make the algorithm "unstable", that is the order of two equal elements is not retained. Let's not worry about that right now.)
while (node) {
Node *next = node->next;
if (node->data < (*head)->data) {
node->next = lt;
lt = node;
} else {
node->next = ge;
ge = node;
}
node = next;
}
We must save next to a temporary, because we are overwriting the next field of the node we have just moved.
Now sort the partitions:
Node *ltail = quicksort(&lt);
Node *gtail = quicksort(&ge);
Nothe that both lt and gt (and therefore ltail and gtail) may be NULL. We have the following lists with {head, tail}:
{lt, ltail} + {pivot, pivot} + {ge, gtail}
The pivot is *head. If ltail is not NULL, ltail->next = pivot and pivot->next = ge, which may be NULL or not. Finally, *head must be the overall new head:
(*head)->next = ge;
if (gtail == NULL) gtail = *head;
if (lt) {
ltail->next = *head;
*head = lt;
}
After this little dance, *head is the new head of the list and gtail is the new tail.
Here's everything put together:
Node *quicksort(Node **head)
{
// base cases: empty list and single node
if (*head == NULL) return NULL;
if ((*head)->next == NULL) return *head;
// partition with *head as pivot
Node *lt = NULL; // less than pivot
Node *ge = NULL; // greater than or equal to pivot
Node *node = (*head)->next;
while (node) {
Node *next = node->next;
if (node->data < (*head)->data) {
node->next = lt;
lt = node;
} else {
node->next = ge;
ge = node;
}
node = next;
}
// quick-sort recursively
Node *ltail = quicksort(&lt);
Node *gtail = quicksort(&ge);
// rearrange lists: lt -> pivot -> ge
*head = *head;
(*head)->next = ge;
if (gtail == NULL) gtail = *head;
if (lt) {
ltail->next = *head;
*head = lt;
}
return gtail;
}
And here is a complete small example.
Two final remarks: You can make the algorithm stable by inserting at the end of the partition lists. And you don't need two partition lists: You could also extract the nodes that are less from the original list and insert it to the le list. The reiaining elements are the other partition and if you do your extraction right, pivot->next has already the correct value. Memory-wise using just one list won't buy you anything.)
Your implementation has multiple problems:
your quickSortList() function would run forever because you do not update temp in the body of the loop. You should test if the list has at least 3 elements and there is no need for a while loop.
you do not recombine the sublists after recursing, so the sort cannot succeed.
the partition function should not allocate memory, it should merely distribute the nodes into 3 sublists: a list of nodes with smaller data, one with nodes with data equal to the pivot's and one with larger data. Note however that the first and last sublists may be empty.
Here is a full implementation that performs stable sorting, which is possible for linked lists but usually not for arrays as it is more costly.
#include <stdio.h>
#include <stdlib.h>
typedef struct list {
struct list *next;
int data;
} list;
void partition(list **lst, list **pivot, list **small, list **big) {
list *cur = *lst;
list *pivot_head = cur, **pivot_link = &cur->next;
list *small_head = NULL, **small_link = &small_head;
list *big_head = NULL, **big_link = &big_head;
int data = pivot_head->data;
/* distribute the list into 3 sublists */
while ((cur = cur->next) != NULL) {
if (cur->data == data) {
*pivot_link = cur;
pivot_link = &cur->next;
} else
if (cur->data < data) {
*small_link = cur;
small_link = &cur->next;
} else {
*big_link = cur;
big_link = &cur->next;
}
}
/* close the sublists */
*pivot_link = NULL;
*small_link = NULL;
*big_link = NULL;
/* return the sublist heads */
*pivot = pivot_head;
*small = small_head;
*big = big_head;
}
list *appendList(list *a, list *b) {
if (a) {
list *node = a;
while (node->next)
node = node->next;
node->next = b;
return a;
} else {
return b;
}
}
void quickSortList(list **lst) {
list *temp = *lst, *big, *small, *pivot;
if (temp && temp->next) {
partition(lst, &pivot, &small, &big);
quickSortList(&small);
quickSortList(&big);
*lst = appendList(small, appendList(pivot, big));
}
}
list *newList(int data) {
list *node = malloc(sizeof(*node));
node->data = data;
node->next = NULL;
return node;
}
void freeList(list **lst) {
list *cur = *lst;
*lst = NULL;
while (cur) {
list *next = cur->next;
free(cur);
cur = next;
}
}
void printList(const list *node) {
for (; node; node = node->next)
printf(" %d", node->data);
printf("\n");
}
int main() {
list *head = NULL, *tail = NULL, *node;
int p;
while (scanf("%d", &p) == 1) {
node = newList(p);
if (head == NULL) {
tail = head = node;
} else {
tail = tail->next = node;
}
}
printList(head);
quickSortList(&head);
printList(head);
freeList(&head);
return 0;
}
I kept your API for quickSortList and partition but it would be more efficient to keep track of tail nodes everywhere to avoid the extra scan in appendList().
If you can change the API, here is an alternative without extra scans:
/* sort the list and return a pointer to the tail node next pointer */
list **quickSortList(list **lst) {
list *cur = *lst;
if (cur == NULL) {
return NULL;
} else
if (!cur->next) {
return &cur->next;
} else {
list *pivot = cur, **pivot_link = &cur->next;
list *small = NULL, **small_link = &small;
list *big = NULL, **big_link = &big;
int data = pivot->data;
/* distribute the list into 3 sublists */
while ((cur = cur->next) != NULL) {
if (cur->data == data) {
*pivot_link = cur;
pivot_link = &cur->next;
} else
if (cur->data < data) {
*small_link = cur;
small_link = &cur->next;
} else {
*big_link = cur;
big_link = &cur->next;
}
}
*small_link = NULL;
if (small) {
small_link = quickSortList(&small);
*lst = small;
*small_link = pivot;
} else {
*lst = pivot;
}
*big_link = NULL;
if (big) {
big_link = quickSortList(&big);
*pivot_link = big;
return big_link;
} else {
*pivot_link = NULL;
return pivot_link;
}
}
}

i want to make sure that my linked list work

Is this a right way to do a linked list ? I am having a problem in a big school project and now i want to make sure that this is true.
void addnode(int a){
struct house* tmp = houses[i].next;
while (tmp != NULL) {
tmp = tmp->next;
}
tmp = (struct house*)malloc(sizeof(struct house));
tmp->id=a;
tmp->next=NULL;
}
i figured out that the error can be in other parts of the code. Now i will share the parts i suspect i hope you can help me.
houses[i] is an array of linked lists. if houses[i].id==-1 it is empty
struct house get_house_byid(int id) {
for (int i = 0; i < 1000; i++) {
if (houses[i].id != -1) {
if (houses[i].id == id) {
return houses[i];
}
if (houses[i].next != NULL) {
struct house* tmp = houses[i].next;
while (tmp != NULL) {
if (tmp->id == id) {
return *tmp;
}
tmp = tmp->next;
}
}
}
}
struct house housep;
housep.id = -1;
return housep;//if it cant find that id it returns housep
}
There may be other issues with your code that is not shown, but there are issues with addnode:
addnode does not set the head of the list (i.e. houses[i].next).
Thus, the newly added node is never connected to anything [and is a memory leak].
Ignoring the [obvious] typo/syntax error: void addnode{int a} instead of void addnode(int a).
The loop on tmp discards the pointer to the tail of the list. We need a separate variable (e.g. prev).
Note that i is global. That's fine, but the function would be cleaner if i was an argument to addnode instead.
Don't cast the return of malloc: Do I cast the result of malloc?
Here's is some refactored code. It is annotated:
void
addnode(int i,int a)
{
struct house *tmp;
struct house *prev;
// find the tail of the list
prev = NULL;
for (tmp = houses[i].next; tmp != NULL; tmp = tmp->next)
prev = tmp;
// allocate the new node
tmp = malloc(sizeof(*tmp));
tmp->id = a;
tmp->next = NULL;
// append to the tail of the [non-empty] list
if (prev != NULL)
prev->next = tmp;
// add to front of the empty list
else
houses[i].next = tmp;
}

A flexible list with indefinite number of nodes

I am supposed to make a linked list that takes in strings and prints them out in the reverse order. Normally I'd ask the number of nodes that need to be created, and then ask for the data in a for loop until we're done.
typedef struct word_st {
string word; // string is meant to be a pointer to a struct
word_st *next;
}
But the problem is, the number of nodes isn't known until runtime. So I have to keep asking for data until the user is done. I'm not really sure where to start/how to do that and can't seem to find anything on the internet either. So a hint would be very helpful.
I have my insert function and the print function looks fairly simple too.
word_t *insert_2(word_t* head, string text) {
word_t * p = NULL;
word_t * temp = (word_t*) malloc(sizeof(word_t));
temp -> word = text;
temp -> next = NULL;
if(head == NULL) {
head = temp;
} else {
p = head;
} while(p -> next != NULL) {
p = p -> next;
}
p -> next = temp;
return head;
}
In reverse, replace next by prev:
typedef struct word_st {
string word; // string is meant to be a pointer to a struct
word_st * prev;
}
And the function:
word_t *insert_2(word_t* head, string text) {
word_t * nextHead = (word_t*) malloc(sizeof(word_t));
nextHead -> word = text;
nextHead -> prev = NULL;
// Check first element of LIFO
if( head == NULL ) {
return nextHead;
}
nextHead -> prev = head;
return nextHead;
}
I hope It compiles and work.
Note:
for(word_t * head = last ; head->prev != NULL ; head = head->prev )
{
// Do the job
;
}

Issues with linked list and pointers (C)

I am writing a C program to sort a linked list according to the largest values. I met an issue whereby the program just hangs when the program reached "prevPtr->next = headPtr".
I want the prevPtr->next to equate to headPtr, if the sum of prevPtr is larger than the sum of headPtr, however the program just hangs there.
compareNodes() function is used to compare the nodes to see if newNode has the same name as any other structs in the linked list, then it will add in the sum.
sortSimilarNodes() function is used to sort the nodes according to the sum of each struct.
The struct is here below:
struct purchase {
char name[30];
double sum;
struct purchase * next;
} ;
LOG * compareNodes(LOG * headPtr, char * name, char * price){
.
.
.
while (curPtr != NULL) {
if (strcmp(newNode->name, curPtr->name)==0) {
curPtr->sum += newNode->sum;
free(newNode);
similar = 1;
break;
}
//advance to next target
prevPtr = curPtr;
curPtr = curPtr->next;
}
/*if (curPtr == NULL){
if(strcmp(newNode->name, prevPtr->name)==0){
prevPtr->sum += newNode->sum;
free(newNode);
similar = 1;
}
}*/
if (similar == 1){
headPtr = sortSimilarNodes(curPtr, headPtr);
}
else{
headPtr = sortNodes(newNode, headPtr);
}
return headPtr;
}
LOG * sortSimilarNodes(LOG * newPtr, LOG * headPtr){
LOG * curPtr;
LOG * prevPtr;
if(headPtr->sum < newPtr->sum){
newPtr->next = headPtr;
return newPtr;
}
prevPtr = headPtr;
curPtr = headPtr->next;
while (curPtr == NULL){
}
while (curPtr != NULL){
if(strcmp(curPtr->name, newPtr->name)==0){
break;
}
prevPtr = curPtr;
curPtr = curPtr->next;
}
return headPtr;
}
This is the output of the program.
Thank you!
It's hard to tell from your code, because you haven't posted all of it, but you seem to have some misconceptions about linked lists. In particular:
There is no need for new nodes unless you really add new nodes to the list. That also means that you don't call malloc except when adding nodes. (There's no malloc in your code, but a suspicious free in your comparison function. Comparing does not involve creating or destroying anything; it just means to look what is already there.)
A corollary to the first point is that there should be no nodes in an empty list, not even dummy nodes. An empty list is a list whose head is NULL. Make sure that you initialise all head pointers before creating a new list:
LOG *head = NULL; // empty list
When you sort the list, the order of the list has changed and the old head is invalid. You cater for that by returning the new head:
head = sort(head);
But that seems redundant and it also seems to imply that the two pointers can be different. That's not the case, because the old pointer will point somehwre in the sorted list, not necessarily at its head. It's probably better to pass the head pointer's address in order to avoid confusion:
sort(&head);
Sorting linked lists can be tricky. One straightforward way is selection sort: Find the node with the highest value, remove it from the original list and add it at the front of a new list. Repeat until there are no more nodes in the original list.
Adding a new node n at the front of a list given by head is easy:
n->next = head;
head= n;
Adding a new node at the end of a list that is given by head is a bit more involved:
LOG **p = &head;
while (*p) p = &(*p)->next;
*p = n;
n->next = NULL;
Here, p is the address of the pointer that points to the current node, *p. After walking the list, that address is either the address of the head node (when the list is empty) or the address of the next pointer of the precedig node.
You could achieve something similar by keeping a prev pointer, but the pointer-to-pointer solution means that you don't have to treat the cases where there is no previous node specially at the cost of some extra & and * operators.
With that, your sorting routine becomes:
void sortByName(LOG **head)
{
LOG *sorted = NULL;
while (*head) {
LOG **p = head; // auxiliary pointer to walk the list
LOG **max = head; // pointer to current maximum
LOG *n; // maximum node
while (*p) {
if (strcmp((*p)->name, (*max)->name) > 0) max = p;
p = &(*p)->next;
}
n = *max;
*max = (*max)->next;
n->next = sorted;
sorted = n;
}
*head = sorted;
}
If you want to sort by sum, change the comparison to:
if ((*p)->sum > (*max)->sum) max = p;
Call the function like this:
LOG *head = NULL;
insert(&head, "apple", 2.3);
insert(&head, "pear", 1.7);
insert(&head, "strawberry", 2.2);
insert(&head, "orange", 3.2);
insert(&head, "plum", 2.1);
sortByName(&head);
print(head);
destroy(&head);
with the insert, destroy and print functions for completeness:
void insert(LOG **head, const char *name, double sum)
{
LOG *n = malloc(sizeof(*n));
if (n) {
snprintf(n->name, sizeof(n->name), "%s", name);
n->sum = sum;
n->next = *head;
*head = n;
}
}
void destroy(LOG **head)
{
LOG *n = *head;
while (n) {
LOG *p = n;
n = n->next;
free(p);
}
*head = NULL;
}
void print(LOG *l)
{
while (l) {
printf("%s: %g\n", l->name, l->sum);
l = l->next;
}
puts("");
}

Creating a sublist of the original list?

I'm trying to make a function that takes two parameters positions to take from list and original list and then copy indexed numbers to a list. Also i included the Struct for the list and the head. I get the EXC_BAD_ACCESS Error i commented the line.
code:
struct node_struct {
Item item;
link next;
};
struct list_struct {
link first;
int length;
};
list sublist(list A, list pos_list) {
int index = 0;
link tempForindex = malloc(sizeof *tempForindex);
link temp2 = malloc(sizeof *temp2);
list finale = malloc(sizeof *finale);
link temp3 = malloc(sizeof *temp3);
tempForindex = pos_list->first;
temp2 = A->first;
temp3 = finale->first;
int counter = 0;
while(tempForindex->next != NULL)
{
index = tempForindex->item;//EXC_BAD_ACCESS code1
counter = 0;
while(temp2->next != NULL)
{
if (counter == index)
{
temp3->item = temp2->item;
temp2 = A->first;
temp3 = temp3->next;
break;
}
temp2 = temp2->next;
counter++;
}
tempForindex = tempForindex->next;
}
return finale;
}
It all hopelessly flawed.
You need to get patterns going with linked lists. The structures are fine, but the fact that the code is crashing in the position indicated suggests to me that pos_list has not been set up correctly.
iterate through a list using this pattern
list mylist;
link ptr;
for(ptr = mylist->first; ptr != NULL; ptr = ptr->next)
{
/* loop body */
}
now write a test function to print out both arguments and ascertain that they are valid.

Resources