日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

C++经典排序算法总结

發(fā)布時(shí)間:2024/1/1 c/c++ 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++经典排序算法总结 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)發(fā)請(qǐng)注明出處:https://www.cnblogs.com/fnlingnzb-learner/p/9374732.html

?最近在研究一些經(jīng)常用到的東西想把它們做一個(gè)匯總,想了想用到最多的應(yīng)該是排序算法,所以對(duì)排序算法做了個(gè)總結(jié),并自己用C++實(shí)現(xiàn)了一下。

一、算法概述

0.1 算法分類(lèi)

十種常見(jiàn)排序算法可以分為兩大類(lèi):

非線性時(shí)間比較類(lèi)排序:通過(guò)比較來(lái)決定元素間的相對(duì)次序,由于其時(shí)間復(fù)雜度不能突破O(nlogn),因此稱(chēng)為非線性時(shí)間比較類(lèi)排序。

線性時(shí)間非比較類(lèi)排序:不通過(guò)比較來(lái)決定元素間的相對(duì)次序,它可以突破基于比較排序的時(shí)間下界,以線性時(shí)間運(yùn)行,因此稱(chēng)為線性時(shí)間非比較類(lèi)排序。?

0.2 算法復(fù)雜度

0.3 相關(guān)概念

穩(wěn)定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。

不穩(wěn)定:如果a原本在b的前面,而a=b,排序之后 a 可能會(huì)出現(xiàn)在 b 的后面。

時(shí)間復(fù)雜度:對(duì)排序數(shù)據(jù)的總的操作次數(shù)。反映當(dāng)n變化時(shí),操作次數(shù)呈現(xiàn)什么規(guī)律。

空間復(fù)雜度:是指算法在計(jì)算機(jī)內(nèi)執(zhí)行時(shí)所需存儲(chǔ)空間的度量,它也是數(shù)據(jù)規(guī)模n的函數(shù)。?

二、快速排序

假設(shè)我們現(xiàn)在對(duì)“6 ?1 ?2 7 ?9 ?3 ?4 ?5 10 ?8”這個(gè)10個(gè)數(shù)進(jìn)行排序。首先在這個(gè)序列中隨便找一個(gè)數(shù)作為基準(zhǔn)數(shù)(不要被這個(gè)名詞嚇到了,就是一個(gè)用來(lái)參照的數(shù),待會(huì)你就知道它用來(lái)做啥的了)。為了方便,就讓第一個(gè)數(shù)6作為基準(zhǔn)數(shù)吧。接下來(lái),需要將這個(gè)序列中所有比基準(zhǔn)數(shù)大的數(shù)放在6的右邊,比基準(zhǔn)數(shù)小的數(shù)放在6的左邊,類(lèi)似下面這種排列:

3 1 2 5 4 6 9 7 10 8

在初始狀態(tài)下,數(shù)字6在序列的第1位。我們的目標(biāo)是將6挪到序列中間的某個(gè)位置,假設(shè)這個(gè)位置是k。現(xiàn)在就需要尋找這個(gè)k,并且以第k位為分界點(diǎn),左邊的數(shù)都小于等于6,右邊的數(shù)都大于等于6,遞歸對(duì)左右兩個(gè)區(qū)間進(jìn)行同樣排序即可。想一想,你有辦法可以做到這點(diǎn)嗎?這就是快速排序所解決的問(wèn)題。

快速排序是C.R.A.Hoare于1962年提出的一種劃分交換排序。它采用了一種分治的策略,通常稱(chēng)其為分治法(Divide-and-ConquerMethod)。它的平均時(shí)間復(fù)雜度為O(nlogn),最壞時(shí)間復(fù)雜度為O(n^2).

首先上圖:

?

?從圖中我們可以看到:

left指針,right指針,base參照數(shù)。

其實(shí)思想是蠻簡(jiǎn)單的,就是通過(guò)第一遍的遍歷(讓left和right指針重合)來(lái)找到數(shù)組的切割點(diǎn)。

第一步:首先我們從數(shù)組的left位置取出該數(shù)(20)作為基準(zhǔn)(base)參照物。(如果是選取隨機(jī)的,則找到隨機(jī)的哨兵之后,將它與第一個(gè)元素交換,開(kāi)始普通的快排)

