树的遍历
3 / \ 1 5 \ / 2 4
先序: 3 1 2 5 4
中序: 1 2 3 4 5
后序: 2 1 4 5 3
层序: 3 1 5 2 4
递归
1class Recursion {
2 // 先序遍历
3 void preorder(Node *root)
4 {
5 if (!root) return;
6 cout << root->val << " ";
7 preorder(root->left);
8 preorder(root->right);
9 }
10 // 中序遍历
11 void inorder(Node *root)
12 {
13 if (!root) return;
14 inorder(root->left);
15 cout << root->val << " ";
16 inorder(root->right);
17 }
18 // 后序遍历
19 void postorder(Node *root)
20 {
21 if (!root) return;
22 postorder(root->left);
23 postorder(root->right);
24 cout << root->val << " ";
25 }
26};
迭代
1class Iteration {
2 // 先序遍历 栈
3 void preorder(Node *root)
4 {
5 stack<Node *> s;
6 s.push(root);
7
8 while (!s.empty()) {
9 Node *node = s.top();
10 s.pop();
11 cout << node->val << " ";
12 if (node->right) s.push(node->right);
13 if (node->left) s.push(node->left);
14 }
15 }
16 // 中序遍历 栈
17 void inorder(Node *root)
18 {
19 stack<Node *> s;
20 Node *cur = root;
21
22 while (cur || !s.empty()) {
23 while (cur) {
24 s.push(cur);
25 cur = cur->left;
26 }
27 cur = s.top();
28 s.pop();
29 cout << cur->val << " ";
30 cur = cur->right;
31 }
32 }
33 // 后序遍历 双栈
34 void postorder1(Node *root)
35 {
36 stack<Node *> s1, s2;
37 s1.push(root);
38
39 while (!s1.empty()) {
40 Node *cur = s1.top();
41 s1.pop();
42 s2.push(cur);
43 if (cur->left) s1.push(cur->left);
44 if (cur->right) s1.push(cur->right);
45 }
46
47 while (!s2.empty()) {
48 Node *cur = s2.top();
49 s2.pop();
50 cout << cur->val << " ";
51 }
52 }
53 // 后序遍历 栈
54 void postorder2(Node *root)
55 {
56 stack<Node *> s;
57 Node *cur = root;
58 Node *vis = nullptr;
59
60 while (cur || !s.empty()) {
61 if (cur) {
62 s.push(cur);
63 cur = cur->left;
64 continue;
65 }
66 Node *top = s.top();
67 // 右子树未访问 转到右子树
68 // 所以出栈时 右子树后一个就是双亲节点
69 if (top->right && top->right != vis) {
70 cur = top->right;
71 continue;
72 }
73 // 没有右子树 或者右子树已访问
74 s.pop();
75 vis = top;
76 cout << top->val << " "; // top
77 }
78 }
79 // 层序遍历 队列
80 void levelorder(Node *root)
81 {
82 if (!root) return;
83 queue<Node *> q;
84 q.push(root);
85 while (!q.empty()) {
86 Node *node = q.front();
87 q.pop();
88 cout << node->val << " ";
89 if (node->left) q.push(node->left);
90 if (node->right) q.push(node->right);
91 }
92 }
93};
Morris
借助队列可以实现层序遍历。借助栈可以实现先序、中序、后序遍历。
Morris遍历,借助叶子结点的空指针,不使用额外空间。用到了“线索二叉树”的概念。利用叶子结点的右空指针来存储某种遍历的后继结点。
实质是构建一个在左子树遍历完之后能返回对应父节点的线索
1class Morris {
2 // 莫里斯 先序遍历 空间复杂度O(1) 时间复杂度o(2N) = O(N)
3 void preorder(Node *root)
4 {
5 Node *cur = root;
6 while (cur) {
7 if (!cur->left) {
8 cout << cur->val << " ";
9 cur = cur->right; // 通过之前建立的线索 回到祖先
10 continue;
11 }
12 Node *pre = cur->left;
13 while (pre->right && pre->right != cur) {
14 pre = pre->right;
15 }
16 if (!pre->right) {
17 cout << cur->val << " ";
18 pre->right = cur; // 建立线索
19 cur = cur->left;
20 }
21 else {
22 pre->right = nullptr; // 删除线索
23 cur = cur->right; // 访问右子树
24 }
25 }
26 }
27
28 // 莫里斯 中序遍历 空间复杂度O(1) 时间复杂度o(2N) = O(N)
29 void inorder(Node *root)
30 {
31 Node *cur = root;
32 while (cur) {
33 if (!cur->left) {
34 cout << cur->val << " ";
35 cur = cur->right; // 通过之前建立的线索 回到祖先
36 continue;
37 }
38 Node *pre = cur->left;
39 while (pre->right && pre->right != cur) {
40 pre = pre->right;
41 }
42 if (!pre->right) {
43 pre->right = cur; // 建立线索
44 cur = cur->left;
45 }
46 else {
47 cout << cur->val << " ";
48 pre->right = nullptr; // 删除线索
49 cur = cur->right; // 访问右子树
50 }
51 }
52 }
53
54 // 莫里斯 后序遍历 空间复杂度O(1) 时间复杂度o(2N) = O(N)
55 void postorder(Node *root)
56 {
57 // 反转右子节点链
58 auto reverse = [](Node *from, Node *to) {
59 Node *x = from, *y = from->right, *z;
60 while (x != to) {
61 z = y->right;
62 y->right = x;
63 x = y;
64 y = z;
65 }
66 };
67 // 逆序输出
68 auto print = [&](Node *from, Node *to) {
69 reverse(from, to);
70 Node *p = to;
71 while (true) {
72 cout << p->val << " ";
73 if (p == from) break;
74 p = p->right;
75 }
76 reverse(to, from);
77 };
78
79 Node dummy(0, root, nullptr);
80 Node *cur = &dummy;
81 while (cur) {
82 if (!cur->left) {
83 cur = cur->right;
84 continue;
85 }
86 Node *pre = cur->left;
87 while (pre->right && pre->right != cur) {
88 pre = pre->right;
89 }
90 if (!pre->right) {
91 pre->right = cur;
92 cur = cur->left;
93 }
94 else {
95 print(cur->left, pre);
96 pre->right = nullptr;
97 cur = cur->right;
98 }
99 }
100 }
101};
树的重建
通过先序和中序遍历重建二叉树。本质是通过中序遍历找到根节点,然后在先序遍历中找到对应的子树。
中序: 9 3 15 20 7 先序: 3 9 20 15 7 后序: 9 15 7 20 3
1class Rebuild {
2 // 通过先序和中序遍历重建二叉树
3 Node *inpre(int l, int r, int rt, vector<int> &preorder, vector<int> &inorder)
4 {
5 if (l > r) return NULL;
6 int i = l;
7 while (inorder[i] != preorder[rt]) i++;
8 Node *p = new Node(preorder[rt]);
9 p->left = inpre(l, i - 1, rt + 1, preorder, inorder);
10 p->right = inpre(i + 1, r, rt + 1 + i - l, preorder, inorder);
11 return p;
12 }
13
14 // 通过后序和中序遍历重建二叉树
15 Node *inpost(int l, int r, int rt, vector<int> &postorder, vector<int> &inorder)
16 {
17 if (l > r) return nullptr;
18 int i = l;
19 while (inorder[i] != postorder[rt]) ++i;
20 Node *p = new Node(postorder[rt]);
21 p->left = inpost(l, i - 1, rt - 1 - r + i, postorder, inorder);
22 p->right = inpost(i + 1, r, rt - 1, postorder, inorder);
23 return p;
24 }
25
26 Node *deduce(vector<int> &preorder, vector<int> &inorder, vector<int> &postorder)
27 {
28 if (preorder.size()) {
29 return inpre(0, preorder.size() - 1, 0, preorder, inorder);
30 }
31 if (postorder.size()) {
32 return inpost(0, postorder.size() - 1, postorder.size() - 1, postorder, inorder);
33 }
34 return nullptr;
35 }
36};
线索二叉树
二叉树的遍历
1 1
2 / \
3 2 3
4 / \ \
5 4 5 6
6 / \
7 7 8
先序遍历: 1 2 4 5 7 8 3 6 中序遍历: 4 2 7 5 8 1 3 6 后序遍历: 4 7 8 5 2 6 3 1 层次遍历: 1 2 3 4 5 6 7 8
1 A
2 / \
3 B C
4 / / \
5 D E F
6 \ \
7 G H
先序遍历: A B D G C E H F 中序遍历: D G B A E H C F 后序遍历: G D B H E F C A 层次遍历: A B C D E F G H
线索二叉树,将NULL指针指向该遍历方法的前驱和后继结点,构建过程即是遍历过程
对于中序遍历来说,找前驱结点,H是C的前驱,C是H的后继,H是C遍历左子树时访问的最后一个结点,即左子树的“最右下端”结点;找后继结点,A是E的前驱,E是A的后继,E是A遍历右子树时访问的第一个结点,即右子树的“最左下端”结点。