Python之常用排序算法(冒泡,選擇,插入,快排,二叉樹生成以及遍歷,堆排,歸排,Python的sort及heaqp,python二分查找,)

目錄篇:python相關目錄篇 點擊跳轉

目錄


排序算法

算法(Algorithm)是指解題方案的準確而完整的描述,是一系列解決問題的清晰指令,算法代表着用系統的方法描述解決問題的策略機制。也就是說,能夠對一定規範的輸入,在有限時間內獲得所要求的輸出。如果一個算法有缺陷,或不適合於某個問題,執行這個算法將不會解決這個問題。不同的算法可能用不同的時間、空間或效率來完成同樣的任務。一個算法的優劣可以用空間複雜度與時間複雜度來衡量。

    一個算法應該具有以下七個重要的特徵:

①有窮性(Finiteness):算法的有窮性是指算法必須能在執行有限個步驟之後終止;
②確切性(Definiteness):算法的每一步驟必須有確切的定義;
③輸入項(Input):一個算法有0個或多個輸入,以刻畫運算對象的初始情況,所謂0個輸     入是指算法本身定出了初始條件;
④輸出項(Output):一個算法有一個或多個輸出,以反映對輸入數據加工後的結果。沒       有輸出的算法是毫無意義的;
⑤可行性(Effectiveness):算法中執行的任何計算步驟都是可以被分解爲基本的可執行       的操作步,即每個計算步都可以在有限時間內完成(也稱之爲有效性);
⑥高效性(High efficiency):執行速度快,佔用資源少;
⑦健壯性(Robustness):對數據響應正確。
 

    算法時間複雜度

看別人寫算法效率高不高(logn最高,O(1)常數不算):
O(1)>O (log2n) > O(N) >= O (nlog2n)>O(n^2)> O(n^2 +logn)>O(n^3)...>O(n^k)

寫算法最少控制在o(n^2) 空間控制在O(1)

    空間複雜度:

你的算法使用的空間需要多少,一般情況就是暫用了臨時的內存,就算佔用也是很少,所以空間複雜度一直是O(1),如果空間複雜度是O(N)等於是複製了一份到內存,根本就沒必要

實戰

    冒泡排序O(n^2)

普通版本

1.列表從第一個元素進行和相鄰的元素進行比大,進行互換(大的往右規則互換),接着大的元素繼續和後面相鄰的元素進行比大,以此類推,最終列表最大的元素放置到了最右邊(該次循環結束)
2.繼續循環(上次循環得到最大元素切片出來(不包含在列表裏面)),然後按照上面的方式繼續把該次列表最大的元素放置在最右邊
3.以上面方式,最終排序完畢(按照上面的情況每次循環只能提取一個最大的元素放置最右邊,那麼外層就應該還有個循環是根據列表元素數量的,這樣循環套循環最終才排序出來)
詳情:看實驗(與選擇排序(普通版)類似)

__author__ = "Burgess Zheng"
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import random,time
def bubble_sort(array):
    for i in range(len(array)):
        #array = [3,2,8,1]
        #len(array) = 4
        #i=0 i=1 i=2
        for j in range(len(array)-1-i,):
            #len(array)-1-i == 4-1-0 = 3
            #j=0 j=1 j=2 j=3
            if array[j] > array[j+1]:  #array[0] > array[1]  ==  3 > 2
                tmp = array[j] #tmp = 3
                array[j] = array[j+1] #array[0] = 2
                array[j+1] = tmp    #array[1]  =3
#第1次大循環的內部循環i=0  range(len(array)-1-i,) == range(3)
#內部循環最終結果 array = [2,3,1,8]  每次內部循環結果會把列表裏面最大元素放到最右邊
#第2次大循環的內部循環i=1  range(len(array)-1-i,) == range(2)
# 內部循環最終結果 array = [2,1,3,8]
#以此類推最終就排序了

if __name__ == '__main__':
    array =[] # [3,2,8,1]
    for i in range(50000):
        array.append(random.randrange(50000))
    #print(array)
    time_start = time.time()
    bubble_sort(array)
    time_end = time.time()

    print(array[0:100])
    print("cost:",time_end-time_start)

優化版本

1.列表從第一個元素進行和相鄰的元素進行比大,大的下標進行標記(此刻不替換)再用標記好的下標元素和其他的元素比大(同理不替換),以此類推找到了列表中最大的元素下標,循環結束,然後把最大元素的下標和該列表最右的元素進行替換位置。
2.繼續循環(上次循環得到最大元素切片出來(不包含在列表裏面)),然後按照上面的方式繼續把該次列表最大的元素放置在最右邊
3.以上面方式,最終排序完畢(按照上面的情況每次循環只能提取一個最大的元素放置最右邊,那麼外層就應該還有個循環是根據列表元素數量的,這樣循環套循環最終才排序出來)
詳情:看實驗(與選擇排序(優化版)相反)

__author__ = "Burgess Zheng"
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import random,time
def bubble_sort(array):
    for i in range(len(array)):
        #array = [3,2,8,1]
        #len(array) = 4
        #i=0 i=1 i=2
        bigess = 0
        for j in range(0,len(array)-i):
            if array[bigess] < array[j]:
                bigess = j
        tmp = array[-1-i]
        array[-1-i] = array[bigess]
        array[bigess] =tmp

if __name__ == '__main__':
    array =[] # [871, 100, 160, 755, 614, 621, 403, 671, 256, 915, 174, 906, 253, 973, 199, 370, 950, 970, 287, 648]
    for i in range(50000):
        array.append(random.randrange(50000))
    print(array)
    time_start = time.time()
    bubble_sort(array)
    time_end = time.time()
    print(array[0:100])
    print("cost:",time_end-time_start)

    選擇排序O(n^2)

普通版本

1.    列表左邊第1個元素進行其他的後面元素進行比小,小的元素替換成爲左邊第1個的元素,繼續和其他的比,以此類推列表左邊第一個元素是最小的,該次循環結束
2.    繼續循環(上次左邊的最小元素被切片了,當做不在列表內),形式如上找到最小的元素放在左邊的第一個位置上
3.    按照以上方式最終完成選中排序
詳情:看實驗(與冒泡排序(普通版)類似)

__author__ = "Burgess Zheng"
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import random,time
def selection_sort(array):
    # array = [3,2,8,1]
    # len(array) = 4
    # i=0 i=1 i=2
    for i in range(len(array)):
        for j  in range(i,len(array)):
            if array[i] > array[j]:
                tmp = array[i]
                array[i] = array[j]
                array[j] = tmp

if __name__ == '__main__':
    array = []
    # array =[871, 100, 160, 755, 614, 621, 403, 671, 256, 915, 174, 906, 253, 973, 199, 370, 950, 970, 287, 648]
    for i in range(50000):
        array.append(random.randrange(100000))
    print(array)
    time_start = time.time()
    selection_sort(array)
    time_end = time.time()
    print(array[:100])
    print("cost:",time_end-time_start)

優化版本

1.列表左邊第1個元素進行其他的後面元素進行比小,小的下標進行標記(此刻不替換)再用標記好的下標元素和其他的元素比小(同理不替換),以此類推找到了列表中最小的元素下標,循環結束,然後把最小元素和該列表最左邊的元素進行替換位置。
2.繼續循環(上次循環得到最小元素切片出來(不包含在列表裏面)),然後按照上面的方式繼續把該次列表最小的元素放置在最左邊
3.以上面方式,最終排序完畢(按照上面的情況每次循環只能提取一個最小的元素放置最左邊,那麼外層就應該還有個循環是根據列表元素數量的,這樣循環套循環最終才排序出來)
詳情:看實驗(與冒泡排序(優化版)類似,規則也差不多)

__author__ = "Burgess Zheng"
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import random,time
def selection_sort(array):
    for i in range(len(array)):
        #array = [3,2,8,1]
        #len(array) = 4
        #i=0 i=1 i=2
        smallest_index = i
        for j  in range(i,len(array)):
            if array[smallest_index] > array[j]:
                smallest_index = j
        tmp = array[i]
        array[i]= array[smallest_index]
        array[smallest_index] = tmp

if __name__ == '__main__':
    array =[] #[871, 100, 160, 755, 614, 621, 403, 671, 256, 915, 174, 906, 253, 973, 199, 370, 950, 970, 287, 648]
    for i in range(50000):
        array.append(random.randrange(100000))
    print(array)
    time_start = time.time()
    selection_sort(array)
    time_end = time.time()
    print(array[:100])
    print("cost:",time_end-time_start)

    插入排序O(n^2)

for循環從下標1元素開始循環,記錄下標1的元素,然後進行while循環,首先下標1跟下標0的元素進行比較,如果比下標0的元素小,那麼久把下標0的元素替換到下標1的位置,下標1剛開始被記錄的就替換到下標0,因爲下標0是邊界,所以排序完成
示例:[3,7,12,22,8]   x=8
while1: [3,7,12,22,22]
while2:[3,7,12,12,22]
while3:[3,7,7 ,12,22]
停止循環
last [3,7, 7-->x=8,12,22]

