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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

《大话数据结构》第9章 排序 9.8 归并排序(上)

發布時間:2025/3/21 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《大话数据结构》第9章 排序 9.8 归并排序(上) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

9.8.1?歸并排序介紹

??????? 前面我們講了堆排序,因為它用到了完全二叉樹,充分利用了完全二叉樹的深度是?log2n?+1的特性,所以效率比較高。不過堆結構的設計本身是比較復雜的,老實說,能想出這樣的結構就挺不容易,有沒有更直接簡單的辦法利用完全二叉樹來排序呢?當然是有。
??????? 先來舉一個例子。你們知道高考一本、二本、專科分數線是如何劃分出來的嗎?
??????? 簡單地說,如果各高校本科專業在某省高三理科學生中計劃招收1萬名,那么將全省參加高考的理科學生分數倒排序,第1萬名的總分數就是當年本科生的分數線(現實可能會比這復雜,這里簡化之)。也就是說,即使你是你們班級第一、甚至年級第一名,如果你沒有上分數線,則說明你的成績排不到全省前1萬名,你也就基本失去了當年上本科的機會了。
??????? 換句話說,所謂的全省排名,其實也就是每個市、每個縣、每個學校、每個班級的排名合并后再排名得到的。注意我這里用到了合并一詞。
我們要比較兩個學生的成績高低是很容易的,比如甲比乙分數低,丙比丁分數低。那么我們也就可以很容易得到甲乙丙丁合并后的成績排名,同樣的,戊己庚辛的排名也容易得到,由于他們兩組分別有序了,把他們八個學生成績合并有序也是很容易做到的了,繼續下去……最終完成全省學生的成績排名,此時高考狀元也就誕生了。
??????? 為了更清晰地說清楚這里的思想,大家來看圖9-8-1,我們將本是無序的數組序列{16,7,13,10,9,15,3,2,5,8,12,1,11,4,6,14},通過兩兩合并排序后,再合并,最終獲得了一個有序的數組。注意仔細觀察它的形狀,你會發現,它像極了一棵倒置的完全二叉樹,通常涉及到完全二叉樹結構的排序算法,效率一般都不低的——這就是我們要講的歸并排序法。
?

?


9.8.2?歸并排序算法
??????? “歸并”一詞的中文含義就是合并、并入的意思,而在數據結構中的定義是將兩個或兩個以上的有序表組合成一個新的有序表。
????????歸并排序(Merging Sort)就是利用歸并的思想實現的排序方法。它的原理是假設初始序列含有n個記錄,則可以看成是n個有序的子序列,每個子序列的長度為1,然后兩兩歸并,得到?n/2?(?x?表示不小于x的最小整數)個長度為2或1的有序子序列;再兩兩歸并,……,如此重復,直至得到一個長度為n的有序序列為止,這種排序方法稱為2路歸并排序。?
??????? 好了,有了對歸并排序的初步認識后,我們來看代碼。

/* 對順序表L作歸并排序 */ void MergeSort(SqList *L) { MSort(L->r,L->r,1,L->length); }

??????? 一句代碼,別奇怪,它只是調用了另一個函數而已。為了與前面的排序算法統一,我們用了同樣的參數定義SqList *L,由于我們要講解的歸并排序實現需要用到遞歸調用 ,因此我們外封裝了一個函數。假設現在要對數組{50,10,90,30,70,40,80,60,20}進行排序,L.length=9,我現來看看MSort的實現。

/* 將SR[s..t]歸并排序為TR1[s..t] */ void MSort(int SR[],int TR1[],int s, int t) {int m;int TR2[MAXSIZE+1];if(s==t)TR1[s]=SR[s];else{m=(s+t)/2; /* 將SR[s..t]平分為SR[s..m]和SR[m+1..t] */MSort(SR,TR2,s,m); /* 遞歸地將SR[s..m]歸并為有序的TR2[s..m] */MSort(SR,TR2,m+1,t); /* 遞歸地將SR[m+1..t]歸并為有序TR2[m+1..t] */Merge(TR2,TR1,s,m,t); /* 將TR2[s..m]和TR2[m+1..t]歸并到TR1[s..t] */} }

