C語言實現(xiàn)單鏈表的快速排序算法
背景
傳統(tǒng)QuickSort算法最大不足之處在于,由于其基于可索引存儲結(jié)構(gòu)設(shè)計(一般為數(shù)組或索引表),因而無法用于鏈?zhǔn)酱鎯Y(jié)構(gòu),而鏈?zhǔn)酱鎯Y(jié)構(gòu)的實際應(yīng)用非常廣泛,例如動態(tài)存儲管理、動態(tài)優(yōu)先級調(diào)度等等,故本文針對于單向鏈表,以QuickSort的分治策略為基礎(chǔ),提出一種可用于單向鏈表的快速排序算法。
設(shè)計思路
將單向鏈表的首節(jié)點作為樞軸節(jié)點,然后從單向鏈表首部的第二個節(jié)點開始,逐一遍歷所有后續(xù)節(jié)點,并將這些已遍歷節(jié)點的key與樞軸節(jié)點的key進行比較,根據(jù)比較結(jié)果,重新將這些節(jié)點鏈接為less和more兩個單向鏈表,less中所包含節(jié)點的key均小于樞軸節(jié)點的 key;more中所包含節(jié)點的key均大于或等于樞軸節(jié)點的key。然后再對得到的兩個單鏈表進行遞歸操作,將進行內(nèi)部遞歸排序最后連接到樞軸上。同時為達到在每次劃分后將規(guī)模更小的兩個單向鏈表鏈接到樞軸節(jié)點上,必須記錄各自的尾節(jié)點位置,即需要設(shè)置兩個指向尾節(jié)點的指針。less鏈表的首節(jié)點指針設(shè)定為lessHead,尾節(jié)點指針設(shè)定為lessTail;more鏈表的首節(jié)點指針設(shè)定為 moreHead,尾節(jié)點指針設(shè)定為moreTail。當(dāng)前正在遍歷的節(jié)點指針設(shè)定為current。當(dāng)單向鏈表遍歷結(jié)束后,亦即完成了一趟劃分, 如此遞歸進行,便可完成整個單向鏈表的排序。除此之外,為了簡化將 less和more單向鏈表鏈接到樞軸節(jié)點前、后部的過程,還需設(shè)定兩個指向單向鏈表尾節(jié)點的指針 lessTail和moreTail。在遞歸過程中,得到的單鏈表的長度會依次減小,直到長度減小到一的時候即為遞歸出口。
算法主要步驟
步驟1:算法接收兩個指針,其中l(wèi)istHead指 向單向鏈表首節(jié)點,listTail為空指針,劃分過程中,listTail將指向單向鏈表的尾節(jié)點,劃分后用于鏈接less單向鏈表到樞軸節(jié)點前。
步驟2:如果單向鏈表listHead僅有一個節(jié)點,則說明已有序,本層遞歸結(jié)束,返回listHead。
步驟3:令lessHead、lessTail、moreHead和 moreTail為空,令current為listHead的next域,即單向鏈表的第二個節(jié)點。
步驟4:如果current節(jié)點為空,則轉(zhuǎn)入步驟13。
步驟5:如果current節(jié)點的key小于樞軸節(jié)點(即listHead)的key,則current節(jié)點應(yīng)鏈接到 less單向鏈表,轉(zhuǎn)入步驟6;否則,current節(jié)點應(yīng)鏈 接到more單向鏈表,轉(zhuǎn)入步驟9。
步驟6:修改lessTail指針使其指向current 節(jié)點。如果lessHead為空,則轉(zhuǎn)入步驟7;否則轉(zhuǎn)入步驟8。
步驟7:將less節(jié)點鏈接為單鏈表的首節(jié)點。
步驟8:將current節(jié)點鏈接為less單鏈表的尾結(jié)點;
步驟9:修改moreTail指針使其指向current 節(jié)點。如果moreHead為空,則轉(zhuǎn)入步驟10;否則轉(zhuǎn)入步驟11。
步驟10:將currnet節(jié)點鏈接為more單向鏈表的首節(jié)點。
步驟11:將currnet節(jié)點鏈接為more單向鏈表的尾節(jié)點。
步驟12:將current節(jié)點移動到單向鏈表的下一個節(jié)點。
步驟13:如果more單向鏈表不為空,則轉(zhuǎn)入步驟14;否則轉(zhuǎn)入步驟18。
步驟14:標(biāo)記more單向鏈表的結(jié)束位置,即 置moreTail的next域為空。
步驟15:遞歸調(diào)用本算法,繼續(xù)劃分more單 向鏈表,傳人moreHead和moreTail。步驟16將經(jīng)過遞歸排序的more單向鏈表 鏈接到樞軸節(jié)點后。
步驟17:修改listTail指針使其指向more— Tail,以便本層遞歸結(jié)束后供上層遞歸過程使用。
步驟18:由于more單向鏈表為空,則樞軸節(jié) 點便是尾節(jié)點,即置listHead的next域為空。 步驟19:修改listTail指針使其指向listHead。
步驟20:如果less單向鏈表不為空,則轉(zhuǎn)入 步驟21;否則轉(zhuǎn)入步驟24。
步驟21:標(biāo)記less單向鏈表的結(jié)束位置,即 置lessTail的next域為空。
步驟22:遞歸調(diào)用本算法,繼續(xù)劃分less單 向鏈表,傳人lessHead和lessTail。 步驟23將經(jīng)過遞歸排序的less單向鏈表鏈 接到樞軸節(jié)點前。
步驟24:由于less單向鏈表為空,則樞軸節(jié) 點便是首節(jié)點,即置lessHead為listHead。
步驟25:本層遞歸結(jié)束,返回lessHead。
示意圖如下:



快速排序算法實現(xiàn)
Linklist Quicksort(Linklist *listHead, Linklist *listTail)
{
Lnode *current;
Lnode* lessHead = NULL, *lessTail = NULL, *moreHead = NULL, *moreTail = NULL;
current = (*listHead)->next;//每次取首節(jié)點為樞紐,current指向第二個節(jié)點用于遍歷
if ((*listHead)->next != NULL)//當(dāng)鏈表節(jié)點數(shù)不為1時(說明鏈表未排好序)
{
for (current = (*listHead)->next; current; current = current->next)
{
if (current->key < (*listHead)->key)
{
if (lessHead == NULL)
lessHead = current;
else
lessTail->next = current;
lessTail = current;
}//current結(jié)點key小于樞紐key時放入less鏈表
else
{
if (moreHead == NULL)
moreHead = current;
else
moreTail->next = current;
moreTail = current;
}//current結(jié)點key大于樞紐key時放入more鏈表
}
//根據(jù)樞紐結(jié)點將T鏈表分為less和more兩個鏈表
if (moreTail)
moreTail->next = NULL;
if (lessTail)
lessTail->next = NULL;
//將more鏈表尾結(jié)點next域置空
if (moreHead != NULL)
{
moreTail->next = NULL;
Quicksort(&moreHead, &moreTail);
(*listHead)->next = moreHead;
*listTail = moreTail;
}
//若moreHead不空,則current為more鏈表的尾結(jié)點,對more鏈表進行遞歸處理,將more鏈表接在樞紐節(jié)點后
else
{
(*listHead)->next = NULL;
*listTail = *listHead;
}
//若moreHead為空,則只有l(wèi)ess鏈表(即結(jié)點key全小于樞紐),將樞紐結(jié)點接在less節(jié)點后
if (lessHead != NULL)
{
lessTail->next = NULL;
Quicksort(&lessHead, &lessTail);
lessTail->next = *listHead;
*listHead = lessHead;
}
//若lesseHead不空,對less鏈表進行遞歸處理,再將樞紐節(jié)點接在less鏈表后
else
{
lessHead = *listHead;
}
//若lesseHead為空,則樞紐結(jié)點作為首節(jié)點
return lessHead;
}
else
return *listHead;
}
整個程序源代碼
#include<stdio.h>
#include<malloc.h>
typedef struct Lnode
{
int key;
struct Lnode* next;
}Lnode, *Linklist;
//鏈表結(jié)構(gòu)體類型
Linklist createList(Linklist L, int n)
{
L = (Linklist)malloc(sizeof(Lnode));
L->next = NULL;
Lnode *p, *r;
r = L;
p = (Lnode*)malloc(sizeof(Lnode));
scanf("%d", &r->key);
for (int i = 1; i < n; i++)
{
p = (Lnode*)malloc(sizeof(Lnode));
scanf("%d", &p->key);
r->next = p;
r = p;
}
r->next = NULL;
return L;
}
//初始初始化及尾插法(正序)創(chuàng)建單鏈表
Linklist getTail(Linklist L)
{
while (L->next)
L = L->next;
return L;
}
//得到尾指針
void Print(Linklist L)
{
Lnode *p;
p = L;
while (p)
{
printf("%d ", p->key);
p = p->next;
}
}
//遍歷單鏈表
Linklist Quicksort(Linklist *listHead, Linklist *listTail)
{
Lnode *current;
Lnode* lessHead = NULL, *lessTail = NULL, *moreHead = NULL, *moreTail = NULL;
current = (*listHead)->next;//每次取首節(jié)點為樞紐,current指向第二個節(jié)點用于遍歷
if ((*listHead)->next != NULL)//當(dāng)鏈表節(jié)點數(shù)不為1時(說明鏈表未排好序)
{
for (current = (*listHead)->next; current; current = current->next)
{
if (current->key < (*listHead)->key)
{
if (lessHead == NULL)
lessHead = current;
else
lessTail->next = current;
lessTail = current;
}//current結(jié)點key小于樞紐key時放入less鏈表
else
{
if (moreHead == NULL)
moreHead = current;
else
moreTail->next = current;
moreTail = current;
}//current結(jié)點key大于樞紐key時放入more鏈表
}
//根據(jù)樞紐結(jié)點將T鏈表分為less和more兩個鏈表
if (moreTail)
moreTail->next = NULL;
if (lessTail)
lessTail->next = NULL;
//將more鏈表尾結(jié)點next域置空
if (moreHead != NULL)
{
moreTail->next = NULL;
Quicksort(&moreHead, &moreTail);
(*listHead)->next = moreHead;
*listTail = moreTail;
}
//若moreHead不空,則current為more鏈表的尾結(jié)點,對more鏈表進行遞歸處理,將more鏈表接在樞紐節(jié)點后
else
{
(*listHead)->next = NULL;
*listTail = *listHead;
}
//若moreHead為空,則只有l(wèi)ess鏈表(即結(jié)點key全小于樞紐),將樞紐結(jié)點接在less節(jié)點后
if (lessHead != NULL)
{
lessTail->next = NULL;
Quicksort(&lessHead, &lessTail);
lessTail->next = *listHead;
*listHead = lessHead;
}
//若lesseHead不空,對less鏈表進行遞歸處理,再將樞紐節(jié)點接在less鏈表后
else
{
lessHead = *listHead;
}
//若lesseHead為空,則樞紐結(jié)點作為首節(jié)點
return lessHead;
}
else
return *listHead;
}
int main()
{
Lnode* L = NULL;
int n;
printf("請輸入元素個數(shù)\n");
scanf("%d", &n);
printf("請輸入元素\n");
L = createList(L, n);
Lnode* listTail;
listTail = getTail(L);
Quicksort(&L, &listTail);
printf("排序后元素序列為\n");
Print(L);
return 0;
}
整個程序已在Visual Studio 2017上運行通過
測試案例
(1)一般數(shù)據(jù)樣例

