树的逻辑很简单:1.除了根节点之外,每个节点都有一个父节点,根节点没有父节点(也可为NULL);2.除了叶节点之外,每个节点都有一个或多个子节点,叶节点没有子节点。3.父节点和子节点之间用指针链接。
树的遍历方式:
1. 前序遍历 VLR
2. 中序遍历 LVR
3. 后序遍历 LRV
在此以重建二叉树为例,了解二叉树的前、中序遍历。
题目描述:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出下图所示的二叉树并输出它的头节点。
二叉树的节点定义如下:
struct BinTreeNode{
int m_value;
BinTreeNode *left;
BinTreeNode *right;
};
解题思路:由于输入的是先序遍历和中序遍历,通过先序遍历我们可以知道根节点为先序遍历的第一个数字,知道根节点后就可以在中序遍历中查找到此节点,并以此节点确定其左侧为左子树,右侧为右子树。最后递归的构建其左右子树。
测试用例:
//层次遍历,借助队列
void levels(BinTreeNode *root){
if(root == NULL)
return;
std::queue<BinTreeNode*> Q;
Q.push(root);
while(!Q.empty()){//如果队列不为空
//赋值x,避免造成死循环
BinTreeNode *x = Q.front();
//先访问根节点(队首)的值
std::cout << x->m_value << " ";
//访问后删除队首节点
Q.pop();
//如果有左孩子
if(x->left)
Q.push(x->left);//入队
//如果有右孩子
if(x->right)
Q.push(x->right);//入队
}
}
//测试用例
int main(){
//先序遍历序列
int preOrder[8] = {1, 2, 4, 7, 3, 5, 6, 8};
//中序遍历序列
int inOrder[8] = {4, 7, 2, 1, 5, 3, 8, 6};
//重建后返回的结果
BinTreeNode *root = new BinTreeNode;
root = NULL;
root = constructBinTree(preOrder, inOrder, 8);
//打印重建后的二叉树,层次遍历
levels(root);//Output:1, 2, 3, 4, 5, 6, 7, 8
return 0;
}
constructBinTree函数实现:
//函数参数依次为:先序数组首地址, 先序数组尾地址, 中序数组首地址, 中序数组尾地址
BinTreeNode* constructCore(int pre[], int endPre[], int in[], int endIn[]){
//先序遍历第一个节点必然是根结点
int rootValue = pre[0];
//这时可以创建二叉树树根
BinTreeNode* root = new BinTreeNode;
root->m_value = rootValue;
root->left = NULL;
root->right = NULL;
//判段是否只有一个根节点值
if(pre == endPre){
if(in == endIn && *pre == *in)
return root;
else//先序和中序值不一致
throw new std::exception("Invalid input.");
}
//在中序遍历中查找根节点
int *rootInOrder = in; //因为会执行递增操作,所以要创建一个副本,在副本进行
while(rootInOrder != endIn && *rootInOrder != rootValue){//直到遇到根节点值停止
++rootInOrder;
}
//遍历后,作此判断。若中序遍历中没有找到这个根值,则表示输入非法
if(rootInOrder == endIn && *rootInOrder != rootValue)
throw new std::exception("Invalid input.");
//若至此,在中序遍历找到根节点后,其左侧是左子树,右侧即右子树
//求出左侧长度
int leftLength = rootInOrder - in;
//在先序遍历中,找到左子树节点结束的位置 = 先序开始 + 左侧长度(中序求出的)
int *leftPreOrderEnd = pre + leftLength;
//递归的构建二叉树的左右子树
if(leftLength > 0)//构建左子树传入值:先序开始位置,先序左子树结束位置,中序开始位置,中序左子树结束位置-1
root->left = constructCore(pre+1, leftPreOrderEnd, in, rootInOrder-1);
if(leftLength < endPre - pre)//构建右子树传入值:先序右子树开始位置, 先序结束位置,中序右子树开始位置
root->right = constructCore(leftPreOrderEnd+1, endPre, rootInOrder+1, endIn);
return root;
}
BinTreeNode* constructBinTree(int pre[], int in[], int length){
//判断是否为空及长度是否小于等于0
if(pre == NULL || in == NULL || length <= 0)
return NULL;
//使用辅助函数, 核心构造代码
return constructCore(pre, pre + length-1, in, in + length-1);
}