抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

构造


前序和中序可以唯一确定一棵二叉树。

后序和中序可以唯一确定一棵二叉树。

前序和后序不能唯一确定一棵二叉树!

没中序遍历就无法分割了。

前序遍历&中序遍历构造

分析

首先要牢记各个遍历方法的顺序。

前序遍历是:根节点->左子树->右子树
中序遍历是:左子树->根节点->右子树

所以前序遍历的第一个元素就是整个二叉树的根节点。
同理,在中序遍历中找到这个元素,它的左边就是左子树,右边就是右子树。

前序遍历的第二个元素就是根节点的左子结点。
同样它也作为一个分割点,把中序遍历分成左子树和右子树。
例如分成[1,4,2],[7,6,8]

前序遍历怎么切割呢?可以发现只需要把它分成与中序遍历数组一样长度的的两块就行了。
例如[4,1,2] (三个元素), [6,7,8] (三个元素)

……

如此循环,我们就可以用递归写出代码了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
TreeNode* traversal(vector<int>& preorder, int preBegin, int preEnd, vector<int>& inorder, int inBegin, int inEnd) {
if (preBegin == preEnd) return nullptr;

//找到当前根节点
int rootValue = preorder[preBegin];
TreeNode* node = new TreeNode(rootValue);

//判断是否为叶子节点
if (preEnd - preBegin == 1) return node;

//在中序遍历中找到对应节点
int divideNode;
for (divideNode = 0; divideNode <= inEnd; ++divideNode)
if (inorder[divideNode] == rootValue)
break;

//左开右闭区间分割 [)
//分割中序数组
int leftInorderBegin = inBegin;
int leftInorderEnd = divideNode;

int rightInorderBegin = divideNode + 1;
int rightInorderEnd = inEnd;

//分割前序数组
int leftPreorderBegin = preBegin + 1;
int leftPreorderEnd = preBegin + (leftInorderEnd - leftInorderBegin) + 1;

int rightPreorderBegin = leftPreorderEnd;
int rightPreorderEnd = preEnd;

//递归
node->left = traversal(preorder, leftPreorderBegin, leftPreorderEnd, inorder, leftInorderBegin, leftInorderEnd);
node->right = traversal(preorder, rightPreorderBegin, rightPreorderEnd, inorder, rightInorderBegin, rightInorderEnd);

return node;
}

中序遍历&后序遍历构造

分析

这样子构造跟上面的前序+中序构造差别不大。

只需注意后序遍历中根节点是在最后的。

后序遍历:左子树->右子树->根节点

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
TreeNode* traversal(vector<int>& postorder, int postorderBegin, int postorderEnd, vector<int>& inorder, int inBegin, int inEnd) {
if (postorderBegin == postorderEnd) return nullptr;

//找到当前根节点
int rootValue = postorder[postorderEnd - 1];
TreeNode* node = new TreeNode(rootValue);

//判断是否为叶子节点
if (postorderEnd - postorderBegin == 1) return node;

//在中序遍历中找到对应节点
int divideNode;
for (divideNode = 0; divideNode <= inEnd; ++divideNode)
if (inorder[divideNode] == rootValue)
break;

//左开右闭区间分割 [)
//分割中序数组
int leftInorderBegin = inBegin;
int leftInorderEnd = divideNode;

int rightInorderBegin = divideNode + 1;
int rightInorderEnd = inEnd;

//分割后序数组
int leftpostorderBegin = postorderBegin;
int leftpostorderEnd = postorderBegin + (leftInorderEnd - leftInorderBegin);

int rightpostorderBegin = leftpostorderEnd;
int rightpostorderEnd = postorderEnd - 1;

//递归
node->left = traversal(postorder, leftpostorderBegin, leftpostorderEnd, inorder, leftInorderBegin, leftInorderEnd);
node->right = traversal(postorder, rightpostorderBegin, rightpostorderEnd, inorder, rightInorderBegin, rightInorderEnd);

return node;
}

彩蛋

画二叉树

1
2
3
4
5
6
7
8
9
10
void printTree(TreeNode* root, int level = 0, int indent = 4) {
if (!root) return;

printTree(root->right, level + 1, indent);
if (level != 0) {
cout << setw(level * indent) << " ";
}
cout << root->val << endl;
printTree(root->left, level + 1, indent);
}

效果

明天再做插入删除的代码吧。

未完待续…

评论