在 C 中对链表进行排序

Posted

技术标签:

【中文标题】在 C 中对链表进行排序【英文标题】:Sorting a linked list in C 【发布时间】:2012-08-02 13:30:57 【问题描述】:

我正在尝试通过找到最大值,将其从其位置删除,然后将其插入列表顶部来对链表进行排序。

我遇到的困难是在顶部实际删除和插入。问题似乎出在 sortList 函数中包含的 while 循环中的 if 条件中,但我不确定如何解决。

任何帮助将不胜感激。

#include <stdio.h>
#include <stdlib.h>

typedef struct node
    int num;
    struct node *next;
 Node, *NodePtr;

void printList(NodePtr np);
NodePtr makeList(void);
NodePtr makeNode(int n);
NodePtr sortList(NodePtr list);

int main(void) 
    NodePtr list;
    printf("Enter numbers for the list (0 to end)\n");
    list = makeList();
    printList(list);
    list = sortList(list);
    printList(list);
    return 0;


NodePtr makeList(void) 
    NodePtr makeNode(int), np, top, last;
    int n;
    top = NULL;
    if(scanf("%d", &n) != 1)n = 0;
    while(n != 0) 
        np = makeNode(n);
        if(top == NULL)top = np;
        else last->next = np;
        last = np;
        if(scanf("%d", &n)!=1)n=0;
    
    return top;



void printList(NodePtr np) 
    while(np != NULL) 
        printf("%d\n", np->num);
        np = np->next;
    


NodePtr makeNode(int n) 
    NodePtr np = (NodePtr)malloc(sizeof(Node));
    np->num = n;
    np->next = NULL;
    return np;


NodePtr sortList(NodePtr list) 
    NodePtr top = list;
    NodePtr curr = NULL;
    NodePtr largest;
    NodePtr prev;
    prev = NULL;
    curr = top;
    largest = top;

    while(curr != NULL) 
        prev = curr;
        if(curr->num > largest->num) 
            largest = curr;
            prev->next = curr->next;
            largest->next = top;
        
        curr = curr->next;
    
    if(prev == NULL) 
        largest->next = top;
        return largest;
    
    return largest;

【问题讨论】:

C语言中链表排序有很多问题;其中许多在页面的 RHS 上列为相关问题。您是否查看过其中任何一个以了解它们是否与您的问题相关? 【参考方案1】:
// Program to sort a single linked list in ascending order
// (without exchanging data in the nodes)
/**************************************************************************

There are two methods of sorting presented here(At a time,we can use any of
these two functions to sort our single linked list.) -

1. Function 'void Sort()' - This function uses selection sort method(I
                            think).
   In this function,a node whose data is the smallest in the list is made
   as 'head' node(i.e. starting node of the list) by scanning the whole list
   once.Then from the remaining list,again a node with the smallest data is
   found out whose address is kept in the 'next' field of previous node(head
   node).This process  continues to sort the whole list.
2. Function 'void Sort_method2()' - This function uses insertion sort
                                    method(I think).
   In this function,starting from second node in the list, all previous node
   data(starting from 'head' node) are compared with current reference node
   (which is initially second node in the list).If 'data' field of current
   reference node is smaller than that of any of its previous nodes,then
   suitable changes in the 'next' field of corresponding nodes are made.If
   data in the current reference node is smaller than that in the 'head' node,
   then the current reference node is made as 'head' node.

   *********************************************************************/

#include<stdio.h>
#include<conio.h>
#include<alloc.h>
#include<stdlib.h>

struct node

 int data;
 struct node *next;
;

struct node *head,*head1;

void Create_node(int data);
void display();
void Sort();
void Sort_method2();


void main()

 int choice,d;
 clrscr();
 while(1)
 
  printf("\n  1.Create new node");
  printf("\n  2.Sort in ascending order");
  printf("\n  3.Exit");
  printf("\nEnter your choice : ");
  scanf("%d",&choice);

   switch(choice)
   
     case 1: printf("\nEnter data :");
             scanf("%d",&d);
             Create_node(d);
             break;
     case 2: Sort();       // At a time,we can use any of these two
             //Sort_method2();  // functions to sort our single linked list.
             break;
     case 3: exit(0);
     default:exit(0);
    
   // end of while(1)
   // end of main()

//--------------------------------------------
void Create_node(int d)

  struct node *newnode,*temp;
  newnode = (struct node *)malloc(sizeof(struct node));
  newnode -> data = d;
  newnode -> next = NULL;
  if(head == NULL)
     head = newnode;
  else
    
      temp = head;
      while(temp -> next   !=   NULL)
        temp = temp -> next;

      temp -> next = newnode;
      // end of 'else'
  // end of 'Create_node(int d)'

