單鏈錶快速排序

來源:互聯網
上載者:User

今天在學習《程式員使用演算法》時,看到了單鏈錶快排序這一節。初看時感覺程式有很大的問題,但是細細品味之後卻發現程式設計的極為巧妙,同時又深感自己C語言指標知識之不牢固,特別是指標的指標方面的知識。

單鏈表的快排序和數組的快排序基本思想相同,同樣是基於劃分,但是又有很大的不同:單鏈表不支援基於下標的訪問。故書中把待排序的鏈表拆分為2個子鏈表。為了簡單起見,選擇鏈表的第一個節點作為基準,然後進行比較,比基準大節點的放入左面的子鏈表,比基準大的放入右邊的子鏈表。在對待排序鏈表掃描一遍之後,左面子鏈表的節點值都小於基準的值,右邊子鏈表的值都大於基準的值,然後把基準插入到鏈表中,並作為串連兩個子鏈表的橋樑。然後根據左右子鏈表中節點數,選擇較小的進行遞迴快速排序,而對數目較多的則進行跌等待排序,以提高效能。

排序函數中使用的變數如下:

struct node *right;   //右邊子鏈表的第一個節點

struct node **left_walk, **right_walk;    //作為指標,把其指向的節點加入到相應的子鏈表中

struct node *pivot, *old;    //pivot為基準, old為迴圈整個待排序鏈表的指標

核心代碼如下:

for (old = (*head)->next; old != end; old = old->next) {

      if (old->data < pivot->data) {  //小於基準,加入到左面的子鏈表,繼續比較

             ++left_count;

         *left_walk = old;            //把該節點加入到左邊的鏈表中,

         left_walk = &(old->next);

} else {                      //大於基準,加入到右邊的子鏈表,繼續比較

         ++right_count;

             *right_walk = old;          

             right_walk = &(old->next);

      }

}

head為struct node **類型,指向鏈表頭部,end指向鏈表尾部,可為NULL,這段程式的重點在於指標的指標的用法,*left_walk為一個指向node節點的指標,說的明白點*left_walk的值就是node節點的記憶體位址,其實還有一個地方也有node的地址,那就是指向node的節點的next域,故我們可以簡單的認為*left_walk = old就是把指向node節點的節點的next域改為節點old的地址,這樣可能造成兩種情況:一種就是*left_walk本來就指向old節點,這樣就沒有改變任何改變,另一種則是改變了*right_walk指向節點的前一個節點的next域,使其指向後部的節點,中間跳過了若干個節點,不過在這裡這樣做並不會造成任何問題,因為鏈表中的節點要麼加入到左面的子鏈表中,要麼加入到右面的子鏈表中,不會出現節點丟失的情況。

下面用圖示說明下上面的問題:

 

 

這裡假設鏈表的值一次是5、2、4、6、1。根據程式首先head = left_walk指向值為5的節點,old指向值為2的節點,2小於5,所以加入2到左面的子鏈表中,*left_walk=old,我們知道,*left_walk指向的是第一個節點,這樣做改變了head指標值,使其指向第二個節點,然後left_walk後移,old後移,4同樣小於5,故繼續上述操作,但是這是*left_walk和old指向的是同一個節點,沒有引起任何變化,left_walk和old後移,6大於5,這時不同就出現了,要把其加入到右邊的子鏈表中,故是*right_walk = old,其實right_walk初試化為&right,這句話相當於right = old,即令old當前指向的節點作為右邊子鏈表的第一個節點,以後大於基準的節點都要加入到這個節點中,且總是加入到尾部。此時right_walk,和old後移,1小於5應該加入到左邊的子鏈表中,*left_walk = old,此時*left_walk指向6,故此語句的作用是更改節點4的next值,把其改為1的地址,這樣6就從原來的鏈表中脫鉤了,繼續left_walk和old後移到9節點,應加入到右邊的子鏈表中,此時*right_walk指向1,故把9節點加入到6節點的後面。

