前序中序确定二叉树

1.图形说明

2.函数代码

题目:
二叉树的先序:ABDECFGH
二叉树的中序:DBEACGFH

我们每次,要在先序序列里找到头结点,然后依靠这个头结点把中序序列分为左右子树,又依靠找出的左右子树在前序序列中找到它们的头结点,依此类推

有更高级的做法,这里用建树的做法
其实建树大致分为三个步骤
一.找到每次子树的头结点
二.让该结点的左孩子指针指向结点的左子树
三.让该结点的右孩子指针指向结点的右子树

听起来像废话,但思路就是这么简单。难就难在这子树压根就不存在,现在是一地玻璃渣,你让我的两个孩子指针指谁去。所以,这里就发挥递归的妙用了。

我觉得递归有个特别好的特点,就是假装它已经存在了,假装它的左右子树都已建好,但实际上现在的这层函数运行到这里时,它的左子树和右子树才刚刚开始建。

首先我们找到树的根,然后连接它的左子树和右子树,它的左子树又去找左子树自己的根,再连接左子树的左子树和右子树,它的右子树又去找右子树自己的根,再连接右子树的左子树和右子树。依次类推,很明显的递归是不是。

写好递归的关键,个人觉得就是找准变量,每一次递归虽然做的都是同一件事,但它们的数据却必有差异,不然,做的事一样,原材料也一样,无缘无故原地踏步踏?当然得数据不一样,函数才好往前走

那么,如何找准变量呢,就以这道题为例

首先,每次函数都需要两个字符串,也就是前序序列和中序序列对吧,那么我们就先设两个字符串指针 char *pre,char *in
然后,看看我们上面提到的第一步,要找结点对不对,结点的数组下标i是变化的,但是我们建树第一步的时候,很容易就知道根是前序序列的第一个对吧。我们再想象一下,在不断的递归中,要处理的序列长度是不是越来越小了,所以,序列长度就是我们要找的另一个变量
在这里插入图片描述
第一次处理的时候,两个序列都是0到7,而第二次的时候,显然,左子树的两个序列分别是1到3和0到2,右子树的两个序列都是4到7。

这里我们也很容易看出每次序列都被分为两部分,而这两部分的字符串都是连续的,这就好办了。我们先设先序序列开头字符的数组下标设为L1,尾部字符的数组下标设为R1。中序序列开头字符的数组下标设为L2,尾部字符的数组下标设为R2

我们每次,要在先序序列里找到头结点,然后依靠这个头结点把中序序列分为左右子树,又依靠找出的左右子树在前序序列中找到它们的头结点,依此类推

我们容易发现,每次找出的结点的数组下标 i 和在前序序列,中序序列中,可以直接推出R1,L1,R2,L2的大小,再上张图,先以左子树为例
在这里插入图片描述
很显然
L2次=L2(中序遍历,序列最左边的肯定是左子树最左下方的字符)
R2次=i-1
L1次=L1+1(毕竟前序找元素都是找头)
R2次=L1次+i-L2-1=L1次-1+i-L2=L1+i-L2(序列是连续的,所以在中序序列中找出序列长度,然后就方便写出L1次,L2次了)
再看右子树,一样的道理,看图
在这里插入图片描述
显然,
L2次=i+1
R2次=R2(中序序列,序列最右端的字符是右子树最右下方的元素)
L1次=左子树序列的尾字符的数组下标+1(就像C的是E的+1一样)=L1+i-L2+1
R1次=R1(对前序序列的上一次处理是找头,再中间划一刀,所以右子树操作时其尾部不变)
好了,终于可以上代码了

typedef struct bitnode
{
	int data;
	struct bitnode *lchild,*rchild;
}bitnode,*bitree;

bitree createbt(char *pre,char *in,int L1,int R1,int L2,int R2)
{
	if(L1>R1)return NULL;
	bitree s;
	s=new bitnode;
	s->lchild=s->rchild=NULL;
	s->data=pre[L1];
	int i;
	for(i=L2;i<=R2;i++)
	if(in[i]==pre[L1])break;
	s->lchild=createbt(pre,in,L1+i-L2,L2,i-1);
	s->rchild=createbt(pre,in,L1+i-L2+1,R1,i+1,R2);
	return s;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
二叉树前序遍历和中序遍历可以确定一棵二叉树,因此可以通过已知的前序遍历和中序遍历来构建出一棵二叉树。而求解二叉树的后序遍历则需要使用递归来实现。 具体的算法流程如下: 1. 如果前序遍历序列和中序遍历序列为空,则返回空节点; 2. 取前序遍历序列的第一个元素作为根节点; 3. 在中序遍历序列中找到根节点,确定左子树和右子树的中序遍历序列; 4. 根据左子树的中序遍历序列和前序遍历序列递归构建左子树; 5. 根据右子树的中序遍历序列和前序遍历序列递归构建右子树; 6. 将根节点加入后序遍历序列中; 7. 返回根节点。 下面是代码实现: ``` #include <iostream> #include <vector> using namespace std; // 二叉树节点结构体 struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; // 根据前序遍历序列和中序遍历序列构建二叉树 TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { if (preorder.empty() || inorder.empty()) { return nullptr; } // 取前序遍历序列的第一个元素作为根节点 int root_val = preorder[0]; TreeNode* root = new TreeNode(root_val); // 在中序遍历序列中找到根节点,确定左子树和右子树的中序遍历序列 int root_idx = 0; for (int i = 0; i < inorder.size(); i++) { if (inorder[i] == root_val) { root_idx = i; break; } } vector<int> left_inorder(inorder.begin(), inorder.begin() + root_idx); vector<int> right_inorder(inorder.begin() + root_idx + 1, inorder.end()); // 根据左子树的中序遍历序列和前序遍历序列递归构建左子树 vector<int> left_preorder(preorder.begin() + 1, preorder.begin() + 1 + left_inorder.size()); root->left = buildTree(left_preorder, left_inorder); // 根据右子树的中序遍历序列和前序遍历序列递归构建右子树 vector<int> right_preorder(preorder.begin() + 1 + left_inorder.size(), preorder.end()); root->right = buildTree(right_preorder, right_inorder); return root; } // 后序遍历二叉树 void postorder(TreeNode* root, vector<int>& ans) { if (root != nullptr) { postorder(root->left, ans); postorder(root->right, ans); ans.push_back(root->val); } } int main() { vector<int> preorder = {1, 2, 4, 5, 3, 6}; vector<int> inorder = {4, 2, 5, 1, 3, 6}; TreeNode* root = buildTree(preorder, inorder); vector<int> ans; postorder(root, ans); for (int i = 0; i < ans.size(); i++) { cout << ans[i] << " "; } cout << endl; return 0; } ``` 输出结果为: ``` 4 5 2 6 3 1 ``` 这就是二叉树的后序遍历序列。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值