//---------------------------------------------
void display()  // Print linked list contents

   struct node *temp;
   printf("\nList contents are :\n");
   temp = head;
   while(temp != NULL)
   
     printf(" Data = %d   Address = %u\n",temp->data,temp);
     temp = temp->next;
   
   printf("\n");

//--------------------------------------------
void Sort()
 
  struct node *t,*t1,*t2,*t3;
  t1 = head;
  head1 = head;
  if(head == NULL)
    printf("\nThe linked list is empty!");
  else
  
    while( (t2 = t1 -> next)   !=   NULL)
    
      while(t2 != NULL)
      
        t3 = t2 -> next;
        if( t1 -> data   >   t2 -> data)
        
          t2 -> next = t1;
          for(t = t1; t -> next != t2;t = t -> next);

          t -> next = t3;
          t1 = t2;       // t1 = Node with smaller data
          t2 = t3;       // t2 = Node to be compared with t1
          // end of 'if'
        else
        
          // t1 = t1;       // That is,no change in t1.
          t2 = t3;
        
        // end of ' while(t2 != NULL)'

      if(head == head1) // We want this action only for first pass of
                       // outer while() loop.Only initially, head = head1.
       head = t1;
       head1 = t1 -> next;
        // end of 'if(head == head1)'
      else
      
        for(t = head;t -> next != head1; t = t -> next);

        t -> next = t1;
        head1 = t1 -> next;
       // end of 'else'

      t1 = t1 -> next;
     // end of 'while( (t2 = t1 -> next)   !=   NULL)'

    display();  // Display the list.
     // end of 'else' of 'if(head == NULL)'
    // end of 'Sort()'

//--------------------------------------------
void Sort_method2()

 struct node *t,*t1,*t2,*tt;
 if(head == NULL)
    printf("\nThe linked list is empty!");
 else
 
   t1 = head -> next;
   while(t1 != NULL)                         // This is i-loop(outer loop).
   
     t2 = t1 -> next;
     for(t = head; t != t1; t = t -> next)   // This is j-loop(inner loop).
     
       if(t1->data  <  t->data)
       
         t1 -> next = t;
         for(tt=head; tt->next != t1; tt=tt->next); //end of for loop in 'if'

         tt -> next = t2;
         if(t == head)
           head = t1;  // There is only one statement in this 'if'.
         else  // i.e.,'if(t != head)'
         
           for(tt=head; tt->next != t; tt=tt->next);

           tt -> next = t1;
         
         break;
         // end of 'if'
         // end of outer 'for' loop
     t1 = t2;
         // end of 'while'

  display(); // Display the list.
         // end of 'else' of 'if(head == NULL)'
         // end of 'Sort_method2()'

【讨论】:

【参考方案2】:

这是我尝试使用 QuickSort 算法对单链表进行排序的尝试。如果您知道 n,那么运行时间将为 O(n log n)。检查这是否有帮助。

#include "malloc.h"

typedef struct node 
    struct node *next;
    int val;
 node;

bool insert_node(struct node **head, int val)

    struct node *elem;
    elem = (struct node *)malloc(sizeof(struct node));
    if (!elem)
        return false;
    elem->val = val;
    elem->next = *head;
    *head = elem;
    return true;


int get_lval(struct node *head, int l)

    while(head && l) 
        head = head->next;
        l--;
    
    if (head != NULL)
        return head->val;
    else
        return -1;


void swap(struct node *head, int i, int j)

    struct node *tmp = head;
    int tmpival;
    int tmpjval;
    int ti = i;
    while(tmp && i) 
        i--;
        tmp = tmp->next;
    
    tmpival = tmp->val;
    tmp = head;
    while(tmp && j) 
        j--;
        tmp = tmp->next;
    
    tmpjval = tmp->val;
    tmp->val = tmpival;
    tmp = head;
    i = ti;
    while(tmp && i) 
        i--;
        tmp = tmp->next;
    
    tmp->val = tmpjval;



struct node *Quick_Sort_List(struct node *head, int l, int r)

    int i, j;
    int jval;
    int pivot;
    i = l + 1;
    if (l + 1 < r) 
        pivot = get_lval(head, l);
        printf("Pivot = %d\n", pivot);
        for (j = l + 1; j <= r; j++) 
            jval = get_lval(head, j);
            if (jval < pivot && jval != -1) 
                swap(head, i, j);
                i++;
            
        
        swap(head, i - 1, l);
        Quick_Sort_List(head, l, i);
        Quick_Sort_List(head, i, r);
    
    return head;