第二步:從數(shù)組的right位置向前找,一直找到比(base)小的數(shù),如果找到,將此數(shù)賦給left位置(也就是將10賦給20),此時(shí)數(shù)組為:10,40,50,10,60,?left和right指針?lè)謩e為前后的10。

第三步:從數(shù)組的left位置向后找,一直找到比(base)大的數(shù),如果找到,將此數(shù)賦給right的位置(也就是40賦給10),此時(shí)數(shù)組為:10,40,50,40,60,?left和right指針?lè)謩e為前后的40。

第四步:重復(fù)“第二,第三“步驟,直到left和right指針重合,最后將(base)放到40的位置,?此時(shí)數(shù)組值為: 10,20,50,40,60,至此完成一次排序。

第五步:此時(shí)20已經(jīng)潛入到數(shù)組的內(nèi)部,20的左側(cè)一組數(shù)都比20小,20的右側(cè)作為一組數(shù)都比20大,?以20為切入點(diǎn)對(duì)左右兩邊數(shù)按照"第一,第二,第三,第四"步驟進(jìn)行,最終快排大功告成。

快速排序代碼如下:

1 //快速排序,隨機(jī)選取哨兵放前面 2 void QuickSort(int* h, int left, int right) 3 { 4 if(h==NULL) return; 5 if(left>=right) return; 6 7 //防止有序隊(duì)列導(dǎo)致快速排序效率降低 8 srand((unsigned)time(NULL)); 9 int len=right-left; 10 int kindex=rand()%(len+1)+left; 11 Swap(h[left],h[kindex]); 12 13 int key=h[left],i=left,j=right; 14 while(i<j) 15 { 16 while(h[j]>=key && i<j) --j; 17 if(i<j) h[i]=h[j]; 18 while(h[i]<key && i<j) ++i; 19 if(i<j) h[j]=h[i]; 20 } 21 22 h[i]=key; 23 24 //QuickSort(&h[left],0,i-1); 25 //QuickSort(&h[j+1],0,right-j-1); 26 27 QuickSort(h,left,i-1); 28 QuickSort(h,j+1,right); 29 }

?

三、冒泡排序

1.原理

冒泡排序在掃描過(guò)程中兩兩比較相鄰記錄,如果反序則交換,最終,最大記錄就被“沉到”了序列的最后一個(gè)位置,第二遍掃描將第二大記錄“沉到”了倒數(shù)第二個(gè)位置,重復(fù)上述操作,直到n-1 遍掃描后,整個(gè)序列就排好序了。

2.算法實(shí)現(xiàn)

1 //冒泡排序 2 void BubbleSort(int* h, size_t len) 3 { 4 if(h==NULL) return; 5 if(len<=1) return; 6 //i是次數(shù),j是具體下標(biāo) 7 for(int i=0;i<len-1;++i) 8 for(int j=0;j<len-1-i;++j) 9 if(h[j]>h[j+1]) 10 Swap(h[j],h[j+1]); 11 12 return; 13 }

?

四、選擇排序

選擇排序也是一種簡(jiǎn)單直觀的排序算法。它的工作原理很容易理解:初始時(shí)在序列中找到最小(大)元素,放到序列的起始位置作為已排序序列;然后,再?gòu)氖S辔磁判蛟刂欣^續(xù)尋找最小(大)元素,放到已排序序列的末尾。以此類(lèi)推,直到所有元素均排序完畢。

注意選擇排序與冒泡排序的區(qū)別:冒泡排序通過(guò)依次交換相鄰兩個(gè)順序不合法的元素位置,從而將當(dāng)前最小(大)元素放到合適的位置;而選擇排序每遍歷一次都記住了當(dāng)前最小(大)元素的位置,最后僅需一次交換操作即可將其放到合適的位置。

算法實(shí)現(xiàn):

1 //選擇排序 2 void SelectionSort(int* h, size_t len) 3 { 4 if(h==NULL) return; 5 if(len<=1) return; 6 7 int minindex,i,j; 8 //i是次數(shù),也即排好的個(gè)數(shù);j是繼續(xù)排 9 for(i=0;i<len-1;++i) 10 { 11 minindex=i; 12 for(j=i+1;j<len;++j) 13 { 14 if(h[j]<h[minindex]) minindex=j; 15 } 16 Swap(h[i],h[minindex]); 17 } 18 19 return; 20 }