(2)只有一個數(shù)據(jù)時

(2)有重復(fù)數(shù)據(jù)時

總結(jié)
到此這篇關(guān)于C語言實現(xiàn)單鏈表的快速排序算法的文章就介紹到這了,更多相關(guān)C語言快速排序算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
從匯編看c++中默認(rèn)構(gòu)造函數(shù)的使用分析
c++中,如果為一個類沒有明確定義一個構(gòu)造函數(shù),那么,編譯器就會自動合成一個默認(rèn)的構(gòu)造函數(shù)。下面,通過匯編程序,來看一下其真實情況2013-05-05
Inline Hook(ring3)的簡單C++實現(xiàn)方法
這篇文章主要介紹了Inline Hook(ring3)的簡單C++實現(xiàn)方法,需要的朋友可以參考下2014-08-08
C/C++細數(shù)宏與函數(shù)有那些區(qū)別
在C程序中,可以用宏代碼提高執(zhí)行效率。宏代碼本身不是函數(shù),但使用起來象函數(shù)。預(yù)處理器用復(fù)制宏代碼的方式代替函數(shù)調(diào)用,省去了參數(shù)壓棧、生成匯編語言的CALL調(diào)用、返回參數(shù)、執(zhí)行return等過程,從而提高了速度2022-10-10
C++ 數(shù)字的反轉(zhuǎn)實現(xiàn)實例
這篇文章主要介紹了C++ 數(shù)字的反轉(zhuǎn)實現(xiàn)實例的相關(guān)資料,需要的朋友可以參考下2017-06-06