struct node *Sort_linkedlist(struct node *head)

    struct node *tmp = head;
    // Using Quick sort.
    int n = 0;

    while (tmp) 
        n++;
        tmp = tmp->next;
    
    printf("n = %d\n", n);
    head = Quick_Sort_List(head, 0, n);
    return head;


void print_list(struct node *head)

    while(head) 
        printf("%d->", head->val);
        head = head->next;
    
    printf("\n");


int _tmain(int argc, _TCHAR* argv[])

    struct node *head = NULL;
    struct node *shead = NULL;

    insert_node(&head, 10);
    insert_node(&head, 12);
    insert_node(&head, 9);
    insert_node(&head, 11);
    insert_node(&head, 7);
    insert_node(&head, 1);
    insert_node(&head, 3);
    insert_node(&head, 8);
    insert_node(&head, 5);
    insert_node(&head, 2);
    insert_node(&head, 4);
    insert_node(&head, 6);
    print_list(head);

    shead = Sort_linkedlist(head);
    print_list(shead);

    return 0;

【讨论】:

【参考方案3】:

sortList 函数存在问题。

这个函数只把一些大节点放在列表的开头。它并没有列出所有列表。您可以使用排序算法对文件进行排序:快速排序/冒泡排序/... 我在这个答案的末尾放了一个代码。

这里有一个代码来做列表的排序:

//它用第一个替换最大的节点,然后用子列表(列表第一个元素)做同样的操作

NodePtr sortList(NodePtr list) 


// 
if(list == null || list->next == null)
    return list; // the list is sorted.

//replace largest node with the first : 

//1- find largest node : 
NodePtr curr, largest,largestPrev;
curr = list;
largest = list;
prev = list;
largestPrev = list;
while(curr != NULL) 
        if(curr->num > largest->num) 
            largestPrev = prev;
            largest = curr;
        
        prev = curr;
        curr = curr->next;

    
//largest node is in largest. 

//2- switching firt node and largest node : 
NodePtr tmp;
if(largest != list)

    largestPrev->next = list;
    tmp = list->next;
    list->next = largest->next;
    largest->next = tmp;


// now largest is the first node of the list.

// calling the function again with the sub list :
//            list minus its first node :
largest->next = sortList(largest->next);


return largest;

【讨论】:

请更正您的代码(prev 始终指向答案中的尾部),您必须添加一个新指针 maximum_prev 并将其设置为 prev,以防出现:if(curr->num > maximum- >num),然后用它代替 prev (largest_prev->next = list) @TOC:你,完成。更易读的代码是使用搜索最大元素的函数和切换节点的函数。 当您有这样的列表时,此算法不起作用:524361【参考方案4】:

以下是您的排序逻辑中存在的一些问题:

    您在循环本身的开头将 prev 指针设置为 curr,这是不正确的。通过这样做,您使当前指针和前一个节点指针相同,从而无法删除该节点。 您应该将最大的指针也分配给顶部,从而可以将最大的->下一个节点设置为真正的顶部节点。

代码可以修改如下(只是一个指针,其他问题需要自己检查):

while(curr != NULL)


    if(curr->num > largest->num)
    
        largest = curr;
        prev->next = curr->next;
        largest->next = top;
        top = largest;

    
    prev = curr;
    curr = curr->next;

【讨论】:

还有一个指针问题,curr 会一直在顶部:larger 指向与 curr 相同的节点。您将最大-> 放在顶部,然后将 curr->next 指向顶部。当你写 curr = curr->next 时,你实际上把 top 放在了 curr 中(因为你已经把 top 写进了最大(此时最大 = curr))。所以你从顶部无限重启。【参考方案5】:

通过写信给largest-&gt;next,您覆盖了curr-&gt;next。所以你最终总是从顶部重新开始。

确保:

    列表保持一致 您的列表迭代器保持一致

但总的来说,您的代码似乎严重损坏,我相信您的排序逻辑中可能还有其他一些错误。

【讨论】:

以上是关于在 C 中对链表进行排序的主要内容,如果未能解决你的问题,请参考以下文章

使用插入排序对链表进行并行排序

C语言如何对链表的数进行排序?

c语言对链表的数据排序的问题,分不是问题!

对链表进行排序的函数

四种语言刷算法之对链表进行插入排序

c_cpp 使用常量空间复杂度在O(n log n)时间内对链表进行排序。