【二叉樹】全面剖析層序遍歷算法

思路是很簡單。
一種思路是對每一層的節點數進行計數,首先在隊列中預存好整棵樹的根節點。然後不斷地從隊列中取出當前層的節點,以及加入下一層的節點。如果你只是按照層序的順序遍歷,那很簡單。但如果你在每一層結束之後還需要做一些額外操作(比如換行等),那麼就有一些細節需要注意。這裏主要是剖析一下後面的情況。

如果在每一層的最後都需要一些額外操作,那麼每一層的尾節點(每一層的最後一個節點)很關鍵,必須先處理完所有普通節點都具有的操作(比如打印當前值、添加左右子節點到隊列等)後,然後再處理尾節點作爲一個特殊節點所特有的操作(比如換行等)。詳細看下圖和代碼。
在這裏插入圖片描述

代碼如下:

#include <iostream>
#include <queue>
using namespace std;

class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node* next;

    Node() : val(0), left(NULL), right(NULL), next(NULL) {}

    Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}

    Node(int _val, Node* _left, Node* _right, Node* _next)
        : val(_val), left(_left), right(_right), next(_next) {}
};

void f(Node* root) {
    if (root == nullptr) {
        return ;
    }

    queue<Node*> q;
    q.push(root);
    
    int cur_len = 1;
    int next_len = 0;

    while (!q.empty()) {
    	// 所有普通節點都有的操作
        Node* cur = q.front();
        q.pop();
        cout << cur->val << " ";
        --cur_len;
        
        // 所有普通節點都有的操作
        if (cur->left) {
            q.push(cur->left);
            ++next_len;
        }
        if (cur->right) {
            q.push(cur->right);
            ++next_len;
        }

        // 尾節點作爲一個特殊節點所特有的操作
        if (cur_len <= 0) {
            cur_len = next_len;
            next_len = 0;
            cout << endl;
        }
    }

}

int main() {
    Node *node1 = new Node(1, nullptr, nullptr, nullptr);
    Node *node2 = new Node(2, nullptr, nullptr, nullptr);
    Node *node3 = new Node(3, nullptr, nullptr, nullptr);
    Node *node4 = new Node(4, nullptr, nullptr, nullptr);
    Node *node5 = new Node(5, nullptr, nullptr, nullptr);
    Node *node6 = new Node(6, nullptr, nullptr, nullptr);
    Node *node7 = new Node(7, nullptr, nullptr, nullptr);

    node1->left = node2;
    node1->right = node3;
    node2->left = node4;
    node2->right = node5;
    node3->left = node6;
    node3->right = node7;

    f(node1);
}

在這裏插入圖片描述


另一種思路不是對每一層計數,而是用level_begin記錄下一層的首節點(下一層的第一個節點)的指針。當按照層序遍歷的順序遍歷到level_begin時,說明當前指針已經指向了新的一層。如果在每一層的最後都需要一些額外操作,那麼下一層的首節點(下一層的第一個節點)很關鍵。對於首節點的處理,與上一個思路正好相反,必須先處理完首節點作爲一個特殊節點所特有的操作(比如換行等)後,然後再處理所有普通節點都具有的操作(比如打印當前值、添加左右子節點到隊列等)。

代碼如下:

#include <iostream>
#include <queue>
using namespace std;

class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node* next;

    Node() : val(0), left(NULL), right(NULL), next(NULL) {}

    Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}

    Node(int _val, Node* _left, Node* _right, Node* _next)
        : val(_val), left(_left), right(_right), next(_next) {}
};

void f(Node* root) {
    if (root == nullptr) {
        return ;
    }

    queue<Node*> q;
    q.push(root);
    Node* level_begin = root;

    
    while (!q.empty()) {
        // 所有普通節點都有的操作
        Node* cur = q.front();
        q.pop();

        // 首節點作爲一個特殊節點所特有的操作
        if (cur == level_begin) {
            level_begin = nullptr;
            cout << endl;
        }

        // 所有普通節點都有的操作
        cout << cur->val << " ";
        
        // 所有普通節點都有的操作
        if (cur->left) {
            if (level_begin == nullptr) {
                level_begin = cur->left;
            }
            q.push(cur->left);
        }
        if (cur->right) {
            if (level_begin == nullptr) {
                level_begin = cur->right;
            }
            q.push(cur->right);
        }

        
    }

}