__author__ = "Burgess Zheng"
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import random,time

def insertion_sort(source):
    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)

if __name__ == '__main__':

    source =[] #[64, 77, 67, 8, 6, 84, 55, 20, 43, 67] #[871, 100, 160, 755, 614, 621, 403, 671, 256, 915, 174, 906, 253, 973, 199, 370, 950, 970, 287, 648]
    #[55,64,64,67,77,84] 58
    for i in range(50000):
        source.append(random.randrange(100000))
    # print('list generate done ')
    time_start = time.time()
    insertion_sort(source)
    time_end = time.time()
    print(source[:100])
    print("cost:",time_end-time_start)


    快排排序O(nlog2n) == O(nlogn)

__author__ = "Burgess Zheng"
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import random,time
def quick_sort(array,start,end):
    #print("-->",start,end)
    if start >=end:
        return
    k = array[start]    #關鍵K默認下標0的元素
    left_flag = start  #關鍵下標默認0
    right_flag = end  #關鍵下標默認最後-1
    while left_flag < right_flag:
        while left_flag < right_flag and array[right_flag] > k: # 代表要繼續往左邊移動小旗子
            right_flag -=1
        tmp = array[left_flag]
        array[left_flag] = array[right_flag]
        array[right_flag] = tmp

        #左邊小旗子開始向右移動
        while left_flag < right_flag and  array[left_flag] <= k :
            left_flag +=1
        #上面的loop一跳出,代表左邊小旗子 現在所處的位置的值是比k大的,
        tmp = array[left_flag]
        array[left_flag] = array[right_flag]
        array[right_flag] = tmp
        #print(array,left_flag,right_flag)

    #開始把問題分半(進行遞歸)
    quick_sort(array,start,left_flag-1)
    quick_sort(array,left_flag+1,end)

if __name__ == '__main__':
    # array =[64,77,67,8,6,84,55,20,43,67]
    array=[]
    for i in range(50000):
        array.append(random.randrange(100000))
    print('list generate done ')
    print(array)
    time_start = time.time()
    quick_sort(array,0,len(array)-1)
    time_end = time.time()
    print("---->",array[:100])
    print("cost:",time_end- time_start)



    二叉樹生成以及遍歷O(NLOG2N) == O(N*LOGN)

 

每個節點都是實例化的對象
前序遍歷:根節點->左子樹->右子樹 root-n7-n6-n2-n1-n5-n3-n4-n8
中序遍歷:左子樹->根節點->右子樹 n1-n2-n6-n3-n5-n4-n7-root-n8
後序遍歷:左子樹->右子樹->根節點 n1-n2-n3-n4-n5-n6-n7-n8-root

__author__ = "Burgess Zheng"
#!/usr/bin/env python
#-*- coding:utf-8 -*-

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):
        '''
        前序遍歷:根節點->左子樹->右子樹 root-n7-n6-n2-n1-n5-n3-n4-n8
        :param treenode:
        :return:
        '''
        if treenode is 0:
            return
        print(treenode.data)
        self.preOrder(treenode.left)
        self.preOrder(treenode.right)
    def inOrder(self,treenode):
        '''
        中序遍歷:左子樹->根節點->右子樹 n1-n2-n6-n3-n5-n4-n7-root-n8
        :param treenode:
        :return:
        '''
        if treenode is 0:
            return
        self.inOrder(treenode.left)
        print(treenode.data)
        self.inOrder(treenode.right)

    def postOrder(self,treenode):
        '''
        後序遍歷:左子樹->右子樹->根節點 n1-n2-n3-n4-n5-n6-n7-n8-root
        :param treenode:
        :return:
        '''
        if treenode is 0:
            return
        self.postOrder(treenode.left)
        self.postOrder(treenode.right)
        print(treenode.data)

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))



執行結果

    堆排序O(nlog2n) == O(nlogn)

__author__ = "Burgess Zheng"
#!/usr/bin/env python
#-*- coding:utf-8 -*-

def sift_down(arr, node, end):
    # heap_cycle_one: arr=[2,8,4,7,10,12,6]  node=2  end=6
    # heap_cycle_two: arr=[2,8,12,7,10,4,6]  node=1  end=6
    # heap_cycle_three: arr=[2,10,12,7,8,4,6]  node=0 end=6

    # last_cycle_one: arr=[2,10,6,7,8,4,12]  node=0  end=5

    root = node#子樹節點
    # heap_cycle_one: root=2
    # heap_cycle_two: root=1
    # heap_cycle_three: root=0

    # last_cycle_one:root=0

    while True:
        child = 2 * root + 1  #子樹的left子節點
            # heap_cycle_one(while_one):child = 5
            # heap_cycle_one(while_two): root = 5  child = 11  arr=[2,8,12,7,10,4,6]
            # heap_cycle_two(while_one):child = 3
            # heap_cycle_two(while_two): root = 4  child = 9  arr=[2,10,12,7,8,4,6]
            # heap_cycle_three(while_one):child = 1
            # heap_cycle_three(while_two): root=2  child = 5  arr=[12,10,2,7,8,4,6]
            # heap_cycle_three(while_three): root=6  child = 13  arr=[2,10,6,7,8,4,12]

            # last_cycle_one(while_one):child=1
            # last_cycle_one(while_two):root = 1  child=3   arr=[10,2,6,7,8,4,12]
            # last_cycle_one(while_three):root = 3  child=7   arr=[10,7,6,2,8,4,12]
        if child > end:
             # heap_cycle_one(while_one): 5>6 不成立
             # heap_cycle_one(while_two): 11>6 成立 結束該次循環 得到 arr=[2,8,12,7,10,4,6]
             # heap_cycle_two(while_one):3>6不成立
             # heap_cycle_two(while_two): 9>6 成立 結束該次循環 得到 arr=[2,10,12,7,8,4,6]
             # heap_cycle_three(while_one):1>6不成立
             # heap_cycle_three(while_two): 5>6 不成立
             # heap_cycle_three(while_two): 13>6 成立 結束該次循環 得到 arr=[12,10,6,7,8,4,2]

             # last_cycle_one(while_one):1>5 不成立
             # last_cycle_one(while_two):3>5 不成立
             # last_cycle_one(while_three):7>5 成立 結束該次循環 得到   arr=[10,7,6,2,8,4,12]
             break
        if child + 1 <= end and arr[child] < arr[child + 1]:
            # heap_cycle_one(while_one): 6>=6 and 12<6 不成立
            # heap_cycle_two(while_one): 4>=6 and 7<10 成立 child=3+1
            # heap_cycle_three(while_one): 2>=6 and 10<12 成立 child=1+1
            # heap_cycle_three(while_two): 6>=6 and 4<6 成立 child=5+1

            # last_cycle_one(while_one):2>=5 and 10<6 不成立
            # last_cycle_one(while_two):4>=5 and 7<8 不成立
            child += 1

        if arr[root] < arr[child]:
            # heap_cycle_one(while_one): 4 < 12 成立
            # heap_cycle_two(while_one): 8 < 10 成立
            # heap_cycle_three(while_one): 2 < 12 成立
            # heap_cycle_three(while_two): 2 < 6 成立

            # last_cycle_one(while_one):2<10 成立
            # last_cycle_one(while_one):2<7 成立
            tmp = arr[root]
            arr[root] = arr[child]
            arr[child] = tmp

            # heap_cycle_one(while_one): 下標2的元素和下標5元素替換原 [2,8,4,7,10,12,6] ==> [2,8,12,7,10,4,6]
            # heap_cycle_two(while_one): 下標1的元素和下標4元素替換原 [2,8,12,7,10,4,6]==> [2,10,12,7,8,4,6]
            # heap_cycle_three(while_one): 下標0的元素和下標2元素替換原 [2,10,12,7,8,4,6]==> [12,10,2,7,8,4,6]
            # heap_cycle_three(while_two): 下標2的元素和下標6元素替換原 [12,10,2,7,8,4,6]==> [12,10,6,7,8,4,2]

            # last_cycle_one(while_one):下表0的元素和下標1的元素替換原 [2,10,6,7,8,4,12] ==> [10,2,6,7,8,4,12]
            # last_cycle_one(while_two):下表1的元素和下標3的元素替換原 [10,2,6,7,8,4,12] ==> [10,7,6,2,8,4,12]
            root = child
            # heap_cycle_one(while_one): root = 5   [2,8,12,7,10,4,6]
            # heap_cycle_two(while_one): root = 4  [2,10,12,7,8,4,6]
            # heap_cycle_two(while_one): root = 2  [12,10,2,7,8,4,6]
            # heap_cycle_two(while_one): root = 6  [2,10,6,7,8,4,12]

            # last_cycle_one(while_one):root = 1  [10,2,6,7,8,4,12]
            # last_cycle_one(while_two):root = 3 [10,7,6,2,8,4,12]
        else:
            # 無需調整的時候, 退出
            break

