幾種排序演算法的講解(二)

2021-08-05 23:13:22 字數 2939 閱讀 2487

希爾排序其實是直接插入排序演算法的一種變形,實質是分組插入排序。又稱縮小增量排序。

該方法首先要理解分組操作,其實是以間隔分組,即每間隔幾個數後就分做一組,然後進行插入排序。接著把間隔縮短一半,以此下去,直到間隔沒有,才停下操作。

如:有10個資料,開始的時候每間隔10/2 = 5 劃為一組,即第1個數與第6個數為一組,第2個數與第7個數為一組。。。。。。以此類推,然後對每一組的數進行插入排序。排序好後縮短間隔,5/2 = 2,即每間隔2個數就劃為一組,以此操作直到間隔為0,無法縮小間隔為止。
如,對10、49、23、1、5、50、44、8、23、2、6這10個資料進行希爾排序。

第一次分組的時候: inter = 10/2 = 5

第一次分組如圖,分成了5組,,,,,這五個組,接著對每一組進行直接插入排序。(沒間隔幾個數,就分成幾組)

第二次分組的時候: inter = 5/2 = 2

現在分成了2組,同上,對每組進行直接插入排序

第三次分組的時候: inter = 2/2 = 1

現在只有一組了,仍然進行插入排序

第四次無法分組了: inter = 1/2 = 0

希爾排序實現的**如下:

#include 

using

namespace

std;

const

int maxn = 10000;

int a[maxn];

int main()

if(cur!=i-inter)}}

}for(int i=1; i<=n; i++)

cout

cout

0;}

為了讓**盡可能地貼近所描述的語言,上面的**寫得過於複雜,看得有些頭疼,我們可以把該**縮減為以下**:

#include 

using

namespace

std;

const

int maxn = 10000;

int a[maxn];

int main()

}for(int i=1; i<=n; i++)

cout

cout

0;}

**精簡,不過不易懂,再次也不多講解這乙個**,讀者自己思考一番吧。

堆排序演算法在處理大資料的效率是前面四種演算法不可比的。

那麼要學堆排序,首先要對樹有個了解,準確地說是對二叉樹要又了解。堆排序就是乙個特殊的完全二叉樹。

我們用陣列儲存一堆資料,然後要把陣列當做乙個二叉樹來看。二叉樹特點是,乙個父節點連線兩個兒子節點。開始的時候把一堆數存入陣列裡,然後把陣列看成二叉樹,即存數到陣列裡後就建立好了乙個二叉樹,此時的樹裡面的值都是混亂的。

如何去排序一棵二叉樹?以排序乙個最小堆為例。

一棵樹的「某乙個節點」父子間的值所在的位置不對的時候,即兩個兒子中有值小於父親的值時,要排序父子關係的話,也會影響後面子孫的位置。

我們首先要對這種牽連一人而禍害一窩的行為用一種方式進行處理(即用乙個函式),然後遍歷每乙個節點,用這種行為處理方式處理每乙個節點。

下面說說怎麼建立這種處理方式函式。

要找一種規律,就像數學思維一樣,任意取乙個值設為變數x,繞著x這個值進行某些變化直到某條件成立為止。

這裡就任意取乙個樹的節點作為「變數節點 」,從這個節點開始進行排序,首先找出該節點和該節點的兩個兒子之間的最小的,讓最小的與節點的值換一換。

如果沒有換值,那麼停止,證明了該節點與他的兒子順序是正確的;如果換值了,那麼接下來取與節點交換的兒子作為「變數節點」,繼續以上操作,直到要麼是到了最後的葉處即到底了,要麼是兒子與父親的值沒有交換為止。可見這裡是乙個遞迴操作。

好的,找到了處理乙個節點關係的方式(函式),那麼我們現在要遍歷每個節點,用這個方式處理每乙個節點,以達到排序乙個陣列的目的。

那麼,如何遍歷才是好呢?我們找到的處理方式是用來處理有兒子的節點,是吧。那麼從只有兒子沒有孫子的節點開始處理。

這樣從底部打好基礎,節省了很多的麻煩。

根據樹的定義,有n個節點,那麼兒子是葉的節點是從第n/2個節點開始的,前面的節點都有子孫了。那就是從第n/2個節點開始遍歷到根節點。

說來說去,有點繞,結合下面的**有助於消化堆排序。

#include 

using

namespace

std;

const

int maxn = 100000;

int a[maxn];

//這個就是乙個牽一髮而動全身的處理方式,乙個遞迴函式

//引數即當前的節點的位置,還有樹的節點數

void softdown(int cur, int n)

//如果發現最小值的位置不在父節點處,那麼交換節點值,繼續下乙個節點進行處理

//否則遞迴停止

if(cur != temp) swap(a[cur], a[temp]);

else flag = 1;

cur = temp;

}}void create(int n) //排序二叉樹

//因為根節點的值是最小的,所以每次輸出根節點的值

//然後把最後一位與根節點值交換,順便讓長度-1.再排序一次樹。

//這樣就成了陣列裡存的值是從大到小排序,但輸出的時候是從小到大。

void cout(int n) //輸出函式

}int main()

幾種排序的演算法

插入排序 將乙個資料插入到已經排好序的有序資料中,從而得到乙個新的 個數加一的有序資料 def insert sort list count len list 算出列表長度用於控制迴圈次數 for i in range 1,count key list i 列表中每個值 j i 1 j 比 i 值小...

幾種排序演算法

幾種比較常見的排序演算法 第一種 函式功能 雙向氣泡排序 2013.7.8 時間複雜度o n 2 include void mp int array,int n if mmax 0 沒有記錄交換,掃瞄結束 break bmax mmax for i bmax 1 i bmin i 此次掃瞄使輕氣泡上...

幾種排序演算法

本帖依據學習進度持續更新 資料結構與演算法分析 c語言描述 學到第七章,是時候該系統的學習一下排序演算法了。首先學到的是插入排序,演算法就不贅述了,書上部落格上到處都有。書上的兩個定理還不太明白 插入排序 定理7.1 n個互異數的陣列的平均逆序數是n n 1 4。定理7.2 通過交換相鄰元素進行排序...