问题
It might be for pedantic purposes but not homework. I have below question about 'Sorting a singly linked list' (Any kind of list for that matter) Assume for this questions we want to sort in ascending order of values in each node.
Does this question expect me to just sort the list by swapping the values of each node in the list so that the values are in some order(ascending/descending). Here the original values of nodes (before sorting) would be changed in order to reflect the sorted list. Here the same earlier head pointer is returned but now having the smallest element in it, which might be different than its earlier element.
Like this C code I have below(The code below might not compile as is , but I have it working fine. Posted here as illustrative to clear my point):
struct lnk_lst
{
int val;
struct lnk_lst *next;
};
main()
{
//Assume curr is the head node of the singly linked list created with some values
curr = llist_bubble_sort(curr);
}
struct lnk_lst* llist_bubble_sort(struct lnk_lst *lst)
{
int i,n=0,tmpint;
struct lnk_lst *tmp=lst,*tmp2=lst;
while(tmp->next)
{
lst = tmp2;
while(lst->next)
{
if(lst->val > lst->next->val)
{
tmpint = lst->val;
lst->val = lst->next->val;
lst->next->val = tmpint;
}
lst = lst->next;
}
tmp = tmp->next;
}
return tmp2;
}
OR
Is it expected to that the pointer to the node with the smallest element(assuming ascending order) in the original ordering is moved as new head node, then the node having the next smallest element is linked to the head node, and so on such that the list is reordered completely and now the head node returned is not same pointer as earlier.
If the interpretation of list sort is this second, then I have to see how yet get the idea done in code.
回答1:
The whole idea behind linked lists is that the links can be changed without affecting the content. Changing a pointer sometimes involves creating a pointer to pointer variable. Also: if you only swap the contents, you could just as well leave out the ->next pointers, use an array instead, and swap the array's contents.
IMHO the natural way of sorting a linked list is mergesort. The fragment below splits the list in two part: the nodes that are already in place, and those that are not. The second list is sorted, and the two lists are merged. Both splitting&merging involve some pointer-to-pointer variables.
#include <stdio.h>
#include <string.h>
struct llist {
struct llist *next;
char *payload;
};
int llist_cmp(struct llist *l, struct llist *r);
struct llist * llist_split(struct llist **hnd
, int (*cmp)(struct llist *l, struct llist *r) );
struct llist * llist_merge(struct llist *one, struct llist *two
, int (*cmp)(struct llist *l, struct llist *r) );
struct llist * llist_sort(struct llist *ptr
, int (*cmp)(struct llist *l, struct llist *r) );
struct llist * llist_split(struct llist **hnd, int (*cmp)(struct llist *l, struct llist *r) )
{
struct llist *this, *save, **tail;
for (save=NULL, tail = &save; this = *hnd; ) {
if (! this->next) break;
if ( cmp( this, this->next) <= 0) { hnd = &this->next; continue; }
*tail = this->next;
this->next = this->next->next;
tail = &(*tail)->next;
*tail = NULL;
}
return save;
}
struct llist * llist_merge(struct llist *one, struct llist *two, int (*cmp)(struct llist *l, struct llist *r) )
{
struct llist *result, **tail;
for (result=NULL, tail = &result; one && two; tail = &(*tail)->next ) {
if (cmp(one,two) <=0) { *tail = one; one=one->next; }
else { *tail = two; two=two->next; }
}
*tail = one ? one: two;
return result;
}
struct llist * llist_sort(struct llist *ptr, int (*cmp)(struct llist *l, struct llist *r) )
{
struct llist *save;
save=llist_split(&ptr, cmp);
if (!save) return ptr;
save = llist_sort(save, cmp);
return llist_merge(ptr, save, cmp);
}
int llist_cmp(struct llist *l, struct llist *r)
{
if (!l) return 1;
if (!r) return -1;
return strcmp(l->payload,r->payload);
}
struct llist lists[] =
{{ lists+1, "one" }
,{ lists+2, "two" }
,{ lists+3, "three" }
,{ lists+4, "four" }
,{ lists+5, "five" }
,{ lists+6, "six" }
,{ lists+7, "seven" }
,{ lists+8, "eight" }
,{ NULL, "nine" }
};
int main()
{
struct llist *root,*tmp;
root = lists;
fprintf(stdout, "## %s\n", "initial:" );
for (tmp=root; tmp; tmp=tmp->next) {
fprintf(stdout, "%s\n", tmp->payload);
}
fprintf(stdout, "## %s\n", "sorting..." );
root = llist_sort(root, llist_cmp);
for (tmp=root; tmp; tmp=tmp->next) {
fprintf(stdout, "%s\n", tmp->payload);
}
fprintf(stdout, "## %s\n", "done." );
return 0;
}
RESULT:
## initial:
one
two
three
four
five
six
seven
eight
nine
## sorting...
eight
five
four
nine
one
seven
six
three
two
## done.
回答2:
The correct way of sorting a linked list is the second one. This also makes sense because often you would want to sort objects on some attribute while retaining their other attribute values. In that case, the first approach doesn't help much.
The idea is similar to traditional sorting approaches. For example you can use insertion sort by iterating through the elements and then inserting the next element in the right position in the correctly sorted linked list upto that element. For detailed code in C, you can see the Wikipedia page here.
来源:https://stackoverflow.com/questions/8961851/what-is-expected-when-i-am-told-sort-a-singly-linked-list