1)?MSort被調用時,SR與TR1都是{50,10,90,30,70,40,80,60,20},s=1,t=9,最終我們的目的就是要將TR1中的數組排好順序。
2)?第5行,顯然s不等于t,執行第8~13行語句塊。
3)?第9行,m=(1+9)/2=5。m就是序列的正中間下標
4)?此時第10行,調用“MSort(SR,TR2,1,5);”的目標就是將數組SR中的第1~5的關鍵字歸并到有序的TR2(調用前TR2為空數組),第11行,調用“MSort(SR,TR2,6,9);”的目標就是將數組SR中的第6~9的關鍵字歸并到有序的TR2。也就是說,在調用這兩句代碼之前,代碼已經準備將數組分成了兩組了。如圖9-8-2。
?


5)?第12行,函數Merge的代碼細節一會再講,調用“Merge(TR2,TR1,1,5,9);”的目標其實就是將第10和11行代碼獲得的數組TR2(注意它是下標為1~5和6~9的關鍵字分別有序)歸并為TR1,此時相當于整個排序就已經完成了。如圖9-8-3。
?
6)?再來看第10行遞歸調用進去后,s=1,t=5,m=(1+5)/2=3。此時相當于將5個記錄再為三個和兩個。繼續遞歸進去,直到細分為一個記錄填入TR2,此時s與t相等,遞歸返回,如圖9-8-4的左圖。每次遞歸返回后都會執行當前遞歸函數的第12行,將TR2歸并到TR1中。如圖9-8-4的右圖。最終使得當前序列有序。
?

7)?同樣的第11行也是類似方式,如圖9-8-5。
?
8)?此時也就是剛才所講的最后一次執行第12行代碼,將{10,30,50,70,90}與{20,40,60,80}歸并為最終有序的序列。
可以說,如果對遞歸函數的運行方式理解比較透的話,MSort函數還是很好理解的。我們來看看整個數據變換示意圖,如圖9-8-6。
?

現在我們來看看Merge函數的代碼是如何實現的。

?

/* 將有序的SR[i..m]和SR[m+1..n]歸并為有序的TR[i..n] */ void Merge(int SR[],int TR[],int i,int m,int n) {int j,k,l;for(j=m+1,k=i;i<=m && j<=n;k++) /* 將SR中記錄由小到大歸并入TR */{if (SR[i]<SR[j])TR[k]=SR[i++];elseTR[k]=SR[j++];}if(i<=m){for(l=0;l<=m-i;l++)TR[k+l]=SR[i+l]; /* 將剩余的SR[i..m]復制到TR */}if(j<=n){for(l=0;l<=n-j;l++)TR[k+l]=SR[j+l]; /* 將剩余的SR[j..n]復制到TR */} }

1)?假設我們此時調用的Merge就是將{10,30,50,70,90}與{20,40,60,80}歸并為最終有序的序列,因此數組SR為{10,30,50,70,90,20,40,60,80},i=1,m=5,n=9。
2)?第4行,for循環,j由m+1=6開始到9,i由1開始到5,k由1開始每次加1,k值用于目標數組TR的下標。
3)?第6行,SR[i]=SR[1]=10,SR[j]= SR[6]=20,SR[i]<SR[j],執行第7行,TR[k]=TR[1]=10,并且i++。如圖9-8-7。
?


4)?再次循環,k++得到k=2,SR[i]=SR[2]=30,SR[j]= SR[6]=20,SR[i]>SR[j],執行第9行,TR[k]=TR[2]=20,并且j++,如圖9-8-8。
?

5)?再次循環,k++得到k=3,SR[i]=SR[2]=30,SR[j]= SR[7]=40,SR[i]<SR[j],執行第7行,TR[k]=TR[3]=30,并且i++,如圖9-8-9。
?

6)?接下來完全相同的操作,一直到j++后,j=10,大于9退出循環。如圖9-8-10。
?

7)?第11~20行的代碼,其實就將歸并剩下的數組數據,移動到TR的后面。當前k=9,i=m=5,執行第13~20行代碼,for循環l=0,TR[k+l]=SR[i+l]=90。大功告成。
??????? 就這樣,我們的歸并排序就算是完成了一次排序工作,怎么樣,和堆排序比,是不是要簡單一些呢? 出處:http://www.cnblogs.com/cj723/archive/2011/04/25/2026751.html

總結

以上是生活随笔為你收集整理的《大话数据结构》第9章 排序 9.8 归并排序(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。