经典二叉树遍历问题_仅指我个人的定义
1.第1类(给遍历,构造树)
(1)从中序与前序遍历构造二叉树
LeetCode 105. 从前序与中序遍历序列构造二叉树
①代码少,但性能一般
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.size() == 0) return NULL;
TreeNode *root = new TreeNode(preorder[0]);
//左右子树的前序遍历和中序遍历;
auto mid = find(inorder.begin(), inorder.end(), preorder[0]);
vector<int> leftPreorder, leftInorder, rightPreorder, rightInorder;
//左子树的中序遍历
leftInorder.assign(inorder.begin(), mid);
//左子树的前序遍历
leftPreorder.assign(preorder.begin() + 1, preorder.begin() + 1 + leftInorder.size());
//右子树的前序遍历
rightPreorder.assign(preorder.begin() + 1 + leftInorder.size(), preorder.end());
//右子树的中序遍历
rightInorder.assign(mid + 1, inorder.end());
root->left = buildTree(leftPreorder, leftInorder);
root->right = buildTree(rightPreorder, rightInorder);
return root;
}
};
②性能稍好,更常规的写法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* myBuildTree(const vector<int>& preorder, int headP, int tailP, const vector<int> &inorder, int headI, int tailI) {
if(headP > tailP) return NULL;
TreeNode *root = new TreeNode(preorder[headP]);
//中序遍历中,根节点的下标
int mid;
for(int i = headI; i <= tailI; i++) {
if(inorder[i] == preorder[headP]) {
mid = i;
break;
}
}
//左子树
root->left = myBuildTree(preorder, headP + 1, headP + mid - headI, inorder, headI, mid - 1);
//右子树
root->right = myBuildTree(preorder, headP + mid - headI + 1, tailP, inorder, mid + 1, tailI);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.size() == 0) return NULL;
TreeNode *root = myBuildTree(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1);
return root;
}
};
(2)从中序与后序遍历构造二叉树
LeetCode 106. 从中序与后序遍历序列构造二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* myBuildTree(const vector<int>& inorder, int headI, int tailI, const vector<int>& postorder, int headP, int tailP) {
//递归边界
if(headI > tailI) return NULL;
//递归式
TreeNode *root = new TreeNode(postorder[tailP]);
int mid; //中序遍历中,根节点的下标
for(int i = headI; i<= tailI; i++) {
if(inorder[i] == postorder[tailP]) {
mid = i;
break;
}
}
int leftTreeNodeNumber = mid - headI;
//左子树 中序遍历 后序遍历
root->left = myBuildTree(inorder, headI, mid - 1, postorder, headP, headP + leftTreeNodeNumber - 1);
root->right = myBuildTree(inorder, mid + 1, tailI, postorder, headP + leftTreeNodeNumber, tailP - 1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.size() == 0) return NULL;
TreeNode *root = myBuildTree(inorder, 0, inorder.size() - 1, postorder, 0, postorder.size() - 1);
return root;
}
};
(3)从中序与层序遍历构造二叉树
//从中序与层序遍历构造二叉树
#include<cstdio>
#include <vector>
using namespace std;
typedef unsigned int UL;
//Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
TreeNode* myBuildTree(const vector<int>& inorder, int headI, int tailI, const vector<int> &levelorder, int headL, int tailL) {
if(headI> tailI) return NULL;
TreeNode *root = new TreeNode(levelorder[headL]);
//中序遍历中,根节点的下标
int mid = -1;
for(int i = headI; i <= tailI; i++) {
if(inorder[i] == levelorder[headL]) {
mid = i;
break;
}
}
UL leftTreeNodeNUmber = mid - headI;
UL rightTreeNodeNumber = inorder.size() - leftTreeNodeNUmber - 1;
//构建左子树的层序遍历
vector<int> leftLevelOrder;
bool flag = false; //还未找全;
for (int i = headL; i <= tailL; i++) {
for (int j = headI; j <= mid - 1; j++) {
if(levelorder[i] == inorder[j]) { //既然左子树的层序遍历是分散的,根据左子树的中序遍历,可以把分散的收集到vector中;
leftLevelOrder.push_back(levelorder[i]);
if(leftLevelOrder.size() == (leftTreeNodeNUmber))
flag = true; //找全了
break;
}
}
if(flag)
break;
}
//构建右子树的层序遍历
vector<int> rightLevelOrder;
flag = false; //还未找全;
for (int i = headL; i <= tailL; i++) {
for (int j = mid + 1; j <= tailI; j++) {
if(levelorder[i] == inorder[j]) {
rightLevelOrder.push_back(levelorder[i]);
if(rightLevelOrder.size() == rightTreeNodeNumber) {
flag = true; //找全了
break;
}
}
}
if(flag)
break;
}
//左子树
root->left = myBuildTree(inorder, headI, mid - 1, leftLevelOrder, 0, leftTreeNodeNUmber - 1);
//右子树
root->right = myBuildTree(inorder, mid + 1, tailI, rightLevelOrder, 0, rightTreeNodeNumber - 1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& levelorder) {
if(inorder.size() == 0) return NULL;
TreeNode *root = myBuildTree(inorder, 0, inorder.size() - 1, levelorder, 0, levelorder.size() - 1);
return root;
}
void preOrder(TreeNode *root) {
if(root == NULL)
return;
printf("%d ", root->val);
preOrder(root->left);
preOrder(root->right);
}
int main() {
//测试
//input
vector<int> inorder = {7, 5, 4, 2, 8, 6, 9}, levelorder = {4, 5, 6, 7, 8, 9, 2};
TreeNode *root = buildTree(inorder, levelorder);
//前序遍历验证
preOrder(root);
return 0;
}
总结:(1)~(2)思路一致(注释已说明),(3)多了一个步骤:根据子树的中序遍历和树的层序遍历求子树的层序遍历(求法见代码)。
(1)~(2)难度相当,(3)较难点
2.第2类(给遍历,求遍历)
(1)中序遍历 + 前序遍历 -> 后序遍历 || 层序遍历
①求后序遍历
void postOrder(vector<int>& postorder, const vector<int>& inorder, int headI, int tailI, const vector<int>& preorder, int headP, int tailP) {
//递归边界;
if(headI > tailI)
return;
//找中序遍历中根节点的下标
int mid;
for (int i = headI; i <= tailI; i++) {
if(inorder[i] == preorder[headP]) {
mid = i;
break;
}
}
int leftTreeNodeNumber = mid - headI;
//后序遍历
//左子树
postOrder(postorder, inorder, headI, mid - 1, preorder, headP + 1, headP + leftTreeNodeNumber);
//右子树
postOrder(postorder, inorder, mid + 1, tailI, preorder, headP + leftTreeNodeNumber + 1, tailP);
postorder.push_back(preorder[headP]);
}
②求层序遍历(本文:先建树(见上文[1.(1)])后层序遍历)
(2)中序遍历 + 后序遍历 -> 前序遍历 || 层序遍历
①前序遍历
void preOrder(vector<int>& preorder, const vector<int>& inorder, int headI, int tailI, const vector<int>& postorder, int headP, int tailP) {
//递归边界;
if(headI > tailI)
return;
//找中序遍历中根节点的下标
int mid = -1;
for (int i = headI; i <= tailI; i++) {
if(inorder[i] == postorder[tailP]) {
mid = i;
break;
}
}
int leftTreeNodeNumber = mid - headI;
//前序遍历
preorder.push_back(postorder[tailP]);
//左子树
preOrder(preorder, inorder, headI, mid - 1, postorder, headP, headP + leftTreeNodeNumber - 1);
//右子树
preOrder(preorder, inorder, mid + 1, tailI, postorder, headP + leftTreeNodeNumber, tailP - 1);
}
②求层序遍历(本文:先建树(见上文[1.(2)])后层序遍历)
(3)中序遍历 + 层序遍历 -> 前序遍历 || 后序遍历
①求前序遍历
//前序遍历
void preOrder(vector<int>& preorder, const vector<int>& inorder, int headI, int tailI, const vector<int> &levelorder, int headL, int tailL) {
if(headI> tailI) return;
//中序遍历中,根节点的下标
int mid = -1;
for(int i = headI; i <= tailI; i++) {
if(inorder[i] == levelorder[headL]) {
mid = i;
break;
}
}
UL leftTreeNodeNumber = mid - headI;
UL rightTreeNodeNumber = inorder.size() - leftTreeNodeNumber - 1;
//构建左子树的层序遍历
vector<int> leftLevelOrder;
bool flag = false; //还未找全;
for (int i = headL; i <= tailL; i++) {
for (int j = headI; j <= mid - 1; j++) {
if(levelorder[i] == inorder[j]) { //既然左子树的层序遍历是分散的,根据左子树的中序遍历,可以把分散的收集到vector中;
leftLevelOrder.push_back(levelorder[i]);
if(leftLevelOrder.size() == (leftTreeNodeNumber))
flag = true; //找全了
break;
}
}
if(flag)
break;
}
//构建右子树的层序遍历
vector<int> rightLevelOrder;
flag = false; //还未找全;
for (int i = headL; i <= tailL; i++) {
for (int j = mid + 1; j <= tailI; j++) {
if(levelorder[i] == inorder[j]) {
rightLevelOrder.push_back(levelorder[i]);
if(rightLevelOrder.size() == rightTreeNodeNumber) {
flag = true; //找全了
break;
}
}
}
if(flag)
break;
}
//前序遍历
preorder.push_back(levelorder[headL]);
preOrder(preorder, inorder, headI, mid - 1, leftLevelOrder, 0, leftTreeNodeNumber - 1);
preOrder(preorder, inorder, mid + 1, tailI, rightLevelOrder, 0, rightTreeNodeNumber - 1);
}
②后序遍历
和上面①前序遍历代码几乎一致,最大的差别仅在最后三行,即:①是前序遍历,②是后序遍历)
//后序遍历
void postOrder(vector<int>& postorder, const vector<int>& inorder, int headI, int tailI, const vector<int> &levelorder, int headL, int tailL) {
if(headI> tailI) return;
//中序遍历中,根节点的下标
int mid = -1;
for(int i = headI; i <= tailI; i++) {
if(inorder[i] == levelorder[headL]) {
mid = i;
break;
}
}
UL leftTreeNodeNumber = mid - headI;
UL rightTreeNodeNumber = inorder.size() - leftTreeNodeNumber - 1;
//构建左子树的层序遍历
vector<int> leftLevelOrder;
bool flag = false; //还未找全;
for (int i = headL; i <= tailL; i++) {
for (int j = headI; j <= mid - 1; j++) {
if(levelorder[i] == inorder[j]) { //既然左子树的层序遍历是分散的,根据左子树的中序遍历,可以把分散的收集到vector中;
leftLevelOrder.push_back(levelorder[i]);
if(leftLevelOrder.size() == (leftTreeNodeNumber))
flag = true; //找全了
break;
}
}
if(flag)
break;
}
//构建右子树的层序遍历
vector<int> rightLevelOrder;
flag = false; //还未找全;
for (int i = headL; i <= tailL; i++) {
for (int j = mid + 1; j <= tailI; j++) {
if(levelorder[i] == inorder[j]) {
rightLevelOrder.push_back(levelorder[i]);
if(rightLevelOrder.size() == rightTreeNodeNumber) {
flag = true; //找全了
break;
}
}
}
if(flag)
break;
}
//后序遍历
postOrder(postorder, inorder, headI, mid - 1, leftLevelOrder, 0, leftTreeNodeNumber - 1);
postOrder(postorder, inorder, mid + 1, tailI, rightLevelOrder, 0, rightTreeNodeNumber - 1);
postorder.push_back(levelorder[headL]);
}
总结:思路和1.(3)几乎一致,只不过需求不同,一个是建树,一个仅需把遍历结果存储在vector中。
3.总结
①第1类,会(1)便会(2),反而(3)需要留心一下;
②第2类,同样是会(1)便会(2),而且思路和第1类是一致的,只是需求不同,第1类是建树,第2类是仅需把遍历结果存储在vector中即可。反而求层序遍历需要留心一下,本文采用先构建树再层序遍历的方式,不建树的方式见给定二叉树的中序遍历和后序遍历,不建树求其层序遍历(PAT A1020)。
③对于(3),思路和第1类的(3)相同,也只是需求不同。