?

五、插入排序

直接插入排序(straight insertion sort),有時(shí)也簡(jiǎn)稱(chēng)為插入排序(insertion sort),是減治法的一種典型應(yīng)用。其基本思想如下:

  • 對(duì)于一個(gè)數(shù)組A[0,n]的排序問(wèn)題,假設(shè)認(rèn)為數(shù)組在A[0,n-1]排序的問(wèn)題已經(jīng)解決了。
  • 考慮A[n]的值,從右向左掃描有序數(shù)組A[0,n-1],直到第一個(gè)小于等于A[n]的元素,將A[n]插在這個(gè)元素的后面。

  很顯然,基于增量法的思想在解決這個(gè)問(wèn)題上擁有更高的效率。

直接插入排序?qū)τ谧顗那闆r(嚴(yán)格遞減的數(shù)組),需要比較和移位的次數(shù)為n(n-1)/2;對(duì)于最好的情況(嚴(yán)格遞增的數(shù)組),需要比較的次數(shù)是n-1,需要移位的次數(shù)是0。當(dāng)然,對(duì)于最好和最壞的研究其實(shí)沒(méi)有太大的意義,因?yàn)閷?shí)際情況下,一般不會(huì)出現(xiàn)如此極端的情況。然而,直接插入排序?qū)τ诨居行虻臄?shù)組,會(huì)體現(xiàn)出良好的性能,這一特性,也給了它進(jìn)一步優(yōu)化的可能性。(希爾排序)。直接插入排序的時(shí)間復(fù)雜度是O(n^2),空間復(fù)雜度是O(1),同時(shí)也是穩(wěn)定排序。

下面用一個(gè)具體的場(chǎng)景,直觀地體會(huì)一下直接插入排序的過(guò)程:

場(chǎng)景:

現(xiàn)有一個(gè)無(wú)序數(shù)組,共7個(gè)數(shù):89 45 54 29 90 34 68。

使用直接插入排序法,對(duì)這個(gè)數(shù)組進(jìn)行升序排序。

89?45 54 29 90 34 68

45 89?54 29 90 34 68

45 54 89?29 90 34 68

29 45 54 89?90 34 68

29 45 54 89 90?34 68

29 34 45 54 89 90?68

29 34 45 54 68 89 90

?

算法實(shí)現(xiàn):

1 //插入排序 2 void InsertSort(int* h, size_t len) 3 { 4 if(h==NULL) return; 5 if(len<=1) return; 6 7 int i,j; 8 //i是次數(shù),也即排好的個(gè)數(shù);j是繼續(xù)排 9 for(i=1;i<len;++i) 10 for(j=i;j>0;--j) 11 if(h[j]<h[j-1]) Swap(h[j],h[j-1]); 12 else break; 13 14 return; 15 }

?

六、歸并排序

基本思想

  歸并排序(MERGE-SORT)是利用歸并的思想實(shí)現(xiàn)的排序方法,該算法采用經(jīng)典的分治(divide-and-conquer)策略(分治法將問(wèn)題(divide)成一些小的問(wèn)題然后遞歸求解,而治(conquer)的階段則將分的階段得到的各答案"修補(bǔ)"在一起,即分而治之)。

分而治之

?  可以看到這種結(jié)構(gòu)很像一棵完全二叉樹(shù),本文的歸并排序我們采用遞歸去實(shí)現(xiàn)(也可采用迭代的方式去實(shí)現(xiàn))。階段可以理解為就是遞歸拆分子序列的過(guò)程,遞歸深度為log2n。

合并相鄰有序子序列

  再來(lái)看看階段,我們需要將兩個(gè)已經(jīng)有序的子序列合并成一個(gè)有序序列,比如上圖中的最后一次合并,要將[4,5,7,8]和[1,2,3,6]兩個(gè)已經(jīng)有序的子序列,合并為最終序列[1,2,3,4,5,6,7,8],來(lái)看下實(shí)現(xiàn)步驟。

?

算法實(shí)現(xiàn):