這就是基本的排序過程,然而有一個問題需要搞明白,比如有節點依次為struct node *a, *b, *c,node **p , p = &b,如果此時令*p = c,即實際效果是a->next = c;我們知道這相當於該a的next域的值。而p僅僅是一個指標的指標,它是指向b所指向的節點的地址的指標,那麼當我們更改*p的值的時候怎麼會改到了a的next呢(這個可以寫程式驗證下,確實如此)?其實並非如此,我們自習的看看程式,left_walk初始化為head,那麼第一次執行*left_walk是把head指向了左邊鏈表的起始節點,然後left_walk被賦值為&(old->next),這句話就有意思了,我們看以看看下面在執行*left_walk=old時的情況,可以簡單的來個等價替換,*left_walk = old也就相當於*&(old->next) = old,即old->nex = old,不過這裡的old可不一定是old->next所指向的節點,應為left_walk和right_walk都指向它們的old節點,但是卻是不同的。

演算法到這裡並沒有完,這隻是執行了一次劃分,把基準放入了正確的位置,還要繼續,不過下面的就比較簡單了,就是遞迴排序個數比較小的子鏈表,迭代處理節點數目比較大的子鏈表。

整體代碼如下:

 

view plaincopy to clipboardprint?
01./** 
02.* 單鏈表的快排序 
03.* author :blue 
04.* data   :2010-4-6 
05.*/ 
06. 
07.#include <stdio.h>  
08.#include <stdlib.h>  
09.#include <time.h>  
10.//鏈表節點  
11.struct node {  
12.    int data;  
13.    struct node *next;  
14.};  
15.//鏈錶快排序函數  
16.void QListSort(struct node **head, struct node *head);  
17.//列印鏈表  
18.void print_list(struct node *head) {  
19.    struct node *p;  
20.    for (p = head; p != NULL; p = p->next) {  
21.        printf("%d ", p->data);  
22.    }  
23.    printf("/n");  
24.}  
25.int main(void) {  
26.    struct node *head;  
27.    struct node *p;  
28.    int i = 0;  
29.    /** 
30.    * 初始化鏈表 
31.    */ 
32.    head = (struct node*)malloc(sizeof(struct node));  
33.    head->next = NULL;  
34.    head->data = 0;  
35.    srand((unsigned)time(NULL));  
36.    for (i = 1; i < 11; ++i) {  
37.        p = (struct node*)malloc(sizeof(struct node));  
38.        p->data = rand() % 100 + 1;  
39.        p->next = head->next;  
40.        head->next = p;  
41.    }  
42.      
43.    print_list(head);  
44.    printf("---------------------------------/n");  
45.    QListSort(&head, NULL);  
46.    print_list(head);  
47.    return 0;  
48.}  
49. 
50.void QListSort(struct node **head, struct node *end) {  
51.    struct node *right;  
52.    struct node **left_walk, **right_walk;  
53.    struct node *pivot, *old;  
54.    int count, left_count, right_count;  
55.    if (*head == end)  
56.        return;  
57.    do {  
58.        pivot = *head;  
59.        left_walk = head;  
60.        right_walk = &right;  
61.        left_count = right_count = 0;  
62.        //取第一個節點作為比較的基準,小於基準的在左面的子鏈表中,  
63.        //大於基準的在右邊的子鏈表中  
64.        for (old = (*head)->next; old != end; old = old->next) {  
65.            if (old->data < pivot->data) {   //小於基準,加入到左面的子鏈表,繼續比較  
66.                ++left_count;  
67.                *left_walk = old;            //把該節點加入到左邊的鏈表中,  
68.                left_walk = &(old->next);  
69.            } else {                         //大於基準,加入到右邊的子鏈表,繼續比較  
70.                ++right_count;  
71.                *right_walk = old;             
72.                right_walk = &(old->next);  
73.            }  
74.        }  
75.        //合并鏈表  
76.        *right_walk = end;       //結束右鏈表  
77.        *left_walk = pivot;      //把基準置於正確的位置上  
78.        pivot->next = right;     //把鏈表合并  
79.        //對較小的子鏈表進行快排序,較大的子鏈表進行迭代排序。  
80.        if(left_walk > right_walk) {  
81.            QListSort(&(pivot->next), end);  
82.            end = pivot;  
83.            count = left_count;  
84.        } else {  
85.            QListSort(head, pivot);  
86.            head = &(pivot->next);  
87.            count = right_count;  
88.        }  
89.    } while (count > 1);   
90.} 

本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/PinkRobin/archive/2010/04/06/5456094.aspx

本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/PinkRobin/archive/2010/04/06/5456094.aspx

本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/PinkRobin/archive/2010/04/06/5456094.aspx

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.