《啊哈演算法》第七章 神奇的樹

飄過的小熊發表於2016-05-15

參考:《啊哈演算法》

樹其實就是不包含迴路的連通無向圖

**

  • 連通是說其其中任意兩點之間是可達的

  • 不包含迴路就是說一定能夠不會出現走一圈的情況

**

樹的特性

  • 一棵樹中的任意兩個結點有且僅有唯一的一條路徑連通

  • 一棵樹如果有n個結點,那麼它一定恰好有n-1條邊

  • 在一棵樹中加一條邊將會構成迴路


為了確定一棵樹的形態,在樹中可以指定一個特殊的結點—–根
如果一個結點沒有子結點,這個結點就稱為葉結點。
沒有父結點的結點稱為根結點。
如果一個結點既不是根結點也不是葉結點,則稱為內部結點。
每個結點還有深度,深度是指從根結點到這個結點的層數(根為第一層)


二叉樹

嚴格遞迴定義:二叉樹要麼為空,要麼由根結點、左子樹和右子樹組成,而左子樹和右子樹分別是一棵二叉樹

二叉樹中還有兩種特殊的二叉樹:

  1. 滿二叉樹
    所有的葉結點都有相同的深度

  2. 完全二叉樹
    如果一棵二叉樹除了最右邊位置上有一個或者幾個葉結點缺少外,其他事豐滿的二叉樹,就是滿二叉樹。
    (完全二叉樹的典型應用就是堆)


堆—神奇的優先佇列

  • 所有的父結點都比子結點要小的完全二叉樹稱為最小堆(小頂堆)

  • 所有的父結點都比子結點要大的完全二叉樹稱為最大堆(大頂堆)

完全二叉樹一個性質:最後一個非葉結點是第n/2個結點

建堆及堆排序的展示(小頂堆)

演算法分析:先輸入資料存在陣列中建立一棵完全二叉樹,然後從最後一個非葉結點一次向前進行調整。調整結束後就滿足小頂堆,根結點就是最小值,此時記錄下根結點,並將其從堆中刪除,將最後一個結點送到根結點的位置,此時不滿足小頂堆,我們就向下調整使其再次滿足。重複步驟至完全二叉樹為空

#include <iostream>
#include <cstdio>
using namespace std;
//向下調整
int h[102];
int n;
void siftdown(int i) {
    int t,flag;
    while(i*2<=n&&flag==0) {
        if(h[i]>h[i*2]) {//先於左結點比
            t=i*2;
        } else {
            t=i;
        }
        if(i*2+1<=n) {//是否有右結點
            if(h[t]>h[i*2+1]) {
                t=i*2+1;
            }
        }
        if(t!=i) {
            swap(h[i],h[t]);//交換
            i=t;
        } else {
            flag=1;//滿足頂堆條件的訊號傳出
        }
    }
}

//建堆
void create() {
    //從最後一個非葉結點到第一個結點依次進行向下調整
    for(int i=n/2; i>=1; i--) {
        siftdown(i);
    }
}
//刪除最大元素
int deletemax() {
    int t;
    t=h[1];//用臨時變數記錄堆頂點的值
    h[1]=h[n];//將堆的最後一個頂點賦值到堆頂
    n--;//堆元素少一
    siftdown(1);//向下調整
    return t;//返回記錄的堆的頂點的最小值
}
int main() {
    int num;
    scanf("%d",&num);
    for(int i=1; i<=num; i++) {
        scanf("%d",&h[i]);
    }
    n=num;
    create();
    for(int i=1; i<=num; i++) {
        printf("%d ",deletemax());
    }
    return 0;
}

測試樣例

14
99 5 36 7 22 17 46 12 2 19 25 28 1 92

當然還可以通過建立大頂堆的方式進行堆排序。

雖然這裡的堆排序看上去很是費時,但是要是使用在新增一個數,或者新增一個數同時刪除一個數的情況時就比較的高效了。比如我們刪除一組數的最小值,並新增一個數,同時求新陣列的最小值時。直接將新增的數放到小頂堆的堆頂(本身這裡之前是最小的數),然後調整到符合小頂堆時的堆頂就是新的最小值。
另外要是我們新增一個數,但是不進行任何刪除操作,只是最快求出新的最小值時,只需要將新增的數新增到最後。然後一路向上調整至符合要求,新的堆頂就是新的最小值。

堆還經常被用作求一個數列中第k大的數,只需要建立一個大小為k的小頂堆,堆頂就是第k大的數

相關文章