第四百一十五節,python經常使用排序算法學習

第四百一十五節,python經常使用排序算法學習html

 

經常使用排序

 

名稱node

複雜度python

說明git

備註算法

冒泡排序
Bubble Sortshell

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.

 

箱排序
Bin Sort

O(n)

設置若干個箱子,把關鍵字等於 k 的記錄全都裝入到第k 個箱子裏 ( 分配 ) ,而後按序號依次將各非空的箱子首尾鏈接起來 ( 收集 ) 。

分配排序的一種:經過" 分配 " 和 " 收集 " 過程來實現排序。

 

http://www.jxiou.com/
http://www.jxiou.com/lu_yin_wang_zhan.html

 

冒泡排序(Bubble Sort)

冒泡排序(Bubble Sort),是一種 計算機科學領域的較簡單的 排序算法
它重複地走訪過要排序的數列,一次比較兩個元素,若是他們的順序錯誤就把他們交換過來。走訪數列的工做是重複地進行直到沒有再須要交換,也就是說該數列已經排序完成。
這個算法的名字由來是由於越大的元素會經由交換慢慢「浮」到數列的頂端,故名。
 
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)

插入排序(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]

 

 

 

快速排序(quick sort)

設要排序的數組是A[0]……A[N-1],首先任意選取一個數據(一般選用數組的第一個數)做爲關鍵數據,而後將全部比它小的數都放到它前面,全部比它大的數都放到它後面,這個過程稱爲一趟快速排序。值得注意的是,快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變更  

注:在待排序的文件中,若存在多個關鍵字相同的記錄,通過排序後這些具備相同關鍵字的記錄之間的相對次序保持不變,該排序方法是穩定的;若具備相同關鍵字的記錄之間的相對次序發生改變,則稱這種排序方法是不穩定的。
要注意的是,排序算法的穩定性是針對全部輸入實例而言的。即在全部可能的輸入實例中,只要有一個實例使得算法不知足穩定性要求,則該排序算法就是不穩定的。 

排序演示

示例

假設用戶輸入了以下數組:
下標
0
1
2
3
4
5
數據
6
2
7
3
8
9
建立變量i=0(指向第一個數據), j=5(指向最後一個數據), k=6( 賦值爲第一個數據的值)。
咱們要把全部比k小的數移動到k的左面,因此咱們能夠開始尋找比6小的數,從j開始,從右往左找,不斷遞減變量j的值,咱們找到第一個下標3的數據比6小,因而把數據3移到下標0的位置,把下標0的數據6移到下標3,完成第一次比較:
下標
0
1
2
3
4
5
數據
3
2
7
6
8
9
i=0 j=3 k=6
接着,開始第二次比較,此次要變成找比k大的了,並且要從前日後找了。遞加變量i,發現下標2的數據是第一個比k大的,因而用下標2的數據7和j指向的下標3的數據的6作交換,數據狀態變成下表:
下標
0
1
2
3
4
5
數據
3
2
6
7
8
9
i=2 j=3 k=6
稱上面兩次比較爲一個循環。
接着,再遞減變量j,不斷重複進行上面的循環比較。
在本例中,咱們進行一次循環,就發現i和j「碰頭」了:他們都指向了下標2。因而,第一遍比較結束。獲得結果以下,凡是k(=6)左邊的數都比它小,凡是k右邊的數都比它大:
下標
0
1
2
3
4
5
數據
3
2
6
7
8
9
若是i和j沒有碰頭的話,就遞加i找大的,尚未,就再遞減j找小的,如此反覆,不斷循環。注意判斷和尋找是同時進行的。
而後,對k兩邊的數據,再分組分別進行上述的過程,直到不能再分組爲止。
注意:第一遍快速排序不會直接獲得最終結果,只會把比k大和比k小的數分到k的兩邊。爲了獲得最後結果,須要再次對下標2兩邊的數組分別執行此步驟,而後再分解數組,直到數組不能再分解爲止(只有一個數據),才能獲得正確結果。

 

# _*_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

 

 二叉樹的類型

(1) 徹底二叉樹——若設二叉樹的高度爲h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第h層有 葉子結點,而且葉子結點都是從左到右依次排布,這就是 徹底二叉樹
(2) 滿二叉樹——除了葉結點外每個結點都有左右子葉且葉子結點都處在最底層的二叉樹。
(3)平衡二叉樹——平衡二叉樹又被稱爲AVL樹(區別於AVL算法),它是一棵二叉排序樹,且具備如下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹

如何判斷一棵樹是徹底二叉樹?按照定義,

教材上的說法:一個深度爲k,節點個數爲 2^k - 1 的二叉樹爲滿二叉樹。這個概念很好理解,

就是一棵樹,深度爲k,而且沒有空位。

首先對滿二叉樹按照廣度優先遍歷(從左到右)的順序進行編號。

一顆深度爲k二叉樹,有n個節點,而後,也對這棵樹進行編號,若是全部的編號都和滿二叉樹對應,那麼這棵樹是徹底二叉樹。

 

image

 

 

如何判斷平衡二叉樹?

2008111712242127

(b)左邊的圖 左子數的高度爲3,右子樹的高度爲1,相差超過1

(b)右邊的圖 -2的左子樹高度爲0  右子樹的高度爲2,相差超過1

 

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)

希爾排序(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)
相關文章
相關標籤/搜索