所有排序總結(內排序)(續) 線性時間排序

2022-03-11 18:17:39 字數 3748 閱讀 2573

一、前言

這裡稍微證明一下基於比較的排序演算法的下界,採用決策樹模型。下圖是乙個含三個元素的輸入序列,採用插入排序演算法的決策樹。每乙個結點裡面包含(i,j)。左子樹代表i<=j的情況,右子樹代表i>j的情況。

各位,如果有跟我一樣,半天看不懂的話,我就稍微解釋一下。拿(1,2,3)這個序列的得出,稍作說明。在根節點處,1和2比較(輸入位置為1的元素與輸入位置為2的元素比較,不是1和2比較,下同)。 1<2,進入到左子樹。  2和3比較,  2<3,還是到左子樹   得出排序後的序列(1,2,3)。

有一點要說明的是:任何乙個正確的排序演算法,都能產生全部n!個序列。這n!個序列對應決策樹中的所有葉節點。

從這個模型明顯可以得出結論:從根節點到任意乙個葉節點之間最長路徑的長度,也即決策樹的高度(每一本書所說的高度概念不一致,這裡取圖中的高度為3),表示排序演算法在最壞情況下的比較次數。

利用決策樹模型對基於比較的排序演算法時間下界的證明:

考慮乙個高為 h,  葉節點個數為l的  決策樹。  輸入序列為n。  顯然  n!<=l。(說明:如果不考慮n個元素有相等的情況,n!應該等於l).

又,乙個高度為h的完全二叉樹的葉節點的數目為2h。 l

<=2h

即 n!<=l<=2h.

取對數可得到 h>=lg(n!)。

又, lg(n!) =θ(nlgn);

得到 h>=ω(nlgn);

說明一下這個:lg(n!) = θ(nlgn);

(注:好長時間沒看這些,應該是從來沒有認真看過==...  都沒有搞清楚  ο  θ   ω  這個幾個符號的意思。翻了導論前面幾章才算明白。   這個的證明要用到斯特靈公式。    我感覺已經超出我的能力範圍之外了,不想花時間再去研究這玩意兒了 。)

斯特林公式參看這裡 

簡單說就是:

f(n) = θ(g(n)) 當且僅當f(n) =  ω(nlgn)和f(n) = ο(nlgn)。  這裡的「=」並不是等號的意思。  f(n) = θ(g(n)) 是 f(n) 屬於 θ(g(n)) 的意思,θ(g(n)) 是乙個集合。

lg(n!) = θ(nlgn);  就是說存在  c1和c2  所有的n>=n0,有 0<=c1(nlgn) <= lg(n!) <= c2(nlgn) 。

二、三種線性時間排序

1、計數排序

計數排序就是根據元素的值統計元素的個數,然後直接把元素放到合適的位置,以達到θ(n)的目的。思路也很簡單,廢話不多說,直接上**

1 #include 2 #include 3

void countingsort(int *a,int n,int *b,int *c,intk)4

21}22int

main()

23

寫**的時候,習慣從0開始了。導致第四個for迴圈的時候忘記減1了,糾結了好久。。。。顯然可以看出上面的計數排序的θ(n+k)。好吧,我一開始也不知道這個是怎麼來的。一共四個迴圈,θ(2n+2k),去掉常數就是θ(n+k),n表示待排序陣列,k表示要排序的的值的範圍。這樣,計數排序就很有侷限了,k的值注定了這種排序的適用範圍很小。

2、桶排序

也許看完上面的計數排序,你會發現既然c陣列已經統計完了每個值的個數,那麼直接掃瞄一遍c陣列,依次序輸出也就ok了。這個就算是桶排序了,這裡的每個桶只能儲存乙個值,算作是特殊的桶排序吧。將上面的**稍作改變就可以得到:

1

void bucketsort(int *a,int n,int *b,int *c,intk)2

20}21 }

一般桶排序的操作步驟就是:

1、確定要排序陣列的取值範圍,並劃分成m個區間;

2、給這個m區間劃分m個桶,並對每個桶內進行排序;

3、再依次序把這m個桶的元素串接起來。

這裡上一幅導論裡面的圖,它講100劃分成10個區間,設計10個桶。然後將陣列裡面的元素放到相應的桶裡面去。

}這段**裡面,設元素有n個,m個桶,平均每個桶裡面有n/m個元素。在桶裡面的,我沒有採用其他的排序演算法,簡單的插入。因此桶裡面為θ(n/m)。總的時間複雜度為   θ(n * n/m)=θ(n2/m) ,可以看出來,當桶的數量越多。。。趨近於n時,時間複雜度就為θ(n)了。

3、基數排序

基數排序是對多個關鍵字的排序。兩種方式,一是從首要關鍵字到次要關鍵字,另外一種是從次要關鍵字到首要關鍵字。導論上面說的是第二種。如果是從首要關鍵字開始,就要對首要關鍵字相同的進行分堆,再根據次要關鍵字排序。增加開銷,有點桶排序的味道了。

這裡我以技術排序為基礎,從次要關鍵字開始,寫了這個基數排序:

1 #include 2 #include 3 #include 4

void countingsort(int *a,int n,int *c,int k,intd)5

20for(int i=1;i)

21 c[i] += c[i-1]; //

c[i]表示小於等於i的個數

22for(int j = n-1;j>=0;j--)//

放到合適的位置,因為是從0開始 要減1

2328

for(int i=0;i)

2932}33

void radixsort(int a,int n,int

d)3441}

42int

main()

43

有人跟我說,**裡面key對 位數的獲取 ,可以用位操作,我想了想,需要bcd碼,沒有想到特別好的解決方案,還是這樣易懂。我總覺得硬性搞成位操作反而有點麻煩了,沒有多大意義。沒有讓人眼睛一亮的解決方案。上面的**時間複雜度為θ(d(n+k));

三、總結

顯然,線性時間排序有很大的侷限,對資料值範圍有要求。

另外,寫這三個排序,原本以為半天多都可以搞定,結果弄了兩三天。遭到了無數的鄙視。。最關鍵還是自己不夠熟悉。。

關於排序,暫時就到這裡吧。開學了,下學期專業課壓死人。每學期好多門課,但是都感覺沒一門學得紮實。

以後如果有新的認識,我會再繼續寫寫的。我覺得還會有續的,就是不知道多久了。。。。

持續關注資料結構演算法,一來,它很重要,二來,還是有點興趣。

所有排序總結(內排序)

花時間把所有的排序重新 寫了一遍。應該是認真寫過一遍,學的時候根本就沒寫過 寫得時候才發現,理解不深刻。基本上 只是懂怎麼做,不懂為什麼。把我寫得記在這裡,以後用得著了回來看看。暫時就到這裡吧,以後有時間,繼續研究這些東西。在寫出來。三個o n2 的演算法 選擇排序 1 void selection...

3 1 1 排序 內排序

穩定與非穩定 相等的數相對位置是否改變 內排序與外排序 是否用外存儲存 思路 從第乙個元素開始構建有序的排序 後面未排序的逐一往前面有序的排序中找適當位置插入 時間複雜度 分類 穩定 內排序 插入排序 void insertion sort int data,int len 比較和交換次數 int ...

線性時間排序

public void radixsort int a a k key 我們前面提到的方法,基本上都是比較排序,本篇介紹三種非比較型別的排序,計數排序 基數排序 桶排序,比較排序的最壞的情況都是經過nlgn的,線性排序的時間複雜度基本上都是線性關係。1.計數排序 計數排序不是通過元素之間的比較,而是...