def heap_sort(arr):
    '''
    完全二叉樹的規則:
        1最頂的稱:根節點
        2有子節點的稱:子樹根節點、也可以是子節點
        3沒有子節點的稱:葉節點
        4非葉節點:根節點、子樹根節點
        5根節點和子樹根節點擁有不可超過2個子節點,且有順序,左子節點到右子節點
    堆原理:
        1.遵守完全二叉樹規則
        2.根節點或子樹根節點的值 大於或者等於 其左右孩子節點的值  稱大頂堆
        3.根節點或子樹根節點的值 小於或者等於 其左右孩子節點的值  稱小頂堆
        4.根據子樹根節點的下標可以找到該節點的左右子節點
        子樹根節點 = i
        父節點 = i//2 -1
        左子節點 = i*2 + 1
        右子節點 = i*2+2

    堆排序實現邏輯
      一.把一個無序的列表變成大頂堆或者小頂堆
        一個列表 arr= [2,8,4,7,10,12,6] 是無序的,
        所以我們需要該列表的非葉節點:len(arr)//2-1 得到的非葉節點
        #非葉子節點 == 根節點 或 子樹根節點
        非葉節點的作用:通過非葉節點的下標和該下標的前面下標就可以調用整個列表的元素
        把列表[2,8,4,7,10,12,6]繪畫完全二叉樹:圖如下:
        ###############################################
                         2[下標0]
                8[下標1]             4[下標2]
          7[下標3]   10[下標4]  12[下標5]  6[下標6]
        ###############################################
        非葉節點 = 2   那麼我們就可以通過下標2 1 0的非葉節點可得到整個列表的元素
            非葉節點 = 2
            2*2+1==5:非葉節點的left子節點
            2*2+2==6:非葉節點的right子節點
            非葉節點 = 1
            1*2+1==3:非葉節點的left子節點
            1*2+2==4:非葉節點的right子節點
            非葉節點 = 0
            0*2+1==1:非葉節點的left子節點
            0*2+2==2:非葉節點的right子節點
        通過以上把所有的非葉節點和自身的子節點對比以後最大值作爲非葉子節點(根或子樹根節點)
        意味着我們需要從最後的非葉子節點(下標2)進行讓其和自身的子節點進行對比,最大值替換到非葉子節點(下標2)
        然後往上一層非葉子節點(下標1)進行讓其和自身的子節點進行對比,最大值替換到非葉子節點(下標1)
        接着往上一層非葉子節點(下標0)進行讓其和自身的子節點進行對比,最大值替換到非葉子節點(下標0)
        這裏我們又需要記住,下標0和其子節點也就是下標1和2進行對比,最大值推到下標0位置
        如果下標0的值非常小,和其中的一個子節點最大的值替換了,該子節點如果是子樹根節點的話
        也必須繼續和其子節點對比拿到最大值,以此類推,因爲堆原理是所有的非葉節點大於自身的子節點
        這樣纔可以形成堆所有的根節點和子樹根節點比自身的子節點大
        每次循環必定把一個非葉節點排序完畢,那麼意味着有多少個非葉子節點就循環多少次操作(如上循環3次)
        執行邏輯:
            arr= [2,8,4,7,10,12,6]
            ###############################################
                              2[下標0]
                    8[下標1]             4[下標2]
              7[下標3]   10[下標4]  12[下標5]  6[下標6]
            ###############################################
            最後一個非葉節點=len(arr) // 2 – 1
            非葉節點分別是:2 1 0
            1.從最後一個非葉節點開始
              root(非葉節點)=2 arr= [2,8,4,7,10,12,6] list_len=len(arr)-1==6
            2.找非葉節點(子樹根節點)的左兒子
              left = root* 2 +1
              left==5
              left大於list_len 證明沒該左兒子
            3.找出非葉節點(子樹根節點)的右兒子:
              right=root* 2 +2
              right = 6
              right大於len(arr) 證明沒該右兒子
            4.子節點之間的比較(arr (left) 和arr (right)進行比大)
              arr (left) = arr(5)=12  <  arr (right)=arr(6)=6
            5.非葉節點(子樹根節點)root和right子節點的進行比較:
              arr (root) =arr(2)= 4 <  arr (right)=arr(5) =12
            6.進行替換最大值替換到root節點(下標5替換到下標2)
              arr= [2,8,12,7,10,4,6]
              #############################################
                                2[下標0]
                      8[下標1]            12[下標2]
               7[下標3]   10[下標4]  4[下標5]  6[下標6]
              #############################################
              這樣最後的非葉節點的值大於其子節點
              被替換的子節點(下標5)也需要進行判斷是否是子樹根節點
              如果是的話也需要進行和自身的子節點進行對比替換 ,以此類推
            7.倒數第二個非葉節點(下標1)按照上面步驟1-6相同操作:也就是和自身子節點進行比較替換
              進行替換最大值替換到root節點(下標4替換到下標1)
              arr= [2,10,12,7,8,4,6]
              #############################################
                               2[下標0]
                     10[下標1]          12[下標2]
               7[下標3]   8[下標4]  4[下標5]  6[下標6]
              #############################################
              被替換的子節點(下標4)也需要進行判斷是否是子樹根節點
              如果是的話也需要進行和自身的子節點進行對比替換,以此類推
            8.假如中間有多個非葉節點,按照上面的操作執行....
            9.第一非葉子節點(下標0)按照步驟1-7相同操作:也就是和自身子節點進行比較替換
              進行替換最大值替換到root節點(下標2替換到下標0)
              arr= [12,10,2,7,8,4,6]
              #############################################
                            12[下標0]
                   10[下標1]           2[下標2]
               7[下標3]   8[下標4]  4[下標5]  6[下標6]
              #############################################
              被替換的子節點(下標2)也需要進行判斷是否是子樹根節點
              如果是的話也需要進行和自身的子節點進行對比替換 (下標6替換到下標2)
              arr= [12,10,6,7,8,4,2]
              #############################################
                            12[下標0]
                    10[下標1]          6[下標2]
               7[下標3]   8[下標4]  4[下標5]  2[下標6]
              #############################################
              被替換的子節點(下標6)也需要進行判斷是否是子樹根節點
              如果是的話也需要進行和自身的子節點進行對比替換,以此類推

           10 最終得到了大頂堆:
              arr= [12,10,6,7,8,4,2]
              #############################################

                             12[下標0]
                    10[下標1]         6[下標2]
               7[下標3]   8[下標4]  4[下標5]  2[下標6]
              #############################################

      二.獲得了大頂堆意味根節點和子樹根節點絕對比自身的子節點大:如下
        #############################################
                      12[下標0]
              10[下標1]         6[下標2]
         7[下標3]   8[下標4]  4[下標5]  2[下標6]
        #############################################
        把頂點(下標0)替換到下標最後:[2,10,6,7,8,4,12],這樣最大的值排到最後面
        進行切片,把最大值切掉得到新的列表:arr= [2,10,6,7,8,4],根節點值變成了未知大小
        #############################################
                        2[下標0]
                10[下標1]         6[下標2]
          7[下標3]   8[下標4]  4[下標5]
        #############################################
        由於我們都知道切片後大頂堆就失效的了意味根節點變成最後下標的值,該值我們未知大小
        但是除了根節點(下標0)的值大小我們不清楚以外,子樹根節點是一定比自身的子節點大
        意味根節點的子節點也就是下標1或者2其中一個肯定是列表最大值
        那麼我們這次可以從根節點(下標0)順序讓其和自己的子節點(下標1和2進行對比替換最大值)
        未知值替換到了下標1或2其中之一,假如下標1被替換了,
        那麼下標1該子節點如果是子樹根節點,也需要和自身的子節點進行對比替換,
        不然的話如果該子樹根節點如果小於其子節點那麼就形參不了大頂堆
        而下標2由於沒替換,那麼自然比其子節點大所以我們可以不用任何操作
        執行邏輯:
            1.大頂堆的根值和列表最後元素替換後切片得到:
              arr= [2,10,6,7,8,4]
              #############################################
                              2[下標0]
                     10[下標1]          6[下標2]
                7[下標3]   8[下標4]  4[下標5]
              #############################################
            2.雖然根節點值不清楚,但是子樹根節點肯定比自身的子節點大,也就是下標1和標2的值肯定比他們子節節點大
              我們可以開始進行從下標0的根節點進行和其子節點(下標1 2) 進行對比,繼續形成新的大頂堆
            3.從根節點開始
              root(非葉節點)=0 arr=[2,10,6,7,8,4] list_len=len(arr)-1==5
            4.找非葉節點(根節點)的左兒子
              left = root* 2 +1
              left==1
              left大於list_len 證明沒該左兒子
            5.找出非葉節點(子樹根節點)的右兒子:
              right=root* 2 +2
              right = 2
              right大於len(arr) 證明沒該右兒子
            6.子節點之間的比較(arr (left) 和arr (right)進行比大)
              arr (left) = arr(1)=10  <  arr (right)=arr(2)=6
            7.非葉節點(子樹根節點)root和left子節點的進行比較:
              arr (root) =arr(0)= 2 <  arr (left)=arr(1) =10
            8.進行替換最大值替換到root節點(下標1替換到下標0)
              arr=[10,2,6,7,8,4]
              #############################################
                              10[下標0]
                     2[下標1]           6[下標2]
               7[下標3]   8[下標4]  4[下標5]
              #############################################
              被替換的子節點(下標1)也需要進行判斷是否是子樹根節點
              如果是的話也需要進行和自身的子節點進行對比替換 (下標4替換下標1)
              arr=[10,8,6,7,2,4]
              #############################################
                            10[下標0]
                    8[下標1]           6[下標2]
               7[下標3]   2[下標4]  4[下標5]
              #############################################
              被替換的子節點(下標1)也需要進行判斷是否是子樹根節點
              如果是的話也需要進行和自身的子節點進行對比替換 ,以此類推
              沒被替換的子節點如果是子樹根節點那麼按照第一次大頂堆他肯定比自身子節點大無需操作
            9.此次循環得到大頂堆以後,按照規矩繼續和最後一個下標替換然後切片按照1-8步驟重複操作
            也就是每次循環可得到一個大頂堆(也就是切片後的列表裏最大的值)循環次數大頂堆列表切片後的長度
      按上面2個大步驟操作最終排序完畢:[2,4,6,7,8,10,12]
    '''

    ##開始根據原理代碼實現 arr=[2,8,4,7,10,12,6]

    ##第一步驟獲取大頂堆

    ##找到最後的非葉節點
    first = len(arr) // 2 -1
        # arr=[2,8,4,7,10,12,6]  len(arr)=7   7//2-1
        # first=2

    ##最後的非葉節點的數量(其下標到下標0)作爲循環的次數
    for i in range(first, -1, -1):
        # range(2, -1, -1) = [2,1,0]
        # heap_cycle_one:i=2
        # heap_cycle_two:i=1
        # heap_cycle_three:i=0

        ##順序從最後一個非葉節點到根節點開始進行和自身的子節點對比替換最大值
        ##記住非葉節點和其子節點替換以後,替換的子節點如果也是子樹根節點,
        ##那麼也需要和自己的子節點對比替換最大值,以此類推得到大頂堆
        sift_down(arr, i, len(arr) - 1)
        # heap_cycle_one: arr=[2,8,4,7,10,12,6]  i=2  7-1=6   得到結果:arr=[2,8,12,7,10,4,6]
        # heap_cycle_two: arr=[2,8,12,7,10,4,6]  i=1  7-1=6   得到結果:arr=[2,10,12,7,8,4,6]
        # heap_cycle_three: arr=[2,10,12,7,8,4,6]  i=0 7-1=6   得到結果:arr=[12,10,6,7,8,4,2]
        ############上面得到了大頂堆 arr = [12,10,6,7,8,4,2]##################
            ####                  12[下標0]                     ####
            ####         10[下標1]             6[下標2]         ####
            ####  7[下標3]   8[下標4]  4[下標5]  2[下標6]    ####
            #大頂堆:所以樹根節點比子節點大



    ##以列表的長度進行循環-1次數進行循環(-1是因爲大頂堆最大值替換到最後下標並且切片少了一個元素)
    for end in range(len(arr) -1, 0, -1):
        # arr=[12,10,6,7,8,4,2] len(arr)=7
        # range(7-1, 0, -1)  == [6,5,4,3,2,1]
        # last_cycle_one: end = 6
        # last_cycle_two: end = 5
        # last_cycle_three: end = 4
        # last_cycle_four: end = 3
        # last_cycle_five: end = 2
        # last_cycle_six: end = 1
        arr[0], arr[end] = arr[end], arr[0]
        #進行替換把最大值替換到最後的下標
        #last_cycle_one:原arr=[12,10,6,7,8,4,2] ==> arr=[2,10,6,7,8,4,12]

        ##由於我們都知道切換後大頂堆就失效的了意味根節點變成最後下標的值,該值我們未知大小
        ##但是除了根節點(下標0)的值大小我們不清楚以外,子樹根節點是一定比自身的子節點大
        ##意味根節點的子節點也就是下標1或者2其中一個肯定是列表最大值
        ##那麼我們這次可以從根節點(下標0)順序讓其和自己的子節點(下標1和2進行對比替換最大值)
        ##未知值替換到了下標1或2其中之一,假如下標1被替換了,
        ##那麼下標1該子節點如果是子樹根節點,也需要和自身的子節點進行對比替換,
        ##不然的話如果該子樹根節點如果小於其子節點那麼就形參不了大頂堆
        ##而下標2由於沒替換,那麼自然比其子節點大所以我們可以不用任何操作
        sift_down(arr, 0, end - 1)
        # last_cycle_one: arr=[2,10,6,7,8,4,12]  arg[1]=0  arg[2]= 6-1 得到結果:arr=[10,7,6,2,8,4,12]
        #last_cycle_one: arr=[4,7,6,2,8,10,12]  arg[1]=0  arg[2]= 5-1
        #.....
        #最終得到結果:arr=[2,8,4,7,10,12,6]




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,day27,0]
    # array = [16, 9, 21, 13, 4, 11, 3, 22, 8, 7, 15, 29]
    array =[] # [871, 100, 160, 755, 614, 621, 403, 671, 256, 915, 174, 906, 253, 973, 199, 370, 950, 970, 287, 648]
    for i in range(50000):
        array.append(random.randrange(50000))
    # 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()


    歸併排序O(nlog2n) == O(nlogn)

