樹,樹的遍歷和堆排序

一 樹基礎

1 樹的定義

1 兩種定義方式

樹: 非線性結構算法

1 樹是n(n>=0)個元素的集合
n=0時,稱爲空樹
樹只有一個特殊的節點是沒有前驅元素的,稱爲樹的根,及Root
樹中除了根節點外,其他的元素只能有一個前驅,能夠有0個或多個後繼,根沒有前驅,只有後繼數據結構


2 遞歸定義
樹T 是n(n>=0)個元素的集合,n=0時,稱爲空樹
其有且只有一個特殊元素及根,剩餘的元素均可以被劃分爲m個互不相交的集合,T1,T2,T3...Tm,而每一個集合都是樹,稱爲T的子樹subtree,其子樹也有本身的根ide

2 數的集羣術語

前驅: 當前節點前面的元素
後驅: 當前節點後面的元素
結點: 樹中的數據元素
節點的度degree:節點擁有的子樹的數目稱爲度,記作d(v)
葉子結點: 結點的度爲0,稱爲葉子結點,終端結點,末端結點
分支結點:結點的度不爲0,稱爲非終端結點或分支結點
分支:結點之間的關係,及連線
內部結點:除根節點之外的分支結點,固然不包括葉子節點,及掐頭,去尾,留中間
樹的度: 樹的度是各個節點的度的最大值。code


孩子(兒子child)結點:結點的子樹的根節點稱爲該結點的孩子
雙親(父Parent)結點:一個結點是它各子樹的根結點的雙親。
兄弟(sibling)結點:具備相同雙親結點的結點
祖先結點:從根結點到該結點所經分支上的全部結點
子孫結點:結點全部子樹上的結點都稱爲子孫
結點的層次:根結點爲第一層,根結點的孩子爲第二層,依次類推,記作L(v)
樹的深度(高度Depth):樹的層次的最大值
堂兄弟:雙親在同一層的結點。orm

有序樹:結點的子樹是有序的(兄弟有大小,有前後次序),不能交換
無序樹:結點的子樹是無序的,能夠交換排序


路徑:樹中的K個節點n1,n2...nk,知足ni是n(i+1)的雙親,稱爲n1到nk的一條路徑,就是一條線串下來的,前一個都是後一個的父(前驅)節點遞歸

路徑長度=路徑上結點長度-1,及分支數it

森林:m(m>=0)棵不相交的樹的集合
對於結點而言,其子樹的集合就是森林,form

3 樹特色:

1 惟一的根
2 子樹不相交
3 除了根之外,每一個元素只能有一個前驅,能夠有0個或多個後繼
4 根結點沒有雙親節點(前驅),葉子節點沒有孩子結點(後繼)
5 vi是vj的雙親,則L(vi)=L(vj)-1,也就是說雙親比孩子節點的層次小1class

2 二叉樹概念

1 特色

1 每一個結點最多2個子樹
二叉樹不存在度數大於2的節點
2 它是有序樹,左子樹,右子樹是順序的,不能交換次序
3 即便某一個結點只有一顆子樹,也要肯定其是左子樹仍是右子樹

2 二叉樹的五種形態

1 空二叉樹
2 只有一個根結點
3 根結點只有左子樹
4 根結點只有右子樹
5 根結點有左子樹和右子樹

3 斜樹

樹,樹的遍歷和堆排序

左斜樹:全部結點都只有左子樹
右斜樹:全部結點都只有右子樹

4 滿二叉樹

樹,樹的遍歷和堆排序

一顆二叉樹的全部分支結點都存在左子樹和右子樹,且全部葉子節點都只存在在最下面一層。
一樣深度的二叉樹,滿二叉樹節點最多
k爲深度(1<=k<=n),則節點總數爲 2**(k)-1

5 徹底二叉樹

樹,樹的遍歷和堆排序

若二叉樹的深度爲k,二叉樹的層數從1到k-1層的結點都打到了最大個數,在第k層的全部結點都集中在最左邊,這就是徹底二叉樹
徹底二叉樹由滿二叉樹引出
滿二叉樹必定是徹底二叉樹,但徹底二叉樹不必定是滿二叉樹
k爲深度(1<=k<=n),則結點總數最大值爲2**k-1,當達到最大值的時候就是滿二叉樹

3 二叉樹的性質

1 在二叉樹中的第i層上最多有2**i-1 個節點(i>=1)

2 深度爲k的二叉樹,至多有2**k -1個節點(k>=1)

3 對於任何一顆二叉樹T,若是其終點節點數爲n0,度數爲2的節點爲n2,則有n0=n2+1,及葉子結點數-1=度數爲2的節點數。

證實:
總結點數爲n=n0+n1+n2,其中n0爲度數爲0的節點,及葉子節點的數量,n1爲度數爲1 的節點的數量,n2爲節點爲2度數的數量。
一棵樹的分支數爲n-1,由於除了根節點外,其他結點都有一個分支,及n0+n1+n2-1
分支數還等於n0*0+n1*1+n2*2及 n1+2n2=n-1
可知 2*n2+n1=n0+n1+n2-1 及就是 n2=n0-1