1 //歸并排序 2 void MergeArray(int* arr, size_t left, size_t mid, size_t right, int* temp) 3 { 4 if(arr==NULL) return; 5 6 size_t i=left,j=mid+1,k=0; 7 while(i<=mid && j<=right) 8 { 9 if(arr[i]<=arr[j]) 10 { 11 temp[k++]=arr[i++]; 12 continue; 13 } 14 15 temp[k++]=arr[j++]; 16 } 17 18 while(i<=mid) 19 temp[k++]=arr[i++]; 20 21 while(j<=right) 22 temp[k++]=arr[j++]; 23 24 memcpy(&arr[left],temp,k*sizeof(int)); 25 26 return; 27 } 28 29 void MMergeSort(int* arr, size_t left, size_t right, int* temp) 30 { 31 if(left<right) 32 { 33 size_t mid=(left+right)/2; 34 MMergeSort(arr, left, mid, temp); 35 MMergeSort(arr, mid+1,right, temp); 36 MergeArray(arr,left, mid, right, temp); 37 } 38 } 39 40 void MergeSort(int* h, size_t len) 41 { 42 if(h==NULL) return; 43 if(len<=1) return; 44 int* temp=(int*)calloc(len,sizeof(int)); 45 MMergeSort(h, 0, len-1, temp); 46 47 memcpy(h,temp,sizeof(int)*len); 48 49 free(temp); 50 51 return; 52 }

?

七、希爾排序

希爾排序是希爾(Donald Shell)于1959年提出的一種排序算法。希爾排序也是一種插入排序,它是簡(jiǎn)單插入排序經(jīng)過(guò)改進(jìn)之后的一個(gè)更高效的版本,也稱(chēng)為縮小增量排序,同時(shí)該算法是沖破O(n2)的第一批算法之一。本文會(huì)以圖解的方式詳細(xì)介紹希爾排序的基本思想及其代碼實(shí)現(xiàn)。

基本思想

  希爾排序是把記錄按下標(biāo)的一定增量分組,對(duì)每組使用直接插入排序算法排序;隨著增量逐漸減少,每組包含的關(guān)鍵詞越來(lái)越多,當(dāng)增量減至1時(shí),整個(gè)文件恰被分成一組,算法便終止。

  簡(jiǎn)單插入排序很循規(guī)蹈矩,不管數(shù)組分布是怎么樣的,依然一步一步的對(duì)元素進(jìn)行比較,移動(dòng),插入,比如[5,4,3,2,1,0]這種倒序序列,數(shù)組末端的0要回到首位置很是費(fèi)勁,比較和移動(dòng)元素均需n-1次。而希爾排序在數(shù)組中采用跳躍式分組的策略,通過(guò)某個(gè)增量將數(shù)組元素劃分為若干組,然后分組進(jìn)行插入排序,隨后逐步縮小增量,繼續(xù)按組進(jìn)行插入排序操作,直至增量為1。希爾排序通過(guò)這種策略使得整個(gè)數(shù)組在初始階段達(dá)到從宏觀上看基本有序,小的基本在前,大的基本在后。然后縮小增量,到增量為1時(shí),其實(shí)多數(shù)情況下只需微調(diào)即可,不會(huì)涉及過(guò)多的數(shù)據(jù)移動(dòng)。

  我們來(lái)看下希爾排序的基本步驟,在此我們選擇增量gap=length/2,縮小增量繼續(xù)以gap = gap/2的方式,這種增量選擇我們可以用一個(gè)序列來(lái)表示,{n/2,(n/2)/2...1},稱(chēng)為增量序列。希爾排序的增量序列的選擇與證明是個(gè)數(shù)學(xué)難題,我們選擇的這個(gè)增量序列是比較常用的,也是希爾建議的增量,稱(chēng)為希爾增量,但其實(shí)這個(gè)增量序列不是最優(yōu)的。此處我們做示例使用希爾增量。

?

算法實(shí)現(xiàn):

1 //希爾排序 2 void ShellSort(int* h, size_t len) 3 { 4 if(h==NULL) return; 5 if(len<=1) return; 6 7 for(int div=len/2;div>=1;div/=2) 8 for(int k=0;k<div;++k) 9 for(int i=div+k;i<len;i+=div) 10 for(int j=i;j>k;j-=div) 11 if(h[j]<h[j-div]) Swap(h[j],h[j-div]); 12 else break; 13 14 return; 15 }

?

八、堆排序

