一、前言
這裡稍微證明一下基於比較的排序演算法的下界,採用決策樹模型。下圖是乙個含三個元素的輸入序列,採用插入排序演算法的決策樹。每乙個結點裡面包含(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).說明一下這個:lg(n!) = θ(nlgn);又,乙個高度為h的完全二叉樹的葉節點的數目為2h。 l
<=2h
即 n!<=l<=2h.
取對數可得到 h>=lg(n!)。
又, lg(n!) =θ(nlgn);
得到 h>=ω(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寫**的時候,習慣從0開始了。導致第四個for迴圈的時候忘記減1了,糾結了好久。。。。顯然可以看出上面的計數排序的θ(n+k)。好吧,我一開始也不知道這個是怎麼來的。一共四個迴圈,θ(2n+2k),去掉常數就是θ(n+k),n表示待排序陣列,k表示要排序的的值的範圍。這樣,計數排序就很有侷限了,k的值注定了這種排序的適用範圍很小。void countingsort(int *a,int n,int *b,int *c,intk)4
21}22int
main()
23
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有人跟我說,**裡面key對 位數的獲取 ,可以用位操作,我想了想,需要bcd碼,沒有想到特別好的解決方案,還是這樣易懂。我總覺得硬性搞成位操作反而有點麻煩了,沒有多大意義。沒有讓人眼睛一亮的解決方案。上面的**時間複雜度為θ(d(n+k));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
三、總結
顯然,線性時間排序有很大的侷限,對資料值範圍有要求。
另外,寫這三個排序,原本以為半天多都可以搞定,結果弄了兩三天。遭到了無數的鄙視。。最關鍵還是自己不夠熟悉。。
關於排序,暫時就到這裡吧。開學了,下學期專業課壓死人。每學期好多門課,但是都感覺沒一門學得紮實。
以後如果有新的認識,我會再繼續寫寫的。我覺得還會有續的,就是不知道多久了。。。。
持續關注資料結構演算法,一來,它很重要,二來,還是有點興趣。
所有排序總結(內排序)
花時間把所有的排序重新 寫了一遍。應該是認真寫過一遍,學的時候根本就沒寫過 寫得時候才發現,理解不深刻。基本上 只是懂怎麼做,不懂為什麼。把我寫得記在這裡,以後用得著了回來看看。暫時就到這裡吧,以後有時間,繼續研究這些東西。在寫出來。三個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.計數排序 計數排序不是通過元素之間的比較,而是...