歸併排序:
    先分開再合併,分開成單個元素,合併的時候按照正確順序合併
    假如我們有一個n個數的數列,下標從0到n-1
  首先是分開的過程
    1 我們按照 n//2 把這個數列分成兩個小的數列
    2 把兩個小數列 再按照新長度的一半 把每個小數列都分成兩個更小的
    。。。一直這樣重複,一直到每一個數分開了
    比如:    6 5 4 3 2 1
        第一次 n=6 n//2=3 分成      6 5 4      3 2 1
        第二次 n=3 n//2=1 分成    6   5 4    3   2 1
        第三次 n=1的部分不分了
                n=2 n//2=1 分成     5   4      2  1
               
    之後是合併排序的過程:
    3 分開之後我們按照最後分開的兩個數比較大小形成正確順序後組合綁定
        剛剛舉得例子 最後一行最後分開的數排序後綁定   變成     4 5     1 2
        排序後倒數第二行相當於把最新分開的數排序之後變成    6  4 5  3  12
    4 對每組數據按照上次分開的結果,進行排序後綁定
        6 和 4 5(兩個數綁定了)  進行排序
        3 和 1 2(兩個數綁定了)  進行排序
        排完後 上述例子第一行待排序的  4 5 6      1 2 3  兩組數據
    5 對上次分開的兩組進行排序
        拿着 4 5 6     1 2 3兩個數組,進行排序,每次拿出每個數列中第一個(最小的數)比較,把較小的數放入結果數組。再進行下一次排序。
        每個數組拿出第一個數,小的那個拿出來放在第一位 1 拿出來了,   變成4 5 6    2 3
        每個數組拿出第一個書比較小的那個放在下一個位置  1 2被拿出來,  待排序 4 5 6      3
        每個數組拿出第一個書比較小的那個放在下一個位置  1 2 3 被拿出來,  待排序 4 5 6
        如果一個數組空了,說明另一個數組一定比排好序的數組最後一個大 追加就可以結果 1 2 3 4 5 6
    相當於我們每次拿到兩個有序的列表進行合併,分別從兩個列表第一個元素比較,把小的拿出來,在拿新的第一個元素比較,把小的拿出來
        這樣一直到兩個列表空了 就按順序合併了兩個列表

時間複雜度: 最好最壞都是 O( n log n )
穩定性:穩定
缺點:每次拆分數組都要開心的數組, 每次合併數組都要開新數組,空間複雜度很大

__author__ = "Burgess Zheng"
#!/usr/bin/env python 
#-*- coding:utf-8 -*-
import time
import random