int main() {
    Node *node1 = new Node(1, nullptr, nullptr, nullptr);
    Node *node2 = new Node(2, nullptr, nullptr, nullptr);
    Node *node3 = new Node(3, nullptr, nullptr, nullptr);
    Node *node4 = new Node(4, nullptr, nullptr, nullptr);
    Node *node5 = new Node(5, nullptr, nullptr, nullptr);
    Node *node6 = new Node(6, nullptr, nullptr, nullptr);
    Node *node7 = new Node(7, nullptr, nullptr, nullptr);

    node1->left = node2;
    node1->right = node3;
    node2->left = node4;
    node2->right = node5;
    node3->left = node6;
    node3->right = node7;

    f(node1);
}

在這裏插入圖片描述


兩種思路的區別在於,按照第一種思路遍歷完之後,則會多輸出一個換行符;而按照第二種思路,則會在輸出第一層的第一個節點之前,多輸出一個換行符。當然,多餘的換行符是可以避免輸出的。對於第一種思路,將尾節點的判斷條件if (cur_len <= 0)改爲if (cur_len <= 0 && !q.empty()),多了一個!q.empty();對於第二種思路,將level_begin的初值從root改爲nullptr就好。

最終修改後的代碼如下:
第一種思路:

#include <iostream>
#include <queue>
using namespace std;

class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node* next;

    Node() : val(0), left(NULL), right(NULL), next(NULL) {}

    Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}

    Node(int _val, Node* _left, Node* _right, Node* _next)
        : val(_val), left(_left), right(_right), next(_next) {}
};

void f(Node* root) {
    if (root == nullptr) {
        return;
    }

    queue<Node*> q;
    q.push(root);

    int cur_len = 1;
    int next_len = 0;

    while (!q.empty()) {
        // 所有普通節點都有的操作
        Node* cur = q.front();
        q.pop();
        cout << cur->val << " ";
        --cur_len;

        // 所有普通節點都有的操作
        if (cur->left) {
            q.push(cur->left);
            ++next_len;
        }
        if (cur->right) {
            q.push(cur->right);
            ++next_len;
        }

        // 尾節點作爲一個特殊節點所特有的操作
        if (cur_len <= 0 && !q.empty()) {
            cur_len = next_len;
            next_len = 0;
            cout << endl;
        }
    }

}

int main() {
    Node* node1 = new Node(1, nullptr, nullptr, nullptr);
    Node* node2 = new Node(2, nullptr, nullptr, nullptr);
    Node* node3 = new Node(3, nullptr, nullptr, nullptr);
    Node* node4 = new Node(4, nullptr, nullptr, nullptr);
    Node* node5 = new Node(5, nullptr, nullptr, nullptr);
    Node* node6 = new Node(6, nullptr, nullptr, nullptr);
    Node* node7 = new Node(7, nullptr, nullptr, nullptr);

    node1->left = node2;
    node1->right = node3;
    node2->left = node4;
    node2->right = node5;
    node3->left = node6;
    node3->right = node7;

    f(node1);
}

第二種思路:

#include <iostream>
#include <queue>
using namespace std;

class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node* next;

    Node() : val(0), left(NULL), right(NULL), next(NULL) {}

    Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}

    Node(int _val, Node* _left, Node* _right, Node* _next)
        : val(_val), left(_left), right(_right), next(_next) {}
};

void f(Node* root) {
    if (root == nullptr) {
        return;
    }

    queue<Node*> q;
    q.push(root);
    Node* level_begin = nullptr;


    while (!q.empty()) {
        // 所有普通節點都有的操作
        Node* cur = q.front();
        q.pop();

        // 首節點作爲一個特殊節點所特有的操作
        if (cur == level_begin) {
            level_begin = nullptr;
            cout << endl;
        }

        // 所有普通節點都有的操作
        cout << cur->val << " ";

        // 所有普通節點都有的操作
        if (cur->left) {
            if (level_begin == nullptr) {
                level_begin = cur->left;
            }
            q.push(cur->left);
        }
        if (cur->right) {
            if (level_begin == nullptr) {
                level_begin = cur->right;
            }
            q.push(cur->right);
        }


    }

}

int main() {
    Node* node1 = new Node(1, nullptr, nullptr, nullptr);
    Node* node2 = new Node(2, nullptr, nullptr, nullptr);
    Node* node3 = new Node(3, nullptr, nullptr, nullptr);
    Node* node4 = new Node(4, nullptr, nullptr, nullptr);
    Node* node5 = new Node(5, nullptr, nullptr, nullptr);
    Node* node6 = new Node(6, nullptr, nullptr, nullptr);
    Node* node7 = new Node(7, nullptr, nullptr, nullptr);

    node1->left = node2;
    node1->right = node3;
    node2->left = node4;
    node2->right = node5;
    node3->left = node6;
    node3->right = node7;

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