思路是很简单。
一种思路是对每一层的节点数进行计数,首先在队列中预存好整棵树的根节点。然后不断地从队列中取出当前层的节点,以及加入下一层的节点。如果你只是按照层序的顺序遍历,那很简单。但如果你在每一层结束之后还需要做一些额外操作(比如换行等),那么就有一些细节需要注意。这里主要是剖析一下后面的情况。
如果在每一层的最后都需要一些额外操作,那么每一层的尾节点(每一层的最后一个节点)很关键,必须先处理完所有普通节点都具有的操作(比如打印当前值、添加左右子节点到队列等)后,然后再处理尾节点作为一个特殊节点所特有的操作(比如换行等)。详细看下图和代码。
代码如下:
#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);
}