def cal_time(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        result = func(*args, **kwargs)
        t2 = time.time()
        print("%s running time: %s secs." % (func.__name__, t2 - t1))
        return result
    return wrapper



def merge(li, low, mid, high):
    """
    merge3:【6】
    :param li: [3,2,5,1,6]
    :param low:  0
    :param mid: 0
    :param high: 1
    #該次從列表中獲取了列表的下標low和high的下標元素進行了比較,小追先追加到tml臨時列表
    #li[low] = 1    li[high] = 4   追加到 tml列表   tml=[1,4]
    #把源列表的相同位置分片以後進行了替換
    li = [1,4,5,6,2][0:2] = [1,4]   = tml  tml複製到li
    li = [1,4,5,6,2]

    merge2
    :param li: [2,3,5,1,6]
    :param low:0
    :param mid:1
    :param high:2

    merge7
    :param li: [2,3,5,1,6]
    :param low:3
    :param mid:3
    :param high:4
    :return:

    merge1
    :param li: [2,3,5,1,6]
    :param low:0
    :param mid:2
    :param high:4
    :return:

    """


    i = low
    #merge3: i = 0
    #merge2:i=0
    #merge7:i=3
    #merge1:i=0
    j = mid + 1
    #merge3: j = 1
    #merge2: j = 2
    #merge7:j=4
    #merge1:j=3
    ltmp = []
    #merge3:  ltmp =[]
    #merge2:  ltmp =[]
    #merge7: ltmp = []
    #merge1: ltmp = []


    while i <= mid and j <= high:
        #merge3_one:li: [3,2,5,1,6] i=0 j=1 mid=0 hight=1 ltmp=[]    0<=0 and  1<=1 成立
        #merge3_two:[3,2,5,1,6] i=0 j=1+1 mid=0 hight=1 ltmp=[2]     0<=0 and  2<=1 不成立 跳出循環

        #merge2_one: li: [2,3,5,1,6] i=0 j=2 mid=1 hight=2 ltmp=[] 0<=1 and 2<=2 成立
        # merge2_two: li: [2,3,5,1,6] i=0+1 j=2 mid=1 hight=2 ltmp=[2]  1<=1 and 2<=2 成立
        # merge2_three: li: [2,3,5,1,6] i=0+2 j=2 mid=1 hight=2 ltmp=[2,3] 2<=1 and 2<=2 不成立

        #merge7_one: li=[2,3,5,1,6] i=3 j=4 mid=3 high=4 ltmp=[]   3<=3 and 4<=4 成立
        #merge7_two: li=[2,3,5,1,6] i=3+1 j=4 mid=3 high=4 ltmp=[1]  4<=3 不成立

        # merge1_one: li=[2,3,5,1,6] i=0 j=3 mid=2 high=4 ltmp=[]  0<=2 and 3<=4 成立
        # merge1_two: li=[2,3,5,1,6] i=0 j=3+1 mid=2 high=4 ltmp=[1]  0<=2 and 4<=4 成立
        # merge1_three: li=[2,3,5,1,6] i=+1 j=3+1 mid=2 high=4 ltmp=[1,2] 1<=2 and 4<=4 成立
        # merge1_four: li=[2,3,5,1,6] i=0+1+1 j=3+1 mid=2 high=4 ltmp=[1,2,3] 2<=2 and 4<=4 成立
        # merge1_five: li=[2,3,5,1,6] i=0+1+1+1 j=3+1 mid=2 high=4 ltmp=[1,2,3,5] 3<=2 and 4<=4 不成立

        if li[i] < li[j]:
        # merge3_one:li: [3,2,5,1,6] i=0 j=1 hight=1 ltmp=[]  3<2 不成立

        # merge2_one: li: [2,3,5,1,6] i=0 j=2 mid=1 hight=2 ltmp=[] 2<5成立
        # merge2_two: li: [2,3,5,1,6] i=1 j=2 mid=1 hight=2 ltmp=[5]   3<5 成立

        #merge7_one: li=[2,3,5,1,6] i=3 j=4 mid=3 high=4 ltmp=[]   1<6 成立

        # merge1_one: li=[2,3,5,1,6] i=0 j=3 mid=2 high=4 ltmp=[] 2<1 不成立
        # merge1_two: li=[2,3,5,1,6] i=0 j=3+1 mid=2 high=4 ltmp=[1] 2<6 成立
        # merge1_three: li=[2,3,5,1,6] i=+1 j=3+1 mid=2 high=4 ltmp=[1,2] 3<6成立
        # merge1_four: li=[2,3,5,1,6] i=0+1+1 j=3+1 mid=2 high=4 ltmp=[1,2,3] 5<6 成立
            ltmp.append(li[i])
            i += 1
        # merge2_one: li: [2,3,5,1,6] i=0+1 j=2 mid=1 hight=2 ltmp=[2]
        # merge2_two: li: [2,3,5,1,6] i=1+1 j=2 mid=1 hight=2 ltmp=[2,3] 成立

        # #merge7_one: li=[2,3,5,1,6] i=3+1 j=4 mid=3 high=4 ltmp=[1]
        # merge1_two: li=[2,3,5,1,6] i=+1 j=3+1 mid=2 high=4 ltmp=[1,2]
        # merge1_three: li=[2,3,5,1,6] i=0+1+1 j=3+1 mid=2 high=4 ltmp=[1,2,3]
        # merge1_four: li=[2,3,5,1,6] i=0+1+1+1 j=3+1 mid=2 high=4 ltmp=[1,2,3,5]
        else:
            ltmp.append(li[j])
            j += 1
            # merge3_one: li: [3,2,5,1,6] i=0 j=1+1 hight=1 ltmp=[2]  繼續while循環

            # merge1_one: li=[2,3,5,1,6] i=0 j=3+1 mid=2 high=4 ltmp=[1] 繼續while循環

    while i <= mid:
        # merge3_two:[3,2,5,1,6] i=0 j=2 mid=0 hight=1 ltmp=[2]   0<=0 成立
        # merge3_three:[3,2,5,1,6] i=1 j=2 mid=0 hight=1 ltmp=[2,3]  1<=0 不成立

        # merge2_three: li: [2,3,5,1,6] i=2 j=2 mid=1 hight=2 ltmp=[2,3] 2<=1 不成立

        # merge7_two: li=[2,3,5,1,6] i=3+1 j=4 mid=3 high=4 ltmp=[1]  4<=3 不成立

        # merge1_five: li=[2,3,5,1,6] i=0+1+1+1 j=3+1 mid=2 high=4 ltmp=[1,2,3,5] 3<=2 不成立
        ltmp.append(li[i])
        i += 1
        # merge3_two:[3,2,5,1,6] i=0+1 j=2 ltmp=[2,3]
    while j <= high:
        # merge3_three:[3,2,5,1,6] i=1 j=2 hight=1 ltmp=[2,3]  2 <= 1 不成立

        # merge2_three: li: [2,3,5,1,6] i=2 j=2 mid=1 hight=2 ltmp=[2,3] 2<=2 成立
        # merge2_four: li: [2,3,5,1,6] i=2 j=3 mid=1 hight=2 ltmp=[2,3,5]  3<=2 不成立

        # merge7_two: li=[2,3,5,1,6] i=3+1 j=4 mid=3 high=4 ltmp=[1]  4<=4 成立
        # merge7_three: li=[2,3,5,1,6] i=3+1 j=4+1 mid=3 high=4 ltmp=[1,6]  5<=4 不成立

        # merge1_five: li=[2,3,5,1,6] i=0+1+1+1 j=3+1 mid=2 high=4 ltmp=[1,2,3,5] 4<=4 成立
        # merge1_six: li=[2,3,5,1,6] i=0+1+1+1 j=3+1+1 mid=2 high=4 ltmp=[1,2,3,5,6] 5<=4 不成立
        ltmp.append(li[j])
        j += 1
        # merge2_three: li: [2,3,5,1,6] i=2 j=2+1 mid=1 hight=2 ltmp=[2,3,5]

        # merge7_two: li=[2,3,5,1,6] i=3+1 j=4+1 mid=3 high=4 ltmp=[1,6]  4<=4 成立

        #merge1_five: li=[2,3,5,1,6] i=0+1+1+1 j=3+1+1 mid=2 high=4 ltmp=[1,2,3,5,6]

    li[low:high+1] = ltmp
    # merge3_three:[3,2,5,1,6] i=1 j=2 mid=0 hight=1 ltmp=[2,3]
    #li[low:high+1] == li[0:2] == [3,2]  進行替換 ltmp
    # li[low:high+1] = ltmp   ==   [3,2] = [2,3]
    #merge3:li=[2,3,5,1,6]

    #merge2_four: li: [2,3,5,1,6] i=2 j=3 mid=1 hight=2 ltmp=[2,3,5]
    #li[low:high+1] == li[0:3] == [2,3,5] 進行替換 ltmp
    # li[low:high+1] = ltmp   ==   [2,3,5] = [2,3,5]
    # merge3:li=[2,3,5,1,6]

    # merge7_three: li=[2,3,5,1,6] i=3+1 j=4+1 mid=3 high=4 ltmp=[1,6]
    # li[low:high+1] == li[3:5] == [1,6] 進行替換 ltmp
    # li[low:high+1] = ltmp   ==   [1,6]= [1,6]
    # merge3:li=[2,3,5,1,6]


    # merge1_six li=[2,3,5,1,6] i=0+1+1+1 j=3+1+1 mid=2 high=4 ltmp=[1,2,3,5,6]
    # li[low:high+1] == li[0:4] == [2,3,5,1,6] 進行替換 ltmp
    # li[low:high+1] = ltmp   ==   [2,3,5,1,6] = [1,2,3,5,6]
    # merge3:li=[1,2,3,5,6]


def _mergesort(li, low, high):
    '''
    one:  【1】
    :param li: [3,2,5,1,6]
    :param low: 0
    :param high: 4

    two: 【2】
    :param li: [3,2,5,1,6]
    :param low: 0
    :param high:2

    three:  【3】
    :param li: [3,2,5,1,6]
    :param low: 0
    :param high:1

    four:  【4】
    :param li:[3,2,5,1,6]
    :param low:0
    :param high:0

     five:  【5】
    :param li:[3,2,5,1,6]
    :param low:1
    :param high:1

    six:  【6】
    :param li:[2,3,5,1,6]
    :param low:2
    :param high:1
    :return:
    '''
    if low < high:
        ######列表左########
        #one【1】: 0 < 4    li=[3,2,5,1,6] low=0 high=4
        #two 【2】:  0 <2     li=[3,2,5,1,6] low=0 high=2
        #three【3】: 0<1      li=[3,2,5,1,6] low=0 high=1
        #four【4】: 0 < 0  #不成立  沒有return所以默認是none給three【3】

        #####列表右########
        #five【5】: 1 < 1 #不成立 沒有return所以默認是none給three【3】:over
        #six【6】: 2 < 2 #不成立 沒有return所以默認是none給Two【2】:over
        #seven【7】: 3 < 4 #成立  li=[3,2,5,1,6] low=3 high=4

        #eight【8】:3<3 #不成立  沒有return所以默認是none給sevev【7】
        # nine【9】:4<4 #不成立  沒有return所以默認是none給sevev【7】

        mid = (low + high) // 2
        #one【1】: mid=0+4//2=2     li=[3,2,5,1,6] low=0 mid=2 high=4
        #two 【2】:  mid = 2//2 =1    li=[3,2,5,1,6] low=0 mid=1 high=2
        #three【3】: mid = 0+1//2=0   li=[3,2,5,1,6] low=0 mid=0 high=1
        #seven【7】: mid = 3+4//2=3   li=[3,2,5,1,6] low=3  mid=3 high=4

        _mergesort(li,low, mid)
        #one【1】: 調用了  _mergesort(li=[3,2,5,1,6] ,low=0, mid=2)  代號:two【2】   li=[3,2,5,1,6] low=0 mid=2 high=4
        #two 【2】: 調用了  _mergesort(li=[3,2,5,1,6] ,low=0, mid=1)  代號:three【3】  li=[3,2,5,1,6] low=0 mid=1 high=2
        #three【3】: 調用了 _mergesort(li=[3,2,5,1,6] ,low=0, mid=0)  代號:four【4】   li=[3,2,5,1,6] low=0 mid=0 high=1

        #three【3】: 收到了 four【4】的返回值是None,li=[3,2,5,1,6] ,low=0, mid=0 hight=1 往下執行three【3】
        # two【2】收到了three【3】的返回值None  但是li=[2,3,5,1,6],low=0, mid=1 hight=2 繼續往下執行two【2】
        # one【1】: 收到了two【2】的返回值None 但是li=[2,3,5,1,6],low=0, mid=2  hight=4繼續往下執行one【1】

        # seven【7】: 調用了 _mergesort(li=[2,3,5,1,6] ,low=3, mid=3)  代號:eight【8】li=[2,3,5,1,6] low=3  mid=3 high=4
        # seven【7】: 收到了eight【8】的返回值None 但是li=[2,3,5,1,6] low=3  mid=3 high=4 繼續往下執行seven【7】

        _mergesort(li, mid+1, high)
        # three【3】: 執行了  _mergesort(li=[3,2,5,1,6],mid=1,high=1)  代號:five【5】 li=[3,2,5,1,6] low=0 mid=0 high=1
        # three【3】: 收到了 five【5】的返回值是None往下執行three【3】

        # two 【2】: 執行了 _mergesort(li=[2,3,5,1,6],mid=2,high=2) 代號: six【6】  li=[2,3,5,1,6] low=0 mid=1 high=2
        # two 【2】: 收到了 six【6】的返回值是None往下執行 two【2】

        # one【1】: 執行了 _mergesort(li=[2,3,5,1,6] ,low=3, mid=4)  代號:seven【7】   li=[2,3,5,1,6] low=0 mid=2 high=4

        # seven【7】: 執行了 _mergesort(li=[2,3,5,1,6] ,low=4, mid=4)  代號:nine【9】 li=[2,3,5,1,6]low=3  mid=3 high=4
        # seven【7】: 收到了 nine【9】的返回值是None往下執行seven【7】
        # one【1】: 收到了seven【7】的返回值None 但是li=[2,3,5,1,6],low=0, mid=2  hight=4繼續往下執行one【1】

        merge(li, low, mid, high)
        # three【3】:執行了 merge(li=[3,2,5,1,6], low=0, mid=0, high=1)   代號:merge3  li=[3,2,5,1,6] low=0  mid=0 high=1
        # three【3】執行的merge3 得到了li = [2,3,5,1,6] 返回None給two【2】(開始執行two【2】)

        # two 【2】執行了 merge(li=[2,3,5,1,6], low=0, mid=1, high=2) 代號:merge2 : li=[2,3,5,1,6] low=0 mid=1 high=2
        # two 【2】執行的merge2 得到了li = [2,3,5,1,6] 返回None給one【1】(開始執行one【1】)

        # seven【7】: 執行了 merge(li=[2,3,5,1,6], low=3, mid=3, high=4) 代號:merge7  li=[2,3,5,1,6] low=3  mid=3 high=4
        # seven【7】 執行的merge7 得到了li = [2,3,5,1,6] 返回None給one【1】(開始執行one【1】)

        # one【1】執行了 merge(li=[2,3,5,1,6], low=0, mid=2, high=4) 代號:merge1 li=[2,3,5,1,6]low=0, mid=2 high=4
        # one【1】 執行的merge1 得到了li =[1,2,3,5,6] 整個程序結束

        #以此類推,
        # 邏輯如下:

                  #row1:
                         #li = [3, 2, 5, 1, 6], low = 0, mid = 0, high = 1
                         #拿到了進行切片得到: li[low:hight+1]== li[0:2] == [3,2]
                         #從列表[3,2]進行while判斷比較元素大小,從小到大順序追加到臨時列表ltmp(簡單理解就是:對該列表進行排序)
                         #得到:ltmp=[2,3]
                         #然後和li切片出來的列表下標對應進行合併(替換):li[low:hight+1]=ltmp == li[0:2] =[2,3]
                         #最終得到 :li=[2, 3, 5, 1, 6]
                 # row2 :
                        #li = [2, 3, 5, 1, 6], low = 0, mid = 1, high = 2
                        #拿到了進行切片得到: li[low:hight+1]== li[0:3] == [2,3,5]
                        # 從列表[2,3,5]進行while判斷比較元素大小,從小到大順序追加到臨時列表ltmp(簡單理解就是:對該列表進行排序)
                        #得到:ltmp=[2,3,5]
                        #然後和li切片出來的列表下標對應進行合併(替換):li[low:hight+1]=ltmp == li[0:3] =[2,3,5]
                        #最終得到: li=[2, 3, 5, 1, 6]
                # row3 :
                        # li = [2, 3, 5, 1, 6], low = 3, mid = 3, high = 4
                        # 拿到了進行切片得到: li[low:hight+1]== li[3:5] == [1.6]
                        # 從列表[1,6]進行while判斷比較元素大小,從小到大順序追加到臨時列表ltmp(簡單理解就是:對該列表進行排序)
                        # 得到:ltmp=[1,6]
                        # 然後和li切片出來的列表下標對應進行合併(替換):li[low:hight+1]=ltmp == li[3:5]=[1,6]
                        # 最終得到:li=[2, 3, 5, 1, 6]
                #row4 :
                        # li = [2, 3, 5, 1, 6], low = 0, mid = 2, high = 4
                        # 拿到了進行切片得到: li[low:hight+1]== li[0:5] == [2,3,5,1,6]
                        # 從列表[2,3,5,1,6]進行while判斷比較元素大小,從小到大順序追加到臨時列表ltmp(簡單理解就是:對該列表進行合併排序)
                        # 得到:ltmp=[1,2,3,5,6]
                        # 然後和li切片出來的列表下標對應進行合併(替換):li[low:hight+1]=ltmp == li[0:5]=[1,2,3,5,6]
                        # 最終得到   li=[1,2,3,5,6]

    return li

@cal_time
def mergesort(li):
    '''
    :param li:[3,2,5,1,6]    【1】
    :return:
    '''
    _mergesort(li, 0, len(li) - 1)
    return li

li = []  # [871, 100, 160, 755, 614, 621, 403, 671, 256, 915, 174, 906, 253, 973, 199, 370, 950, 970, 287, 648]
for i in range(50000):
 li.append(random.randrange(50000))
mergesort(li)

    Python自帶排序sort和sorted內部實現O(nlog2n) == O(nlogn)

__author__ = "Burgess Zheng"
#!/usr/bin/env python 
#-*- coding:utf-8 -*-

import random
import time
import copy
import sys

def cal_time(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        result = func(*args, **kwargs)
        t2 = time.time()
        print("%s running time: %s secs." % (func.__name__, t2 - t1))
        return result
    return wrapper

@cal_time
def sys_sort(data):
    return data.sort()


array =[] # [3,2,8,1]
for i in range(50000):
    array.append(random.randrange(50000))

sys_sort(array)

    python heapq O(nlog2n) == O(nlogn)

__author__ = "Burgess Zheng"
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import heapq
import random
import time


heap = []
data = list(range(10000))
random.shuffle(data)  #列表元素打亂

# for num in data:
#     heapq.heappush(heap, num)
#         # data的元素隨機放入heap該列表
# for i in range(len(heap)):
#     print(heapq.heappop(heap))
#             #提取heap列表裏面最小的元素

t1 = time.time()
result = heapq.nsmallest(50000, data)
t2 = time.time()
print('cost:',t2-t1)
print(result)

    二分查找(兩種方式:遞歸和while)

1.對列表進行排序
2.計算出列表的長度,
3.通過長度除以2變整數取出列表中間下標的元素
4.要查找的的數字和列表中間的元素進行判斷
         如果該數字<中間的元素,說明中間元素的後面元素們就不用找了
                   我們就應該取該列表中間元素的下標-1作爲結尾的下標
         如果該數字>中間的元素,說明中間元素的前面元素們就不用找了
             我們就應該取該列表中間元素的下標+1作爲起始的下標

__author__ = "Burgess Zheng"
#!/usr/bin/env python 
#-*- coding:utf-8 -*-


import time
import random

def cal_time(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        result = func(*args, **kwargs)
        t2 = time.time()
        print("%s running time: %s secs." % (func.__name__, t2 - t1))
        return result
    return wrapper

@cal_time
def bin_search(data_set, val):
    low = 0
    high = len(data_set) - 1
    while low <= high:
        mid = (low+high)//2
        if data_set[mid] == val:
            return data_set[mid]
        elif data_set[mid] < val:
            low = mid + 1
        else:
            high = mid - 1
    return


#裝飾器不可以裝飾在遞歸函數
#那麼可以使用一種方法就是另起一個函數,接收該遞歸函數的結果,這樣裝飾
def binary_search(dataset, find_num):
    if len(dataset) > 1:
        mid = int(len(dataset) / 2) #該列表中間元素的下標
        if dataset[mid] == find_num:
            #print("Find it")
            return dataset[mid]
        elif dataset[mid] > find_num:
            return binary_search(dataset[0:mid], find_num)
            # 如果該數小於列表中間的元素
            # 這裏很清楚如果小於中間的元素代表後面的元素就不用查了
            # 那麼我們只需要把中間元素的下標mid-1作爲結尾下標進行遞歸尋找
        else:
            return binary_search(dataset[mid + 1:], find_num)
            # 如果該數大於於列表中間的元素
            # 這裏很清楚如果大於中間的元素代表前面的元素就不用查了
            # 那麼我們只需要把中間元素的下標mid+1最爲起始下標進行遞歸尋找

    else:
        if dataset[0] == find_num:
            #print("Find it")
            return dataset[0]
        else:
            pass
            print("Cannot find it.")

#裝飾器不可以裝飾在遞歸函數
#那麼可以使用一種方法就是另起一個函數,接收該遞歸函數的結果,這樣裝飾
@cal_time
def binary_search_alex(data_set, val):
    return binary_search(data_set, val)


data = list(range(10000000))
print(bin_search(data, 173320))
print(binary_search_alex(data, 173320))

    計算排序

計算排序(場景:隨一個區域的人的年齡進行排序,)
場景需要:限定範圍(指定的下標>列表最大的元素),且多個元素重複

__author__ = "Burgess Zheng"
#!/usr/bin/env python 
#-*- coding:utf-8 -*-

import random
import time
import copy
import sys

def cal_time(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        result = func(*args, **kwargs)
        t2 = time.time()
        print("%s running time: %s secs." % (func.__name__, t2 - t1))
        return result
    return wrapper


@cal_time
def sys_sort(data):
    return data.sort()

@cal_time
def count_sort(li, max_num):
    count = [0 for i in range(max_num + 1)]
       #count ==[0.0.0.0.0...]
    for num in li:
        count[num] += 1
        #我們生產的li的列表是有邊界的 假如該列表的元素是0-10
        #這樣num如果是元素5,那麼count該列表的下標5的元素+1
        #這樣如果該li的列表裏面有3個5,
        #我們後期通過count列表下標5的元素就知道5的次數
    i = 0

    for num,m in enumerate(count):
        #//num下標, m:重複數字的次數
        for j in range(m):
            li[i] = num
            #從下標0開始添加元素
            i += 1



data = []
for i in range (50000):
    data.append(random.randint(0,100))
print(count_sort(data,100))


data1 = []
for i in range (50000):
    data1.append(random.randint(0,100))
print(sys_sort(data1))

    top榜單排序

場景(榜單TOP):如微博,新聞等排行榜,要從提取點贊最多的新聞排行榜,或者評論排行榜的前10名or前20名等等...
就需要在某個列表中取出排序最小的10個元素(等於點贊最多的)形成一個新的列表(排序完畢),其他的元素無需理會(不管是否有排序)

import random
import time
import copy
import sys

def cal_time(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        result = func(*args, **kwargs)
        t2 = time.time()
        print("%s running time: %s secs." % (func.__name__, t2 - t1))
        return result
    return wrapper

###############Python自帶排序###################
@cal_time
def sys_sort(data):
    return data.sort()
######################################


#######題目#####################################
#現在又n個數(n>10000),設計算法,按大小順序排序後,我們只需要得到前面0-9的下標元素,其餘不要
##############################################################

##############O(n)方案############################
#示例演變:得到li列表排序好的前3個元素,其餘元素排序不排序不理會
#1. li = [1,3,5,4,2,7,9,6,8]
#2. top = [1,3,5,4] 提取了li列表的前4個元素形成top列表
        #對top列表使用插入排序 得到 top = [1,3,4,5]
#3. top[3] = li[4]    top[3] = 2   top=[1,3,4,2]
        # 對top列表使用插入排序 得到 top = [1,2,3,4]
#按照上面這種方式,不斷的把li的元素進行替換top列表執行插入排序
#直到li的元素全被替換完畢,結果自然就出來,我們也得到了前面4個排序好的元素(切片取前3個)

#簡單的來說:
    #1從真正的列表提取前面11個元素形成臨時列表,對臨時列表進行插入排序
    #2.真正列表只需要進行循環從12個元素開始替換掉臨時列表的第11個元素
    #3.臨時列表第11個元素被替換就執行一次插入排序(排序11個元素的列表)
    #4.最終臨時列表得到了真正列表所有元素排序過後的11個最小的元素,那麼提取前10個元素就OK了

def insert(li, i):
    '''
    row1
    :param li: [1,5,2,4]
    :param i: 1

    row2
    :param li: [1,5,2,4]
    :param i: 2

    row3
    :param li: [1,5,2,4]
    :param i: 3

    '''
    tmp = li[i]
    j = i - 1
    while j >= 0 and li[j] > tmp:
        li[j + 1] = li[j]
        j = j - 1
    li[j + 1] = tmp

def insert_sort(li):
    '''
    :param li: [1,5,2,4]
    :return:
    '''
    for i in range(1, len(li)):  #range(1, len(li)) == [1,2,3]
        insert(li, i)  #調用 insert ([1,5,2,4],1)  #得到了li = [1,5,2,4]
                       #調用 insert ([1,5,2,4],2)  #得到了li = [1,2,5,4]
                        # 調用 insert ([1,5,2,4],3)  #得到了li = [1,2,4,5]
@cal_time
def topk(li, k):
    '''
    假設
    :param li: [1,5,2,4,3]
    :param k: 3  #獲取排序完畢前3個元素
    :return:
    '''
    top = li[0:k + 1]  #[1,5,2,4] ,爲什麼要4個,是預留後面排序中插入到前面的元素,
    insert_sort(top)   #調用insert_sort該函數  得到 top = [1,2,4,5]
    for i in range(k+1, len(li)): #range(k+1, len(li) == range(4,len(1,5,2,4,3)) == [4,5]
        top[k] = li[i]   #top[3] = li[4]   ==  top =  [1,2,4,3]
        insert(top, k)  #重新開始排序得出前3位
    return top[:-1]
####################################################


####堆的方案##########################################

def sift(data, low, high):
    '''

    根據完全二叉樹堆的原理,通過非葉子節點最終把列表元素最大的替換到列表的下標0
    #示例演變:得到li列表排序好的前4個元素,其餘元素排序不排序不理會
    #1. li = [1,5,7,4,3,8,2,0]
    #2. heap=[1,5,7,4,3] 提取了li列表的前5個元素形成heap列表
            #對heap列表使用堆排序(列表最大的元素替換到下標 0)
    #3. 條件如果 heap[0]>li[5]才執行heap[0] =li[5] 替換li第6個元素(等於把heap最大的元素扔掉)
            (循環整個li列表下標了5開始到結尾)
            每替換heap一個大元素就進行對heap列表使用堆排序
            以此類推最終:循環最終heap該列表得到了最小的5個元素
    #4  在遍歷heap該列表,讓其進行排序(元素小的往前面)

#簡單的來說:
    #1從真正的列表提取前面10個元素形成臨時列表,進行對該臨時列表進行堆排序,先對臨時列表變成大頂堆(下標0元素最大)
    #2.真正列表只需要進行循環從12個元素開始到結尾每次判斷元素否小於臨時列表的下標0該元素嗎,小就替換
    #3.臨時列表的下標0元素被替換以後就不在是大頂堆了,如果繼續堆排序成爲大頂堆,如此循環到真正列表元素結束爲止
    #4.最終臨時列表獲取到了最小的5個元素,我們在進行堆排序頭尾替換循環遍歷對該列表進行排序


    row1:
    :param data: [1,5,7,4,3]
    :param low: 1
    :param high: 4

    row2:
    :param data: [1,5,7,4,3]
    :param low: 0
    :param high: 4
    :return:
    '''
    i = low     #非葉子節點(子樹)
        # row1:i=1
        # row2: i=0
    j = 2 * i + 1   #左孩子
        #row1: j=3
        #row2: j=1
    tmp = data[i]
        # row1: tmp=5
        # row2: tmp=1
    while j <= high:
        #row1: 3<= 4  成立
        #row2: 1<= 4  成立
        #row2_while j=5 <= 4 不成立 執行結束 data = [7,5,1,4,3]
        if j + 1 <= high and data[j] < data[j+1]:
               #row1: j+1 = 4:右孩子   4<=4 and 4 < 3    不成立
               #row2: j+1 = 2 :右孩子   2<=4 and 5 < 7   成立
            j += 1   #j指向右孩子
                # row2: j+1 = 2 :右孩子

        if data[j] > tmp:
            #row1: 4>5不成立
            #row2: j=2 7>1 成立
            data[i] = data[j]   #孩子填到父親的空位上
                # row2:  data[i] = 1  替換成  7  data[i] =7  data = [7,5,1,4,3]
                # while1: data = [5,4,7,1,3]
            i = j
                #row2:j成爲了非葉子節點 i = 2
            j = 2 * i +1
                #row0:新左孩子3  j=5
            #row2_while1循環
        else:
            break
    data[i] = tmp           #最高領導放到父親位置

@cal_time
def topn(li, n):
    '''
    假設
    :param li: [1,5,7,4,3,8,2,0]
    :param n: 5 #獲取排序完畢前4個元素
    :return:
    '''
    heap = li[0:n]   #heap=[1,5,7,4,3]

    #該循環負責吧指定列表進行完全二叉樹堆排序最大的元素替換到列表的下標0位置
    for i in range(n // 2 - 1, -1, -1):  #==for i in [1,0]  #1爲非葉子節點(子樹)
        sift(heap, i, n - 1)  # row1:  sift([1,5,7,4,3],1,4) 獲取到  heap = [1,5,7,4,3]
                            #row2:  sift([1,5,7,4,3],0,4) 獲取到  heap = data = [7,5,1,4,3]

    #遍歷
    for i in range(n, len(li)):  #5,8  == for i in [5,6,7]
        if li[i] < heap[0]:
            # row1:  li[5]=8 < heap[0]=7 不成立
            # row2:  li[6]=2 < heap[0]=7 成立
            # row3:  [5,2,1,4,3]   li[7]=0 < heap[0]=5 成立
            heap[0] = li[i]
            # row2:  heap = [2,5,1,4,3]    li=[2,5,7,4,3,8,7,0]
            # row3:  heap = [0,2,1,4,3]    i=[0,5,7,4,3,8,7,5]
            sift(heap, 0, n - 1)
            # row2: sift([2,5,1,4,3],0,4)  獲取到:heap = [5,2,1,4,3]
            # row3:sift([0,5,1,4,3],0,4)   獲取到:heap = [4,0,1,2,3]

    for i in range(n - 1, -1, -1):  # i指向堆的最後  [4,3,2,1,0]
        heap[0], heap[i] = heap[i], heap[0]  # 領導退休,刁民上位
            #heap = [4, 0, 1, 2, 3]   heap[0]和heap[4]互換位置   [3,0,1,2,4]
            #row2:heap = [3,2,1,0,4] heap[0]和heap[3]互換位置   [0,2,1,3,4]
            #row3:heap = [2,0,1,3,4] heap[0]和heap[2]互換位置   [1,0,2,3,4]
            # row4:heap = 1,0,2,3,4]  heap[0]和heap[2]互換位置   [0,1,2,3,4]
        sift(heap, 0, i - 1)  # 調整出新領導
        # row2: sift(  [3,0,1,2,4]   , 0, 4 - 1) 獲取到:heap = [3,2,1,0,4]
        # row3: sift(  [0,2,1,3,4]   , 0, 3- 1) 獲取到:heap = [2,0,1,3,4]
        # row4: sift(  [1,0,2,3,4]   , 0, 2 -1) 獲取到:heap = [1,0,2,3,4]
        # row5...                         1
        # row6...                         0
        #最終得到了heap = [0,1,2,3,4]
    return heap
############################################################

####################測試3種排序###############################
data0 = list(range(50000))
random.shuffle(data0)
print(topk(data0,10))

data1 = list(range(50000))
random.shuffle(data1)
print(topn(data1, 10))

data2 = list(range(50000))
random.shuffle(data2)
sys_sort(data2)
print(data2[0:10])

 

所有實驗過的排序執行時間對比

冒泡排序On^2:50000個元素排序時間:574.2363秒(時間跟機器性能有關,該測試只限本機器)

冒泡排序優化版On^2:50000個元素排序時間:212.2070秒(時間跟機器性能有關,該測試只限本機器)

選擇排序On^2:50000個元素排序時間:357.8232秒(時間跟機器性能有關,該測試只限本機器)

選擇排序優化版On^2:50000個元素排序時間:214.9414秒(時間跟機器性能有關,該測試只限本機器)

插入排序On^2:50000個元素排序時間:331.9482秒(時間跟機器性能有關,該測試只限本機器)

快速排序Onlog2n:50000個元素排序時間:0.4804秒(時間跟機器性能有關,該測試只限本機器)

堆排序Onlog2n:50000個元素排序時間:0.8320秒(時間跟機器性能有關,該測試只限本機器)

歸併排序Onlog2n:50000個元素排序時間:0.5224秒(時間跟機器性能有關,該測試只限本機器)

python系統自帶sort排序Onlog2n:50000個元素排序時間:0.0263秒秒(時間跟機器性能有關,該測試只限本機器)

python系統自帶heapq排序Onlog2n:50000個元素排序時間:0.0283秒(時間跟機器性能有關,該測試只限本機器)

-------------------------------------------------------------------------------------

(場景:統計區域所有人的年齡)
【需求:限定範圍(指定的下標>列表最大的元素),且多個元素重複..】:

計算排序Onlog2n50000隨機元素:100以內的數字列表跑的時間和Python自帶排序對比:0.0146秒(時間跟機器性能有關,該測試只限本機器)(列表裏面越多元素重複那麼對Python自帶sort排序有那麼點優勢,如果列表沒有重複元素情況下和Python字典的sort排序比較..那效率低太多了)

--------------------------------------------------------------------------------------

場景(榜單TOP):如微博,新聞等排行榜,要從提取點贊最多的新聞排行榜,或者評論排行榜的前10名or前20名等等...
就需要在某個列表中取出排序最小的10個元素(等於點贊最多的)形成一個新的列表(排序完畢),其他的元素無需理會(不管是否有排序)

topk提取(使用插入排序))O(n):50000個元素排序時間: 0.0419秒
topn提取(使用堆排序)O(n): 50000個元素排序時間:     0.0097秒
Python自帶sort排序O(nlogn): 50000個元素排序時間:    0.0263秒
Python自帶heapq排序O(n): 50000個元素排序時間:      0.0048秒
(時間跟機器性能有關,該測試只限本機器)

目錄篇:python相關目錄篇 點擊跳轉

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章