链接列表合并排序说明

时间:2012-02-23 03:26:48

标签: algorithm sorting linked-list mergesort

有人可以向我解释一下这段代码的工作原理:

http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.c

我不明白这篇文章中使用的算法。感谢

2 个答案:

答案 0 :(得分:2)

合并排序通常是排序链表的首选。链表的缓慢随机访问性能使得其他一些算法(例如快速排序)表现不佳,而其他算法(例如heapsort)完全不可能。

让head成为要排序的链表的第一个节点,headRef是指向head的指针。请注意,我们需要在MergeSort()中引用head,因为下面的实现会更改下一个链接以对链接列表进行排序(而不是节点上的数据),因此如果原始头部的数据不是最小值,则必须更改头节点在链表中。

归并(headRef)

1)如果head为NULL或链接列表中只有一个元素     然后回来。

2)否则将链表分成两半。       FrontBackSplit(head,& a,& b); / * a和b是两半* /

3)对两半a和b进行排序。       归并的(a);       归并(B);

4)合并已排序的a和b(使用此处讨论的SortedMerge())    并使用headRef更新头指针。      * headRef = SortedMerge(a,b);





    /* Link list node */
    struct node
    {
        int data;
        struct node* next;
    };

    /* function prototypes */
    struct node* SortedMerge(struct node* a, struct node* b);
    void FrontBackSplit(struct node* source,
              struct node** frontRef, struct node** backRef);

    /* sorts the linked list by changing next pointers (not data) */
    void MergeSort(struct node** headRef)
    {
      struct node* head = *headRef;
      struct node* a;
      struct node* b;

      /* Base case -- length 0 or 1 */
      if ((head == NULL) || (head->next == NULL))
      {
        return;
      }

      /* Split head into 'a' and 'b' sublists */
      FrontBackSplit(head, &a, &b); 

      /* Recursively sort the sublists */
      MergeSort(&a);
      MergeSort(&b);

      /* answer = merge the two sorted lists together */
      *headRef = SortedMerge(a, b);
    }

    /* See http://geeksforgeeks.org/?p=3622 for details of this
       function */
    struct node* SortedMerge(struct node* a, struct node* b)
    {
      struct node* result = NULL;

      /* Base cases */
      if (a == NULL)
         return(b);
      else if (b==NULL)
         return(a);

      /* Pick either a or b, and recur */
      if (a->data data)
      {
         result = a;
         result->next = SortedMerge(a->next, b);
      }
      else
      {
         result = b;
         result->next = SortedMerge(a, b->next);
      }
      return(result);
    }

    /* UTILITY FUNCTIONS */
    /* Split the nodes of the given list into front and back halves,
         and return the two lists using the reference parameters.
         If the length is odd, the extra node should go in the front list.
         Uses the fast/slow pointer strategy.  */
    void FrontBackSplit(struct node* source,
              struct node** frontRef, struct node** backRef)
    {
      struct node* fast;
      struct node* slow;
      if (source==NULL || source->next==NULL)
      {
        /* length next;

        /* Advance 'fast' two nodes, and advance 'slow' one node */
        while (fast != NULL)
        {
          fast = fast->next;
          if (fast != NULL)
          {
            slow = slow->next;
            fast = fast->next;
          }
        }

        /* 'slow' is before the midpoint in the list, so split it in two
          at that point. */
        *frontRef = source;
        *backRef = slow->next;
        slow->next = NULL;
      }
    }

    /* Function to print nodes in a given linked list */
    void printList(struct node *node)
    {
      while(node!=NULL)
      {
       printf("%d ", node->data);
       node = node->next;
      }
    }

    /* Function to insert a node at the beginging of the linked list */
    void push(struct node** head_ref, int new_data)
    {
      /* allocate node */
      struct node* new_node =
                (struct node*) malloc(sizeof(struct node));

      /* put in the data  */
      new_node->data  = new_data;

      /* link the old list off the new node */
      new_node->next = (*head_ref);    

      /* move the head to point to the new node */
      (*head_ref)    = new_node;
    } 

    /* Drier program to test above functions*/
    int main()
    {
      /* Start with the empty list */
      struct node* res = NULL;
      struct node* a = NULL;
      struct node* b = NULL; 

      /* Let us create a unsorted linked lists to test the functions
       Created lists shall be a: 2->3->20->5->10->15 */
      push(&a, 15);
      push(&a, 10);
      push(&a, 5);
      push(&a, 20);
      push(&a, 3);
      push(&a, 2); 

      /* Remove duplicates from linked list */
      MergeSort(&a);

      printf("\n Sorted Linked List is: \n");
      printList(a);           

      getchar();
      return 0;
    }


答案 1 :(得分:0)

尝试对在数组上以正常合并排序执行的所有合并进行成像:首先,将元素配对并合并到长度为2的已排序子数组中,然后将这些长度为2的子数组配对并合并到已排序的子数组中长度四等。注意子数组的长度:1,2,4等等,让我们调用这个instep,它在每次迭代中加倍。

在任何时候,p指向长度为instep的列表,q指向长度为instep或更小的列表(我们可能会在列表),q紧跟p。它们形成如上所述的一对子阵列。我们在pq上进行合并,以从psize + qsize开始获取长度为p的排序列表。我们将pq移至下一对,依此类推。完成整个列表后,我们加倍instep并开始合并更长的排序列表。