4 高度爲k的二叉樹,至少有k個節點
5 具備n個節點的徹底二叉樹的深度爲int(log2n)+1 或者 math.ceil(log2(n+1))

6 有一個n個節點的徹底二叉樹, 結點按照層序編號,如圖

樹,樹的遍歷和堆排序

若是i=1,則節點i是二叉樹的根,無雙親,若是i>1,則其雙親爲int(i/2),向下取整。就是葉子節點的編號整除2獲得的就是父節點的編號,若是父節點是i,則左孩子是2i,如有右孩子,則右孩子是2i+1,。
若是2i>n,則結點i無左孩子,及結點爲葉子結點,不然其左孩子節點存在編號爲2i。

若是2i+1>n,則節點i無右孩子,此處未說明是否不存在左孩子,不然右孩子的節點存在編號爲2i+1。

二 二叉樹遍歷

1 遍歷方式

遍歷: 及對樹中的元素不重複的訪問一遍,又稱爲掃描

1 廣度優先遍歷

一次將一層所有拿完,層序遍歷

樹,樹的遍歷和堆排序
及 1 2 3 4 5一層一層的從左向右拿取

2 深度優先遍歷

設樹的根結點爲D,左子樹爲L,右子樹爲R。且要求L必定要在R以前,則有下面幾種遍歷方式

1 前序遍歷,也叫先序遍歷,也叫先跟遍歷,DLR
樹,樹的遍歷和堆排序
1-2-4-5-3-6
2 中序遍歷,也叫中跟遍歷, LDR
樹,樹的遍歷和堆排序
4-2-5-1-6-3
3 後序遍歷,也叫後跟遍歷,LRD
樹,樹的遍歷和堆排序
4-5-2-6-3-1

三 堆排序

1 堆定義

1 堆heap 是一個徹底二叉樹
2 每一個非葉子結點都要大於或等於其左右孩子結點的值稱爲大頂堆

樹,樹的遍歷和堆排序

3 每一個非葉子結點都要小於或者等於其左右孩子結點的值稱爲小頂堆

樹,樹的遍歷和堆排序

4 根節點必定是大頂堆的最大值,小頂堆中的最小值

2 堆排序

1 構建徹底二叉樹

1 待排序數字爲 49 38 65 97 76 13 27 49

2 構建一個徹底二叉樹存放數據,並根據性質5對元素進行編號,放入順序的數據結構中

樹,樹的遍歷和堆排序

3 構造一個列表爲 [0,49,38,65,97,76,13,27,49]的列表

2 構建大頂堆

1 度數爲2的結點,若是他的左右孩子最大值比它大,則將這個值和該節點交換
2 度數爲1 的結點,若是它的左孩子的值大於它,則交換
3 若是節點被置換到新的位置,則還須要和其孩子節點重複上述過程

3 構建大頂堆--起點選擇

樹,樹的遍歷和堆排序

1 從徹底二叉樹的最後一個結點的雙親開始,及最後一層的最右邊的葉子結點的父結點開始
2 結點數爲n,則起始節點的編號爲n//2(及最後一個結點的父節點)

4 構建大頂堆--下一個節點的選擇

從起始節點開始向左尋找其同層節點,到頭後再從上一層的最右邊節點開始繼續向左逐步查找,直到根節點

樹,樹的遍歷和堆排序

5 大頂堆目標

確保每一個結點的都比其左右結點的值大

樹,樹的遍歷和堆排序
第一步,調換97和38,保證跟大

樹,樹的遍歷和堆排序
第二步,調換49和97

樹,樹的遍歷和堆排序
第三步,調換76和49

樹,樹的遍歷和堆排序
第四步,調換38和49

此時大頂堆的構建已經完成

3 排序

將大頂堆根節點這個最大值和最後一個葉子節點進行交換,則最後一個葉子節點成爲了最大值,將這個葉子節點排除在待排序節點以外,並從根節點開始,從新調整爲大頂堆,重複上述步驟。

樹,樹的遍歷和堆排序
樹,樹的遍歷和堆排序
樹,樹的遍歷和堆排序
樹,樹的遍歷和堆排序
樹,樹的遍歷和堆排序
樹,樹的遍歷和堆排序
樹,樹的遍歷和堆排序

四 實戰和代碼

1 打印樹

l1=[1,2,3,4,5,6,7,8,9]
打印結果應當以下

樹,樹的遍歷和堆排序

思路:可經過將其數字映射到下方的一條線上的方式進行處理及就是 8 4 9 2 0 5 0 1 0 6 0 3 0 7 0 的數字,其之間的空格能夠設置爲一個空格的長度,其數字的長度也可設置爲固定的2,則

樹,樹的遍歷和堆排序

