第四百一十五節,python經常使用排序算法學習html
名稱node |
複雜度python |
說明git |
備註算法 |
冒泡排序 |
O(N*N)數據庫 |
將待排序的元素看做是豎着排列的「氣泡」,較小的元素比較輕,從而要往上浮編程 |
|
插入排序數組 Insertion sort數據結構 |
O(N*N) |
逐一取出元素,在已經排序的元素序列中從後向前掃描,放到適當的位置 |
起初,已經排序的元素序列爲空 |
選擇排序 |
O(N*N) |
首先在未排序序列中找到最小元素,存放到排序序列的起始位置,而後,再從剩餘未排序元素中繼續尋找最小元素,而後放到排序序列末尾。以此遞歸。 |
|
快速排序 Quick Sort |
O(n *log2(n)) |
先選擇中間值,而後把比它小的放在左邊,大的放在右邊(具體的實現是從兩邊找,找到一對後交換)。而後對兩邊分別使用這個過程(遞歸)。 |
|
堆排序HeapSort |
O(n *log2(n)) |
利用堆(heaps)這種數據結構來構造的一種排序算法。堆是一個近似徹底二叉樹結構,並同時知足堆屬性:即子節點的鍵值或索引老是小於(或者大於)它的父節點。 |
近似徹底二叉樹 |
希爾排序 SHELL |
O(n1+£) 0<£<1 |
選擇一個步長(Step) ,而後按間隔爲步長的單元進行排序.遞歸,步長逐漸變小,直至爲1. |
|
箱排序 |
O(n) |
設置若干個箱子,把關鍵字等於 k 的記錄全都裝入到第k 個箱子裏 ( 分配 ) ,而後按序號依次將各非空的箱子首尾鏈接起來 ( 收集 ) 。 |
分配排序的一種:經過" 分配 " 和 " 收集 " 過程來實現排序。 |
http://www.jxiou.com/
http://www.jxiou.com/lu_yin_wang_zhan.html
data_set = [9, 1, 22, 31, 45, 3, 6, 2, 11] loop_count = 0 for j in range(len(data_set)): for i in range(len( data_set) - j - 1): # -1 是由於每次比對的都 是i 與i +1,不減1的話,最後一次對比會超出list 獲取範圍,-j是由於,每一次大loop就表明排序好了一個最大值,放在了列表最後面,下次loop就不用再運算已經排序好了的值 了 if data_set[i] > data_set[i + 1]: # switch tmp = data_set[i] data_set[i] = data_set[i + 1] data_set[i + 1] = tmp loop_count += 1 print(data_set) print(data_set) print("loop times", loop_count)
E:\Evns\jxiou\Scripts\python.exe H:/shipbfq/22.py [1, 9, 22, 31, 3, 6, 2, 11, 45] [1, 9, 22, 3, 6, 2, 11, 31, 45] [1, 9, 3, 6, 2, 11, 22, 31, 45] [1, 3, 6, 2, 9, 11, 22, 31, 45] [1, 3, 2, 6, 9, 11, 22, 31, 45] [1, 2, 3, 6, 9, 11, 22, 31, 45] [1, 2, 3, 6, 9, 11, 22, 31, 45] [1, 2, 3, 6, 9, 11, 22, 31, 45] [1, 2, 3, 6, 9, 11, 22, 31, 45] [1, 2, 3, 6, 9, 11, 22, 31, 45] loop times 36
該算法經過選擇最小未排序的項目,而後將其與下一個要填充的項目進行交換。
選擇排序的工做原理以下:在整個數組中查找最小元素,一旦找到它,就將它與數組的第一個元素交換(最小元素)。而後查找剩餘數組中的最小元素(沒有第一個元素的數組),並將其與第二個元素交換。而後查找剩餘數組中的最小元素(沒有第一個元素和第二個元素的數組),並將其與第三個元素交換,以此類推。下面是一個例子,
#!/usr/bin/env python # -*- coding:utf8 -*- data_set = [9, 1, 22, 31, 45, 3, 6, 2, 11] smallest_num_index = 0 # 初始列表最小值,默認爲第一個 loop_count = 0 for j in range(len(data_set)): for i in range(j, len(data_set)): if data_set[i] < data_set[smallest_num_index]: # 當前值 比以前選出來的最小值 還要小,那就把它換成最小值 smallest_num_index = i loop_count += 1 else: print("smallest num is ", data_set[smallest_num_index]) tmp = data_set[smallest_num_index] data_set[smallest_num_index] = data_set[j] data_set[j] = tmp print(data_set) print("loop times", loop_count)
E:\Evns\jxiou\Scripts\python.exe H:/shipbfq/22.py smallest num is 1 [1, 9, 22, 31, 45, 3, 6, 2, 11] loop times 9 smallest num is 2 [1, 2, 22, 31, 45, 3, 6, 9, 11] loop times 17 smallest num is 3 [1, 2, 3, 31, 45, 22, 6, 9, 11] loop times 24 smallest num is 6 [1, 2, 3, 6, 45, 22, 31, 9, 11] loop times 30 smallest num is 9 [1, 2, 3, 6, 9, 22, 31, 45, 11] loop times 35 smallest num is 11 [1, 2, 3, 6, 9, 11, 31, 45, 22] loop times 39 smallest num is 22 [1, 2, 3, 6, 9, 11, 22, 45, 31] loop times 42 smallest num is 31 [1, 2, 3, 6, 9, 11, 22, 31, 45] loop times 44 smallest num is 45 [1, 2, 3, 6, 9, 11, 22, 31, 45] loop times 45
最壞狀況下的運行時複雜度是O(n2)。
插入排序(Insertion Sort)的基本思想是:將列表分爲2部分,左邊爲排序好的部分,右邊爲未排序的部分,循環整個列表,每次將一個待排序的記錄,按其關鍵字大小插入到前面已經排好序的子序列中的適當位置,直到所有記錄插入完成爲止。
插入排序很是相似於整撲克牌。
在開始摸牌時,左手是空的,牌面朝下放在桌上。接着,一次從桌上摸起一張牌,並將它插入到左手一把牌中的正確位置上。爲了找到這張牌的正確位置,要將它與手中已有的牌從右到左地進行比較。不管何時,左手中的牌都是排好序的。
也許你沒有意識到,但其實你的思考過程是這樣的:如今抓到一張7,把它和手裏的牌從右到左依次比較,7比10小,應該再往左插,7比5大,好,就插這裏。爲何比較了10和5就能夠肯定7的位置?爲何不用再比較左邊的4和2呢?由於這裏有一個重要的前提:手裏的牌已是排好序的。如今我插了7以後,手裏的牌仍然是排好序的,下次再抓到的牌還能夠用這個方法插入。編程對一個數組進行插入排序也是一樣道理,但和插入撲克牌有一點不一樣,不可能在兩個相鄰的存儲單元之間再插入一個單元,所以要將插入點以後的數據依次日後移動一個單元。
#!/usr/bin/env python # -*- coding:utf8 -*- source = [92, 77, 67, 8, 6, 84, 55, 85, 43, 67] for index in range(1, len(source)): current_val = source[index] # 先記下來每次大循環走到的第幾個元素的值 position = index while position > 0 and source[position - 1] > current_val: # 當前元素的左邊的緊靠的元素比它大,要把左邊的元素一個一個的往右移一位,給當前這個值插入到左邊挪一個位置出來 source[position] = source[position - 1] # 把左邊的一個元素往右移一位 position -= 1 # 只一次左移只能把當前元素一個位置 ,還得繼續左移只到此元素放到排序好的列表的適當位置 爲止 source[position] = current_val # 已經找到了左邊排序好的列表裏不小於current_val的元素的位置,把current_val放在這裏 print(source)
E:\Evns\jxiou\Scripts\python.exe H:/shipbfq/22.py [77, 92, 67, 8, 6, 84, 55, 85, 43, 67] [67, 77, 92, 8, 6, 84, 55, 85, 43, 67] [8, 67, 77, 92, 6, 84, 55, 85, 43, 67] [6, 8, 67, 77, 92, 84, 55, 85, 43, 67] [6, 8, 67, 77, 84, 92, 55, 85, 43, 67] [6, 8, 55, 67, 77, 84, 92, 85, 43, 67] [6, 8, 55, 67, 77, 84, 85, 92, 43, 67] [6, 8, 43, 55, 67, 77, 84, 85, 92, 67] [6, 8, 43, 55, 67, 67, 77, 84, 85, 92]
更容易理解的版本
#!/usr/bin/env python # -*- coding:utf8 -*- data_set = [9, 1, 22, 9, 31, -5, 45, 3, 6, 2, 11] for i in range(len(data_set)): # position = i while i > 0 and data_set[i] < data_set[i - 1]: # 右邊小於左邊相鄰的值 tmp = data_set[i] data_set[i] = data_set[i - 1] data_set[i - 1] = tmp i -= 1 print(data_set)
E:\Evns\jxiou\Scripts\python.exe H:/shipbfq/22.py
[-5, 1, 2, 3, 6, 9, 9, 11, 22, 31, 45]
設要排序的數組是A[0]……A[N-1],首先任意選取一個數據(一般選用數組的第一個數)做爲關鍵數據,而後將全部比它小的數都放到它前面,全部比它大的數都放到它後面,這個過程稱爲一趟快速排序。值得注意的是,快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變更
注:在待排序的文件中,若存在多個關鍵字相同的記錄,通過排序後這些具備相同關鍵字的記錄之間的相對次序保持不變,該排序方法是穩定的;若具備相同關鍵字的記錄之間的相對次序發生改變,則稱這種排序方法是不穩定的。
要注意的是,排序算法的穩定性是針對全部輸入實例而言的。即在全部可能的輸入實例中,只要有一個實例使得算法不知足穩定性要求,則該排序算法就是不穩定的。
排序演示
示例
下標
|
0
|
1
|
2
|
3
|
4
|
5
|
數據
|
6
|
2
|
7
|
3
|
8
|
9
|
下標
|
0
|
1
|
2
|
3 |
4
|
5
|
數據
|
3
|
2
|
7
|
6
|
8
|
9
|
下標
|
0
|
1
|
2
|
3
|
4
|
5
|
數據
|
3
|
2
|
6
|
7
|
8
|
9
|
下標
|
0
|
1
|
2
|
3
|
4
|
5
|
數據
|
3
|
2
|
6
|
7
|
8
|
9
|
# _*_coding:utf-8_*_ __author__ = 'Alex Li' def quick_sort(array, left, right): ''' :param array: :param left: 列表的第一個索引 :param right: 列表最後一個元素的索引 :return: ''' if left >= right: return low = left high = right key = array[low] # 第一個值 while low < high: # 只要左右未碰見 while low < high and array[high] > key: # 找到列表右邊比key大的值 爲止 high -= 1 # 此時直接 把key(array[low]) 跟 比它大的array[high]進行交換 array[low] = array[high] array[high] = key while low < high and array[low] <= key: # 找到key左邊比key大的值,這裏爲什麼是<=而不是<呢?你要思考。。。 low += 1 # array[low] = # 找到了左邊比k大的值 ,把array[high](此時應該剛存成了key) 跟這個比key大的array[low]進行調換 array[high] = array[low] array[low] = key quick_sort(array, left, low - 1) # 最後用一樣的方式對分出來的左邊的小組進行同上的作法 quick_sort(array, low + 1, right) # 用一樣的方式對分出來的右邊的小組進行同上的作法 if __name__ == '__main__': array = [96, 14, 10, 9, 6, 99, 16, 5, 1, 3, 2, 4, 1, 13, 26, 18, 2, 45, 34, 23, 1, 7, 3, 22, 19, 2] # array = [8,4,1, 14, 6, 2, 3, 9,5, 13, 7,1, 8,10, 12] print("before sort:", array) quick_sort(array, 0, len(array) - 1) print("-------final -------") print(array)
E:\Evns\jxiou\Scripts\python.exe H:/shipbfq/22.py before sort: [96, 14, 10, 9, 6, 99, 16, 5, 1, 3, 2, 4, 1, 13, 26, 18, 2, 45, 34, 23, 1, 7, 3, 22, 19, 2] -------final ------- [1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 13, 14, 16, 18, 19, 22, 23, 26, 34, 45, 96, 99]
樹(Tree)是元素的集合。咱們先以比較直觀的方式介紹樹。下面的數據結構是一個樹:
樹有多個節點(node),用以儲存元素。某些節點之間存在必定的關係,用連線表示,連線稱爲邊(edge)。邊的上端節點稱爲父節點,下端稱爲子節點。樹像是一個不斷分叉的樹根。
每一個節點能夠有多個子節點(children),而該節點是相應子節點的父節點(parent)。好比說,3,5是6的子節點,6是3,5的父節點;1,8,7是3的子節點, 3是1,8,7的父節點。樹有一個沒有父節點的節點,稱爲根節點(root),如圖中的6。沒有子節點的節點稱爲葉節點(leaf),好比圖中的1,8,9,5節點。從圖中還能夠看到,上面的樹總共有4個層次,6位於第一層,9位於第四層。樹中節點的最大層次被稱爲深度。也就是說,該樹的深度(depth)爲4。
若是咱們從節點3開始向下看,而忽略其它部分。那麼咱們看到的是一個以節點3爲根節點的樹:
三角形表明一棵樹
再進一步,若是咱們定義孤立的一個節點也是一棵樹的話,原來的樹就能夠表示爲根節點和子樹(subtree)的關係:
上述觀察實際上給了咱們一種嚴格的定義樹的方法:
1. 樹是元素的集合。
2. 該集合能夠爲空。這時樹中沒有元素,咱們稱樹爲空樹 (empty tree)。
3. 若是該集合不爲空,那麼該集合有一個根節點,以及0個或者多個子樹。根節點與它的子樹的根節點用一個邊(edge)相連。
上面的第三點是以遞歸的方式來定義樹,也就是在定義樹的過程當中使用了樹自身(子樹)。因爲樹的遞歸特徵,許多樹相關的操做也能夠方便的使用遞歸實現。咱們將在後面看到。
樹的示意圖已經給出了樹的一種內存實現方式: 每一個節點儲存元素和多個指向子節點的指針。然而,子節點數目是不肯定的。一個父節點可能有大量的子節點,而另外一個父節點可能只有一個子節點,而樹的增刪節點操做會讓子節點的數目發生進一步的變化。這種不肯定性就可能帶來大量的內存相關操做,而且容易形成內存的浪費。
一種經典的實現方式以下:
擁有同一父節點的兩個節點互爲兄弟節點(sibling)。上圖的實現方式中,每一個節點包含有一個指針指向第一個子節點,並有另外一個指針指向它的下一個兄弟節點。這樣,咱們就能夠用統一的、肯定的結構來表示每一個節點。
計算機的文件系統是樹的結構,好比Linux文件管理背景知識中所介紹的。在UNIX的文件系統中,每一個文件(文件夾一樣是一種文件),均可以看作是一個節點。非文件夾的文件被儲存在葉節點。文件夾中有指向父節點和子節點的指針(在UNIX中,文件夾還包含一個指向自身的指針,這與咱們上面見到的樹有所區別)。在git中,也有相似的樹狀結構,用以表達整個文件系統的版本變化 (參考版本管理三國志)。
二叉樹是由n(n≥0)個結點組成的有限集合、每一個結點最多有兩個子樹的有序樹。它或者是空集,或者是由一個根和稱爲左、右子樹的兩個不相交的二叉樹組成。
特色:
(1)二叉樹是有序樹,即便只有一個子樹,也必須區分左、右子樹;
(2)二叉樹的每一個結點的度不能大於2,只能取0、一、2三者之一;
(3)二叉樹中全部結點的形態有5種:空結點、無左右子樹的結點、只有左子樹的結點、只有右子樹的結點和具備左右子樹的結點。
二叉樹(binary)是一種特殊的樹。二叉樹的每一個節點最多隻能有2個子節點:
二叉樹
因爲二叉樹的子節點數目肯定,因此能夠直接採用上圖方式在內存中實現。每一個節點有一個左子節點(left children)和右子節點(right children)。左子節點是左子樹的根節點,右子節點是右子樹的根節點。
若是咱們給二叉樹加一個額外的條件,就能夠獲得一種被稱做二叉搜索樹(binary search tree)的特殊二叉樹。二叉搜索樹要求:每一個節點都不比它左子樹的任意元素小,並且不比它的右子樹的任意元素大。
(若是咱們假設樹中沒有重複的元素,那麼上述要求能夠寫成:每一個節點比它左子樹的任意節點大,並且比它右子樹的任意節點小)
二叉搜索樹,注意樹中元素的大小
二叉搜索樹能夠方便的實現搜索算法。在搜索元素x的時候,咱們能夠將x和根節點比較:
1. 若是x等於根節點,那麼找到x,中止搜索 (終止條件)
2. 若是x小於根節點,那麼搜索左子樹
3. 若是x大於根節點,那麼搜索右子樹
二叉搜索樹所須要進行的操做次數最多與樹的深度相等。n個節點的二叉搜索樹的深度最多爲n,最少爲log(n)。
遍歷即將樹的全部結點訪問且僅訪問一次。按照根節點位置的不一樣分爲前序遍歷,中序遍歷,後序遍歷。
前序遍歷:根節點->左子樹->右子樹
中序遍歷:左子樹->根節點->右子樹
後序遍歷:左子樹->右子樹->根節點
例如:求下面樹的三種遍歷
前序遍歷:abdefgc
中序遍歷:debgfac
後序遍歷:edgfbca
如何判斷一棵樹是徹底二叉樹?按照定義,
教材上的說法:一個深度爲k,節點個數爲 2^k - 1 的二叉樹爲滿二叉樹。這個概念很好理解,
就是一棵樹,深度爲k,而且沒有空位。
首先對滿二叉樹按照廣度優先遍歷(從左到右)的順序進行編號。
一顆深度爲k二叉樹,有n個節點,而後,也對這棵樹進行編號,若是全部的編號都和滿二叉樹對應,那麼這棵樹是徹底二叉樹。
class TreeNode(object): def __init__(self, data=0, left=0, right=0): self.data = data self.left = left self.right = right class BTree(object): def __init__(self, root=0): self.root = root def preOrder(self, treenode): if treenode is 0: return print(treenode.data) self.preOrder(treenode.left) self.preOrder(treenode.right) def inOrder(self, treenode): if treenode is 0: return self.inOrder(treenode.left) print(treenode.data) self.inOrder(treenode.right) def postOrder(self, treenode): if treenode is 0: return self.postOrder(treenode.left) self.postOrder(treenode.right) print(treenode.data) if __name__ == '__main__': n1 = TreeNode(data=1) n2 = TreeNode(2, n1, 0) n3 = TreeNode(3) n4 = TreeNode(4) n5 = TreeNode(5, n3, n4) n6 = TreeNode(6, n2, n5) n7 = TreeNode(7, n6, 0) n8 = TreeNode(8) root = TreeNode('root', n7, n8) bt = BTree(root) print("preOrder".center(50, '-')) print(bt.preOrder(bt.root)) print("inOrder".center(50, '-')) print(bt.inOrder(bt.root)) print("postOrder".center(50, '-')) print(bt.postOrder(bt.root))
E:\Evns\jxiou\Scripts\python.exe H:/shipbfq/22.py ---------------------preOrder--------------------- root 7 6 2 1 5 3 4 8 None ---------------------inOrder---------------------- 1 2 6 3 5 4 7 root 8 None --------------------postOrder--------------------- 1 2 3 4 5 6 7 8 root None
堆排序,顧名思義,就是基於堆。所以先來介紹一下堆的概念。
堆分爲最大堆和最小堆,其實就是徹底二叉樹。最大堆要求節點的元素都要大於其孩子,最小堆要求節點元素都小於其左右孩子,二者對左右孩子的大小關係不作任何要求,其實很好理解。有了上面的定義,咱們能夠得知,處於最大堆的根節點的元素必定是這個堆中的最大值。其實咱們的堆排序算法就是抓住了堆的這一特色,每次都取堆頂的元素,將其放在序列最後面,而後將剩餘的元素從新調整爲最大堆,依次類推,最終獲得排序的序列。
堆排序就是把堆頂的最大數取出,
將剩餘的堆繼續調整爲最大堆,具體過程在第二塊有介紹,以遞歸實現
剩餘部分調整爲最大堆後,再次將堆頂的最大數取出,再將剩餘部分調整爲最大堆,這個過程持續到剩餘數只有一個時結束
#_*_coding:utf-8_*_ __author__ = 'Alex Li' import time,random def sift_down(arr, node, end): root = node #print(root,2*root+1,end) while True: # 從root開始對最大堆調整 child = 2 * root +1 #left child if child > end: #print('break',) break print("v:",root,arr[root],child,arr[child]) print(arr) # 找出兩個child中交大的一個 if child + 1 <= end and arr[child] < arr[child + 1]: #若是左邊小於右邊 child += 1 #設置右邊爲大 if arr[root] < arr[child]: # 最大堆小於較大的child, 交換順序 tmp = arr[root] arr[root] = arr[child] arr[child]= tmp # 正在調整的節點設置爲root #print("less1:", arr[root],arr[child],root,child) root = child # #[3, 4, 7, 8, 9, 11, 13, 15, 16, 21, 22, 29] #print("less2:", arr[root],arr[child],root,child) else: # 無需調整的時候, 退出 break #print(arr) print('-------------') def heap_sort(arr): # 從最後一個有子節點的孩子仍是調整最大堆 first = len(arr) // 2 -1 for i in range(first, -1, -1): sift_down(arr, i, len(arr) - 1) #[29, 22, 16, 9, 15, 21, 3, 13, 8, 7, 4, 11] print('--------end---',arr) # 將最大的放到堆的最後一個, 堆-1, 繼續調整排序 for end in range(len(arr) -1, 0, -1): arr[0], arr[end] = arr[end], arr[0] sift_down(arr, 0, end - 1) #print(arr) def main(): # [7, 95, 73, 65, 60, 77, 28, 62, 43] # [3, 1, 4, 9, 6, 7, 5, 8, 2, 10] #l = [3, 1, 4, 9, 6, 7, 5, 8, 2, 10] #l = [16,9,21,13,4,11,3,22,8,7,15,27,0] array = [16,9,21,13,4,11,3,22,8,7,15,29] #array = [] #for i in range(2,5000): # #print(i) # array.append(random.randrange(1,i)) print(array) start_t = time.time() heap_sort(array) end_t = time.time() print("cost:",end_t -start_t) print(array) #print(l) #heap_sort(l) #print(l) if __name__ == "__main__": main()
人類能理解的版本
dataset = [16,9,21,3,13,14,23,6,4,11,3,15,99,8,22] for i in range(len(dataset)-1,0,-1): print("-------",dataset[0:i+1],len(dataset),i) #for index in range(int(len(dataset)/2),0,-1): for index in range(int((i+1)/2),0,-1): print(index) p_index = index l_child_index = p_index *2 - 1 r_child_index = p_index *2 print("l index",l_child_index,'r index',r_child_index) p_node = dataset[p_index-1] left_child = dataset[l_child_index] if p_node < left_child: # switch p_node with left child dataset[p_index - 1], dataset[l_child_index] = left_child, p_node # redefine p_node after the switch ,need call this val below p_node = dataset[p_index - 1] if r_child_index < len(dataset[0:i+1]): #avoid right out of list index range #if r_child_index < len(dataset[0:i]): #avoid right out of list index range #print(left_child) right_child = dataset[r_child_index] print(p_index,p_node,left_child,right_child) # if p_node < left_child: #switch p_node with left child # dataset[p_index - 1] , dataset[l_child_index] = left_child,p_node # # redefine p_node after the switch ,need call this val below # p_node = dataset[p_index - 1] # if p_node < right_child: #swith p_node with right child dataset[p_index - 1] , dataset[r_child_index] = right_child,p_node # redefine p_node after the switch ,need call this val below p_node = dataset[p_index - 1] else: print("p node [%s] has no right child" % p_node) #最後這個列表的第一值就是最大堆的值,把這個最大值放到列表最後一個, 把神剩餘的列表再調整爲最大堆 print("switch i index", i, dataset[0], dataset[i] ) print("before switch",dataset[0:i+1]) dataset[0],dataset[i] = dataset[i],dataset[0] print(dataset)
希爾排序(Shell Sort)是插入排序的一種。也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本,該方法的基本思想是:先將整個待排元素序列分割成若干個子序列(由相隔某個「增量」的元素組成的)分別進行直接插入排序,而後依次縮減增量再進行排序,待整個序列中的元素基本有序(增量足夠小)時,再對全體元素進行一次直接插入排序。由於直接插入排序在元素基本有序的狀況下(接近最好狀況),效率是很高的,所以希爾排序在時間效率比直接插入排序有較大提升
首先要明確一下增量的取法:
第一次增量的取法爲: d=count/2;
第二次增量的取法爲: d=(count/2)/2;
最後一直到: d=1;
看上圖觀測的現象爲:
d=3時:將40跟50比,因50大,不交換。
將20跟30比,因30大,不交換。
將80跟60比,因60小,交換。
d=2時:將40跟60比,不交換,拿60跟30比交換,此時交換後的30又比前面的40小,又要將40和30交換,如上圖。
將20跟50比,不交換,繼續將50跟80比,不交換。
d=1時:這時就是前面講的插入排序了,不過此時的序列已經差很少有序了,因此給插入排序帶來了很大的性能提升。
import time,random #source = [8, 6, 4, 9, 7, 3, 2, -4, 0, -100, 99] #source = [92, 77, 8,67, 6, 84, 55, 85, 43, 67] source = [ random.randrange(10000+i) for i in range(10000)] #print(source) step = int(len(source)/2) #分組步長 t_start = time.time() while step >0: print("---step ---", step) #對分組數據進行插入排序 for index in range(0,len(source)): if index + step < len(source): current_val = source[index] #先記下來每次大循環走到的第幾個元素的值 if current_val > source[index+step]: #switch source[index], source[index+step] = source[index+step], source[index] step = int(step/2) else: #把基本排序好的數據再進行一次插入排序就行了 for index in range(1, len(source)): current_val = source[index] # 先記下來每次大循環走到的第幾個元素的值 position = index while position > 0 and source[ position - 1] > current_val: # 當前元素的左邊的緊靠的元素比它大,要把左邊的元素一個一個的往右移一位,給當前這個值插入到左邊挪一個位置出來 source[position] = source[position - 1] # 把左邊的一個元素往右移一位 position -= 1 # 只一次左移只能把當前元素一個位置 ,還得繼續左移只到此元素放到排序好的列表的適當位置 爲止 source[position] = current_val # 已經找到了左邊排序好的列表裏不小於current_val的元素的位置,把current_val放在這裏 print(source) t_end = time.time() - t_start print("cost:",t_end)