深入單鏈表的快速排序詳解

2022-09-29 22:33:34 字數 4473 閱讀 6358

單鏈表的快排序和陣列的快排序基本思想相同,同樣是基於劃分,但是又有很大的不同:單鏈表不支援基於下標的訪問。故書中把待排序的鍊錶拆分為2個子鍊錶。為了簡單起見,選擇鍊錶的第乙個節點作為基準,然後進行比較,比基準小得節點放入左面的子鍊錶,比基準大的放入右邊的子鍊錶。在對待排序鍊錶掃瞄一遍之後,左邊子鍊錶的節點值都小於基準的值,右邊子鍊錶的值都大於基準的值,然後把基準插入到鍊錶中,並作為連線兩個子鍊錶的橋梁。然後分別對左、右兩個子鍊錶進行遞迴快速排序,以提高效能。

但是,由於單鏈表不能像陣列那樣隨機儲存,和陣列的快排序相比較,還是有一些需要注意的細節:

1、支點的選取,由於不能隨機訪問第k個元素,因此每次選擇支點時可以取待排序那部分鍊錶的頭指標。

2、遍歷量表方式,由於不能從單鏈表的末尾向前遍歷,因此使用兩個指標分別向前向後遍歷的策略實效,

事實上,可以可以採用一趟遍歷的方式將較小的元素放到單鏈表的左邊。具體方法為:

1)定義兩個指標pslow,pfast,其中pslow指向單鏈表的頭結點,pfast指向單鏈表頭結點的下乙個結點;

2)使用pfast遍歷單鏈表,每遇到乙個比支點小的元素,就令pslow=pslow->next,然後和pslow進行資料交換。

3、交換資料方式,直接交換鍊錶資料指標指向的部分,不必交換鍊錶節點本身。oyeuvaoncg

基於上述思想的單鏈表快速排序實現如下:

複製** **如下:

/**  

** 單鏈表的快速排序  

** author :liuzhiwei

** date   :2011-08-07  

**/#include

#include

using namespace std;

//單鏈表節點

struct slist

;void bulid_slist(slist** phead, int n)    //指向指標的指標

else

}}void print_slist(slist* phead)   //輸出鍊錶

printf("\n");

}void my_swap(int *a,int *b)

void sort_slist(slist* phead, slist* pend)    //將頭指標為phead,尾指標為pend的鍊錶進行排序

pfast = pfast->next;

}my_swap(&pslow->data , &phead->data);  //此時pslow指標指向比基準小的結點組成的鍊錶的最後乙個結點,也就是基準的位置,所以要與基準(head結點)交換

sort_slist(phead , pslow);             //ptemp為左右兩部分分割點(基準)的前乙個結點

sort_slist(pslow->next , null);        //右部分是比基準大的結點組成的鍊錶

}void destroy_slist(slist* phead)

}int main(void)

第二種方法:

選擇鍊錶的第乙個節點作為基準,然後進行比較,比基準小得節點放入左面的子鍊錶,比基準大的放入右邊的子鍊錶。在對待排序鍊錶掃瞄一遍之後,左面子鍊錶的節點值都小於基準的值,右邊子鍊錶的值都大於基準的值,然後把基準插入到鍊錶中,並作為連線兩個子鍊錶的橋梁。然後根據左、右子鍊錶中節點數,選擇較小的進行遞迴快速排序,而對數目較多的則進行迭代排序。

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

複製** **如下:

struct node *right;   //右邊子鍊錶的第乙個節點

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

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

核心**如下:

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

} 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節點,但是卻是不同的。

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

完整的**如下:

複製** **如下:

#include   

#include   

#include   

//鍊錶節點  

struct node

;  

//鍊錶快排序函式

void qlistsort(struct node **head,struct node *h);  

//列印鍊錶  

void print_list(struct node *head)

printf("\n");  

}  

int main(void)

else

}print_list(head);

printf("---------------------------------\n");

qlistsort(&head,null);

print_list(head);

system("pause");

return 0;  

}  

void qlistsort(struct node **head, struct node *end)

else

}  

//合併鍊錶  

*right_walk = end;       //結束右鍊錶  

*left_walk = pivot;      //把基準置於正確的位置上  

pivot->next = right;     //把鍊錶合併  

//對較小的子鍊錶進行快排序,較大的子鍊錶進行迭代排序。  

if(left_walk > right_walk)

else

} while (count > 1);   

}

本文標題: 深入單鏈表的快速排序詳解

本文位址:

深入單鏈表的快速排序詳解

單鏈表的快排序和陣列的快排序基本思想相同,同樣是基於劃分,但是又有很大的不同 單鏈表不支援基於下標的訪問。故書中把待排序的鍊錶拆分為2個子鍊錶。為了簡單起見,選擇鍊錶的第乙個節點作為基準,然後進行比較,比基準小得節點放入左面的子鍊錶,比基準大的放入右邊的子鍊錶。在對待排序鍊錶掃瞄一遍之後,左邊子鍊錶...

單鏈表快速排序

今天在學習 程式設計師使用演算法 時,看到了單鏈表快排序這一節。初看時感覺程式有很大的問題,但是細細品味之後卻發現程式設計的極為巧妙,同時又深感自己c語言指標知識之不牢固,特別是指標的指標方面的知識。單鏈表的快排序和陣列的快排序基本思想相同,同樣是基於劃分,但是又有很大的不同 單鏈表不支援基於下標的...

單鏈表快速排序

今天在學習 程式設計師使用演算法 時,看到了單鏈表快排序這一節。初看時感覺程式有很大的問題,但是細細品味之後卻發現程式設計的極為巧妙,同時又深感自己c語言指標知識之不牢固,特別是指標的指標方面的知識。單鏈表的快排序和陣列的快排序基本思想相同,同樣是基於劃分,但是又有很大的不同 單鏈表不支援基於下標的...