則第一層第一個數字據前面的空格個數爲7個,據後面的空格也是7個,
第二層第一個數字據前面的空格個數爲3個,第二個數字距離後面的空格也是3個,
第三層據前面的空格爲1,第三層最後一個據後面的空格也是1,
第四層據前面的空格是0,第四層最後一個據後面的空格也是0,
如今在其標號前面從1開始,則層數和空格之間的關係是

1 7
2 3
3 1
4 0
轉換獲得
4 7
3 3
2 1
1 0

及就是 2**(l-1) -1 l 表示層數
間隔關係以下
1 0
2 8
3 4
4 2
轉換獲得
4 0
3 8
2 4
1 2
及就是 2**l其中l 表示層數。

代碼以下

import  math 
# 打印二叉樹
def  t(list):
    '''
    層數      層數取反(depth+1-i,depth表示總層數,此處爲4,i表示層數)   據前面的空格數            間隔數        每層字數爲
    1            4                                                              7                     0              1
    2            3                                                              3                     7              2
    3            2                                                              1                     3              4
    4            1                                                              0                     1              8
                                                                    (2**(depth-i)-1)      (2**(depth-i)-1)        (2**i)   

    層數和數據長度的關係是 n表示的是數據長度
    1     1  
    2-3   2   
    4-7   3
    8-15  4       
    2**(i-1)<num<(2**i)-1,及就是int(log2n) +1 及 math.ceil(log2n)
    '''
    index=1
    depth=math.ceil(math.log(len(list),2))    #此處獲取到的是此列表轉換成二叉樹的深度,此處前面插入了一個元素,保證列表和二叉樹同樣,其真實數據都是從1開始
    sep='  ' # 此處表示數字的寬度 
    for  i in range(depth):
        offset=2**i #每層的數字數量1  2  4  8  
        print (sep*(2**(depth-i-1)-1),end="")  # 此處取的是前面空格的長度
        line=list[index:index+offset] #提取每層的數量
        for  j,x  in enumerate(line):
            print ("{:>{}}".format(x,len(sep)),end="") # 此處經過format來獲取其偏移狀況,第一個x表示其大括號中的內容,第二個表示偏移的程度
            interval= 0  if  i ==0  else  2**(depth-i)-1  # 此處獲取到的是間隔數,當值大於1時,知足如此表達式
            if  j < len(line)-1: #選擇最後一個時不打印最後一個的後面的空格,及就是1,3,7後面的空格
                print (sep*interval,end="")
        index += offset
        print ()

結果以下
樹,樹的遍歷和堆排序

2 堆排序算法實現

# 構建一個子樹的大頂堆
def  heap_adjust(n,i,array):
    '''
    調整當前結點(核心算法)
    調整的結點的起點在n//2(二叉樹性質5結論),保證全部調整的結點都有孩子結點
    :param  n : 待比較數個數
    :param  i : 當前結點下標
    :param array : 待排序數據
    :return : None
    '''
    while  2*i<=n: # 孩子結點判斷,2i爲左孩子,2i+1爲右孩子 
        lchile_index= 2*i  #選擇結點起始並記錄 n=2*i-1,由於其是從0開始,所以只有len()-1
        max_child_index=lchile_index  
        # 判斷左右孩子的大小,並將大的賦值給max_child_index 
        if  n > lchile_index  and  array[lchile_index+1] > array[lchile_index]: #說明其有右孩子,且右孩子大於左孩子 
            max_child_index=lchile_index+1  #n=2i+1 # 此時賦值的是下標
        # 判斷左右孩子的最大值和父結點比較,若左右結點的最大值大,則進行交換
        if  array[max_child_index] > array[i]:  #此處的i是父節點,經過和父節點進行比較來確認其最大,
            array[i],array[max_child_index]=array[max_child_index],array[i]
            i= max_child_index   #右子節點的下標會賦值到父結點,並將其值賦值到父結點,
        else:
            break 
# 構建全部的大頂堆傳入參數
def  max_heap(total,array):
    for i in range(total//2,0,-1):  #構建最後一個葉子結點的父結點
        heap_adjust(total,i,array)  #傳入總長和最後一個葉子結點的父結點和數列
        print  (t(array))
    return  array
#構建大頂堆,起點選擇    
def  sort(total,array):    # 此處起點是1,所以必須在前面添加一個元素,以保證其位置編號和樹相同
    while  total>1:
        max_heap(total,array)
        array[1],array[total]=array[total],array[1]  #將最後一個結點和第一個結點調換,
        total-=1
    return array

結果以下
樹,樹的遍歷和堆排序

3 總結

堆排序 Heap Sort 是利用堆性質的一種選擇排序,在堆頂選出最大值或最小值
時間複雜度
堆排序的時間複雜度爲O(nlog2 n),因爲堆排序對原始記錄的排序狀態不敏感,所以其不管是最好,最壞和平均時間複雜度均爲O(nlog2 n)

空間複雜度
只是使用了一個交換用的空間,其空間複雜度爲O(1)穩定性不穩定的排序算法

相關文章
相關標籤/搜索