堆排序?qū)嶋H上是利用堆的性質(zhì)來(lái)進(jìn)行排序的,要知道堆排序的原理我們首先一定要知道什么是堆。?
堆的定義:?
堆實(shí)際上是一棵完全二叉樹(shù)。?
堆滿(mǎn)足兩個(gè)性質(zhì):?
1、堆的每一個(gè)父節(jié)點(diǎn)都大于(或小于)其子節(jié)點(diǎn);?
2、堆的每個(gè)左子樹(shù)和右子樹(shù)也是一個(gè)堆。?
堆的分類(lèi):?
堆分為兩類(lèi):?
1、最大堆(大頂堆):堆的每個(gè)父節(jié)點(diǎn)都大于其孩子節(jié)點(diǎn);?
2、最小堆(小頂堆):堆的每個(gè)父節(jié)點(diǎn)都小于其孩子節(jié)點(diǎn);?
?
堆的存儲(chǔ):?
一般都用數(shù)組來(lái)表示堆,i結(jié)點(diǎn)的父結(jié)點(diǎn)下標(biāo)就為(i – 1) / 2。它的左右子結(jié)點(diǎn)下標(biāo)分別為2 * i + 1和2 * i + 2。如下圖所示:?
?
堆排序:?
由上面的介紹我們可以看出堆的第一個(gè)元素要么是最大值(大頂堆),要么是最小值(小頂堆),這樣在排序的時(shí)候(假設(shè)共n個(gè)節(jié)點(diǎn)),直接將第一個(gè)元素和最后一個(gè)元素進(jìn)行交換,然后從第一個(gè)元素開(kāi)始進(jìn)行向下調(diào)整至第n-1個(gè)元素。所以,如果需要升序,就建一個(gè)大堆,需要降序,就建一個(gè)小堆。?
堆排序的步驟分為三步:?
1、建堆(升序建大堆,降序建小堆);?
2、交換數(shù)據(jù);?
3、向下調(diào)整。?
假設(shè)我們現(xiàn)在要對(duì)數(shù)組arr[]={8,5,0,3,7,1,2}進(jìn)行排序(降序):?
首先要先建小堆:?

堆建好了下來(lái)就要開(kāi)始排序了:?

現(xiàn)在這個(gè)數(shù)組就已經(jīng)是有序的了。?

?

算法實(shí)現(xiàn):

1 //堆排序 2 /* 3 大頂堆sort之后,數(shù)組為從小到大排序 4 */ 5 //====調(diào)整===== 6 void AdjustHeap(int* h, int node, int len) //----node為需要調(diào)整的結(jié)點(diǎn)編號(hào),從0開(kāi)始編號(hào);len為堆長(zhǎng)度 7 { 8 int index=node; 9 int child=2*index+1; //左孩子,第一個(gè)節(jié)點(diǎn)編號(hào)為0 10 while(child<len) 11 { 12 //右子樹(shù) 13 if(child+1<len && h[child]<h[child+1]) 14 { 15 child++; 16 } 17 if(h[index]>=h[child]) break; 18 Swap(h[index],h[child]); 19 index=child; 20 child=2*index+1; 21 } 22 } 23 24 25 //====建堆===== 26 void MakeHeap(int* h, int len) 27 { 28 for(int i=len/2;i>=0;--i) 29 { 30 AdjustHeap(h, i, len); 31 } 32 } 33 34 //====排序===== 35 void HeapSort(int* h, int len) 36 { 37 MakeHeap(h, len); 38 for(int i=len-1;i>=0;--i) 39 { 40 Swap(h[i],h[0]); 41 AdjustHeap(h, 0, i); 42 } 43 }

?

九、基數(shù)排序

基數(shù)排序與本系列前面講解的七種排序方法都不同,它不需要比較關(guān)鍵字的大小

它是根據(jù)關(guān)鍵字中各位的值,通過(guò)對(duì)排序的N個(gè)元素進(jìn)行若干趟“分配”與“收集”來(lái)實(shí)現(xiàn)排序的。?

1.LSD(低位到高位的排序)

不妨通過(guò)一個(gè)具體的實(shí)例來(lái)展示一下,基數(shù)排序是如何進(jìn)行的。?

設(shè)有一個(gè)初始序列為: R {50, 123, 543, 187, 49, 30, 0, 2, 11, 100}。

