二叉樹的四種遍歷算法


二叉樹作為一種重要的數據結構,它的很多算法的思想在很多地方都用到了,比如STL算法模板,里面的優先隊列、集合等等都用到了二叉樹里面的思想,先從二叉樹的遍歷開始:

看二叉樹長什么樣子:

我們可以看到這顆二叉樹一共有七個節點

0號節點是根節點

1號節點和2號節點是0號節點的子節點,1號節點為0號節點的左子節點,2號節點為0號節點的右子節點

同時1號節點和2號節點又是3號節點、四號節點和五號節點、6號節點的雙親節點

五號節點和6號節點沒有子節點(子樹),那么他們被稱為‘葉子節點’

這就是一些基本的概念

 

二叉樹的遍歷

二叉樹常用的遍歷方式有:前序遍歷、中序遍歷、后序遍歷、層序遍歷四種遍歷方式,不同的遍歷算法,其思想略有不同,我們來看一下這四種遍歷方法主要的算法思想:

1、先序遍歷二叉樹順序:根節點 –> 左子樹 –> 右子樹,即先訪問根節點,然后是左子樹,最后是右子樹。
上圖中二叉樹的前序遍歷結果為:0 -> 1 -> 3 -> 4 -> 2 -> 5 -> 6

2、中序遍歷二叉樹順序:左子樹 –> 根節點 –> 右子樹,即先訪問左子樹,然后是根節點,最后是右子樹。
上圖中二叉樹的中序遍歷結果為:3 -> 1 -> 4 -> 0 -> 5 -> 2 -> 6

3、后續遍歷二叉樹順序:左子樹 –> 右子樹 –> 根節點,即先訪問左子樹,然后是右子樹,最后是根節點。
上圖中二叉樹的后序遍歷結果為:3 -> 4 -> 1 -> 5 -> 6 -> 2 -> 0

4、層序遍歷二叉樹順序:從最頂層的節點開始,從左往右依次遍歷,之后轉到第二層,繼續從左往右遍歷,持續循環,直到所有節點都遍歷完成
上圖中二叉樹的層序遍歷結果為:0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6

下面是四種算法的偽代碼:

前序遍歷:

preOrderParse(int n) {
    if(tree[n] == NULL)
        return ; // 如果這個節點不存在,那么結束 

    cout << tree[n].w ; // 輸出當前節點內容     
    preOrderParse(tree[n].leftChild); // 遞歸輸出左子樹 
    preOrderParse(tree[n].rightChild); // 遞歸輸出右子樹 
} 

中序遍歷

inOrderParse(int n) {
    if(tree[n] == NULL)
        return ; // 如果這個節點不存在,那么結束 

    inOrderParse(tree[n].leftChild); // 遞歸輸出左子樹 
    cout << tree[n].w ; // 輸出當前節點內容 
    inOrderParse(tree[n].rightChild); // 遞歸輸出右子樹 
} 

后序遍歷

pastOrderParse(int n) {
    if(tree[n] == NULL)
        return ; // 如果這個節點不存在,那么結束 

    pastOrderParse(tree[n].leftChild); // 遞歸輸出左子樹 
    pastOrderParse(tree[n].rightChild); // 遞歸輸出右子樹 
    cout << tree[n].w ; // 輸出當前節點內容     
} 

 

可以看到前三種遍歷都是直接通過遞歸來完成,用遞歸遍歷二叉樹簡答方便而且好理解,接下來層序遍歷就需要動點腦筋了,我們如何將二叉樹一層一層的遍歷輸出?其實在這里我們要借助一種數據結構來完成:隊列。
我們都知道,隊列是一種先進先出的數據結構,我們可以先將整顆二叉樹的根節點加入隊尾,然后循環出隊,每次讀取對頭元素輸出並且將隊頭元素出隊,然后將這個輸出的元素節點的的左右子樹分別依次加入隊尾,重復這個循環,知道隊列為空的時候結束輸出。那么整個二叉樹就被我們采用層序遍歷的思想輸出來了。下面我們看一下上圖的二叉樹用層序遍歷思想的遍歷步驟:

 

對上面的步驟的偽代碼實現

while(!que.empty())  {
    int n = que.front(); // 得到隊頭元素
    que.pop();  // 隊頭元素出隊列 
    // 如果當前節點不為空,那么輸出節點的數值,並且在隊尾插入左右子節點
    if(tree[n] != NULL) {
        cout << tree[n].w;
        que.push(tree[n].leftChild); 
        que.push(tree[n].rightChild); 
    }
}

這幾個遍歷算法的最終代碼

/*
 * 二叉樹的四種遍歷方式,這里沒有采用真實的指針去做,
 * 而是采用數組下標去模擬指針,是一種更加方便快速的方法 
 */
#include <iostream>
#include <queue> 
using namespace std;
const int N = 10010;
const int INF = -1; // 我們用一個常數來表示當前二叉樹節點為空的情況 

struct Node {
    int w; // 當前樹節點的值 
    int p; // 當前樹節點的雙親所在數組下標 
    int l; // 當前樹節點的左子節點所在數組下標 
    int r; // 當前樹節點的右子節點所在數組下標 
}; 
Node node[N];

// 按照前序遍歷二叉樹的順序輸入樹節點 
void input(int n) {
    cin >> node[n].w;
    if(node[n].w == INF) { // 輸入 -1 代表當前節點所在子二叉樹停止輸入 
        return ;
    }
    node[n].p = n / 2;
    node[n].l = n * 2;
    node[n].r = n * 2 + 1;

    input(n*2);
    input(n*2+1);
}

// 前序遍歷二叉樹 
void preOrderParse(int n) {
    if(node[n].w == INF) {
        return ;
    }

    cout << node[n].w << " ";
    preOrderParse(node[n].l);
    preOrderParse(node[n].r);
} 

// 中序遍歷二叉樹 
void inOrderParse(int n) {
    if(node[n].w == INF) {
        return ;
    }

    inOrderParse(n*2);
    cout << node[n].w << " ";
    inOrderParse(n*2+1); 
}

// 后續遍歷二叉樹 
void postOrderParse(int n) {
    if(node[n].w == INF) {
        return ;
    }

    postOrderParse(n*2);
    postOrderParse(n*2+1); 
    cout << node[n].w << " ";
} 

/* 
 * 層序遍歷二叉樹,這里采用的是 C++ STL 模板的提供的隊列(queue),
 * 並沒有自己去實現一個隊列
 */ 
void sequenceParse() {
    queue<int> que;
    int n = 1;
    que.push(1); // 插入根節點所在數組下標 
    while(!que.empty()) {
        n = que.front();
        que.pop(); // 得到隊頭元素並且將隊頭元素出隊列 
        // 如果當前節點不為空,那么輸出該節點,並且將該節點的左右子節點插入隊尾 
        if(node[n].w != INF) { 
            cout << node[n].w << " ";
            que.push(node[n].l);
            que.push(node[n].r);
        }
    }
}

int main() {
    cout << "請以前序遍歷的順序輸入二叉樹,空節點輸入 -1 :" << endl; 
    input(1); // 從下標為 1 開始前序輸入二叉樹 

    cout << "前序遍歷:" << endl; 
    preOrderParse(1); 
    cout << endl << "中序遍歷:" << endl;
    inOrderParse(1);
    cout << endl << "后序遍歷:" << endl;
    postOrderParse(1); 
    cout << endl << "層序遍歷:" << endl;
    sequenceParse();

    return 0;
} 

 

參考https://blog.csdn.net/Hacker_ZhiDian/article/details/60586445


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM