【二叉树】全面剖析层序遍历算法

思路是很简单。
一种思路是对每一层的节点数进行计数,首先在队列中预存好整棵树的根节点。然后不断地从队列中取出当前层的节点,以及加入下一层的节点。如果你只是按照层序的顺序遍历,那很简单。但如果你在每一层结束之后还需要做一些额外操作(比如换行等),那么就有一些细节需要注意。这里主要是剖析一下后面的情况。

如果在每一层的最后都需要一些额外操作,那么每一层的尾节点(每一层的最后一个节点)很关键,必须先处理完所有普通节点都具有的操作(比如打印当前值、添加左右子节点到队列等)后,然后再处理尾节点作为一个特殊节点所特有的操作(比如换行等)。详细看下图和代码。
在这里插入图片描述

代码如下:

#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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章