我們知道,任何一個(gè)阿拉伯?dāng)?shù),它的各個(gè)位數(shù)上的基數(shù)都是以0~9來(lái)表示的。

所以我們不妨把0~9視為10個(gè)桶。?

我們先根據(jù)序列的個(gè)位數(shù)的數(shù)字來(lái)進(jìn)行分類(lèi),將其分到指定的桶中。例如:R[0] = 50,個(gè)位數(shù)上是0,將這個(gè)數(shù)存入編號(hào)為0的桶中。

?

分類(lèi)后,我們?cè)趶母鱾€(gè)桶中,將這些數(shù)按照從編號(hào)0到編號(hào)9的順序依次將所有數(shù)取出來(lái)。

這時(shí),得到的序列就是個(gè)位數(shù)上呈遞增趨勢(shì)的序列。?

按照個(gè)位數(shù)排序:?{50, 30, 0, 100, 11, 2, 123, 543, 187, 49}。

接下來(lái),可以對(duì)十位數(shù)、百位數(shù)也按照這種方法進(jìn)行排序,最后就能得到排序完成的序列。

LSD?算法實(shí)現(xiàn):

1 int GetMaxDight(int* h, int len) 2 { 3 if(h==NULL) return 0; 4 if(len<1) return 0; 5 6 int max=h[0]; 7 for(int i=1;i<len;++i) 8 { 9 if(h[i]>max) max=h[i]; 10 } 11 12 int digit=1; 13 while(max/10!=0) 14 { 15 max/=10; 16 ++digit; 17 } 18 19 return digit; 20 } 21 22 int GetReminder(int value,int digit) 23 { 24 int div=1; 25 for(int i=1;i<digit;++i) 26 div*=10; 27 28 return value/div%10; 29 } 30 31 void RadixSort_LSD(int* h, int len) 32 { 33 if(h==NULL) return; 34 if(len<=1) return; 35 36 int digit=GetMaxDight(h,len); 37 //printf("MaxDigit:%d\n", digit); 38 39 int count[10]={0}; 40 int *tmp=(int*)calloc(len,sizeof(int)); 41 42 for(int d=1;d<=digit;++d) 43 { 44 memset(count,0,sizeof(count)); 45 46 for(int i=0;i<len;++i) 47 { 48 count[GetReminder(h[i],d)]++; 49 } 50 51 //求右邊界 52 for(int i=1;i<10;++i) 53 { 54 count[i]+=count[i-1]; 55 } 56 57 for(int i=len-1;i>=0;--i) 58 { 59 int r=GetReminder(h[i],d); 60 int index=count[r]; 61 tmp[index-1]=h[i]; 62 count[r]--; 63 } 64 65 memcpy(h,tmp,len*sizeof(int)); 66 } 67 68 free(tmp); 69 } 70 71 void RadixSort_LSD_Reverse(int* h, int len) 72 { 73 if(h==NULL) return; 74 if(len<=1) return; 75 76 int digit=GetMaxDight(h,len); 77 //printf("MaxDigit:%d\n", digit); 78 79 int count[10]={0}; 80 81 int *tmp=(int*)calloc(len,sizeof(int)); 82 83 for(int d=1;d<=digit;++d) 84 { 85 memset(count,0,sizeof(count)); 86 87 for(int i=0;i<len;++i) 88 { 89 count[GetReminder(h[i],d)]++; 90 } 91 92 //printf("haha\n"); 93 94 //求右邊界 95 for(int i=8;i>=0;--i) 96 { 97 count[i]+=count[i+1]; 98 } 99 100 101 102 for(int i=len-1;i>=0;--i) 103 { 104 int r=GetReminder(h[i],d); 105 int index=count[r]; 106 tmp[index-1]=h[i]; 107 count[r]--; 108 } 109 110 memcpy(h,tmp,len*sizeof(int)); 111 } 112 113 free(tmp); 114 }

?

2.MSD(高位到低位排序)

下面我們直接來(lái)介紹MSD基數(shù)排序的步驟。

MSD基數(shù)排序是從最高位開(kāi)始對(duì)序列進(jìn)行分組,到最低位為止。但是其實(shí)現(xiàn)過(guò)程是和LSD基數(shù)排序不同的,排序過(guò)程中需要用到遞歸函數(shù)。

待排序序列

170, 45, 75, 90, 2, 24, 802, 66

我們看到,這里面的最大的數(shù)是3位數(shù)。所以說(shuō)我們開(kāi)始從百位對(duì)這些數(shù)進(jìn)行分組

0: 045, 075, 090,002, 024, 066
1: 170
2-7: 空
8: 802
9: 空

從這里開(kāi)始就和LSD基數(shù)排序有差別了。在LSD基數(shù)排序中,每次分好組以后開(kāi)始對(duì)桶中的數(shù)據(jù)進(jìn)行收集。然后根據(jù)下一位,對(duì)收集后的序列進(jìn)行分組。而對(duì)于MSD,在這里不會(huì)對(duì)桶中的數(shù)據(jù)進(jìn)行收集。我們要做的是檢測(cè)每個(gè)桶中的數(shù)據(jù)。當(dāng)桶中的元素個(gè)數(shù)多于1個(gè)的時(shí)候,要對(duì)這個(gè)桶遞歸進(jìn)行下一位的分組。

在這個(gè)例子中,我們要對(duì)0桶中的所有元素根據(jù)十位上的數(shù)字進(jìn)行分組

0: 002
1: 空
2: 024
3: 空
4: 045
5: 空
6: 066
7: 075
8: 空
9: 090

按照上面所說(shuō),我們應(yīng)該再遞歸的對(duì)每個(gè)桶中的元素根據(jù)個(gè)位上的數(shù)進(jìn)行分組。但是我們發(fā)現(xiàn),現(xiàn)在在每個(gè)桶中的元素的個(gè)數(shù)都是小于等于1的。因此,到這一步我們就開(kāi)始回退了。也就是說(shuō)我們開(kāi)始收集桶中的數(shù)據(jù)。收集完成以后,回退到上一層。此時(shí)按照百位進(jìn)行分組的桶變成了如下的形式

0: 002, 024, 045,066, 075, 090
1: 170
2-7: 空
8: 802
9: 空

然后我們?cè)趯?duì)這個(gè)桶中的數(shù)據(jù)進(jìn)行收集。收集起來(lái)以后序列如下

2, 24, 45, 66, 75, 90, 170, 802

整個(gè)MSD基數(shù)排序就是按照上面的過(guò)程進(jìn)行的。

在我對(duì)MSD基數(shù)排序步驟進(jìn)行敘述的時(shí)候,中間遞歸函數(shù)的過(guò)程可能敘述的不夠清楚。我個(gè)人建議對(duì)遞歸函數(shù)不了解的可以先了解一下遞歸函數(shù)的原理,然后再回來(lái)看這個(gè)過(guò)程可能對(duì)MSD基數(shù)排序過(guò)程更容易理解。

算法實(shí)現(xiàn):

1 int GetMaxDight(int* h, int len) 2 { 3 if(h==NULL) return 0; 4 if(len<1) return 0; 5 6 int max=h[0]; 7 for(int i=1;i<len;++i) 8 { 9 if(h[i]>max) max=h[i]; 10 } 11 12 int digit=1; 13 while(max/10!=0) 14 { 15 max/=10; 16 ++digit; 17 } 18 19 return digit; 20 } 21 22 int GetReminder(int value,int digit) 23 { 24 int div=1; 25 for(int i=1;i<digit;++i) 26 div*=10; 27 28 return value/div%10; 29 } 30 31 void RRadixSort_MSD(int* h, int begin, int end, int digit) 32 { 33 if(h==NULL) return; 34 if(begin>=end) return; 35 if(digit<1) return; 36 37 int start[10]; 38 int count[10]={0}; 39 int *tmp=(int*)calloc(end-begin+1,sizeof(int)); 40 41 for(int i=begin;i<=end;++i) 42 { 43 count[GetReminder(h[i],digit)]++; 44 } 45 46 memcpy(start,count,sizeof(start)); 47 48 //求右邊界 49 for(int i=1;i<10;++i) 50 { 51 start[i]+=start[i-1]; 52 } 53 54 for(int i=end;i>=begin;--i) 55 { 56 int r=GetReminder(h[i],digit); 57 int index=start[r]; 58 tmp[index-1]=h[i]; 59 start[r]--; 60 } 61 62 /* 63 for(int i=0;i<10;++i) 64 { 65 printf("%d ",start[i]); 66 } 67 68 printf("\n"); 69 */ 70 71 memcpy(&h[begin],tmp,(end-begin+1)*sizeof(int)); 72 73 for(int i=0;i<10;++i) 74 { 75 if(count[i]>1) 76 { 77 RRadixSort_MSD(h, start[i], start[i]+count[i]-1, digit-1); 78 } 79 } 80 } 81 82 void RadixSort_MSD(int* h, int len) 83 { 84 if(h==NULL) return; 85 if(len<=1) return; 86 87 int digit=GetMaxDight(h,len); 88 89 //printf("MaxDigit:%d\n",digit); 90 91 RRadixSort_MSD(h, 0, len-1, digit); 92 } 93 94 void RRadixSort_MSD_Reverse(int* h, int begin, int end, int digit) 95 { 96 if(h==NULL) return; 97 if(begin>=end) return; 98 if(digit<1) return; 99 100 int start[10]; 101 int count[10]={0}; 102 int *tmp=(int*)calloc(end-begin+1,sizeof(int)); 103 104 for(int i=begin;i<=end;++i) 105 { 106 count[GetReminder(h[i],digit)]++; 107 } 108 109 memcpy(start,count,sizeof(start)); 110 111 //求右邊界 112 for(int i=8;i>=0;--i) 113 { 114 start[i]+=start[i+1]; 115 } 116 117 for(int i=end;i>=begin;--i) 118 { 119 int r=GetReminder(h[i],digit); 120 int index=start[r]; 121 tmp[index-1]=h[i]; 122 start[r]--; 123 } 124 125 /* 126 for(int i=0;i<10;++i) 127 { 128 printf("%d ",start[i]); 129 } 130 131 printf("\n"); 132 */ 133 134 memcpy(&h[begin],tmp,(end-begin+1)*sizeof(int)); 135 136 for(int i=0;i<10;++i) 137 { 138 if(count[i]>1) 139 { 140 RRadixSort_MSD_Reverse(h, start[i], start[i]+count[i]-1, digit-1); 141 } 142 } 143 } 144 145 void RadixSort_MSD_Reverse(int* h, int len) 146 { 147 if(h==NULL) return; 148 if(len<=1) return; 149 150 int digit=GetMaxDight(h,len); 151 152 //printf("MaxDigit:%d\n",digit); 153 154 RRadixSort_MSD_Reverse(h, 0, len-1, digit); 155 }

?

十、主函數(shù)

?

1 void Swap(int& a, int& b) 2 { 3 int t=a; 4 a=b; 5 b=t; 6 7 return; 8 } 9 10 int main() 11 { 12 int A[10]={0}; 13 srand((unsigned)time(NULL)); 14 15 printf("before:\n"); 16 for(int i=0;i<10;++i) 17 { 18 A[i]=rand()%100; 19 printf("%d ",A[i]); 20 } 21 printf("\n"); 22 23 printf("after:\n"); 24 //QuickSort(A,0,9); 25 //BubbleSort(A,sizeof(A)/sizeof(int)); 26 //SelectionSort(A,sizeof(A)/sizeof(int)); 27 //InsertSort(A,sizeof(A)/sizeof(int)); 28 //MergeSort(A,sizeof(A)/sizeof(int)); 29 //ShellSort(A,sizeof(A)/sizeof(int)); 30 //HeapSort(A,sizeof(A)/sizeof(int)); 31 //RadixSort_LSD(A,sizeof(A)/sizeof(int)); 32 //RadixSort_MSD(A,sizeof(A)/sizeof(int)); 33 //RadixSort_LSD_Reverse(A,sizeof(A)/sizeof(int)); 34 RadixSort_MSD_Reverse(A,sizeof(A)/sizeof(int)); 35 for(int i=0;i<sizeof(A)/sizeof(int);++i) 36 { 37 printf("%d ",A[i]); 38 } 39 printf("\n"); 40 41 return 0; 42 }

?

轉(zhuǎn)載于:https://www.cnblogs.com/fnlingnzb-learner/p/9374732.html

總結(jié)

以上是生活随笔為你收集整理的C++经典排序算法总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。