L2-006 树的遍历--(已知中序和后序求前序,这里是求层次遍历)

L2-006 树的遍历

树的三种遍历方式的遍历顺序:

先序遍历:根、左子树、右子树(特点:第一个元素为根)
中序遍历:左子树、根、右子树(特点:根的两边分别为左子树和右子树)
后序遍历:左子树、右子树、根(特点:最后一个元素为根)
有如下图的二叉树:
其先序、中序、后序遍历分别为:DBACEGF、ABCDEFG、ACBFGED

1、已知先序和中序求后序
先序遍历的第一个字符为根,因此只需在中序遍历中找到它,就可以把根节点的左子树和右子树分开,就可以知道左子树的字符个数和右子树的字符个数,然后可以确定先序遍历中哪部分是左子树,哪部分是右子树,之后递归先序遍历的序列,直到结束。
如上面的例子:先序遍历的第一个字符是D,则根节点为D,从中序遍历中可以找到D的位置,左边的ABC即为左子树的字符,右边的EFG即为右子树的字符,如果开始递归函数为:build(“DBACEGF”),则找到根的位置后,可以分为递归左子树的先序遍历和递归右子树的先序遍历:build(“BAC”)和build(“EFG”),其对应的中序遍历为:ABC和EFG。然后继续进行以上步骤,直到找完先序序列。每找到根就可以直接输出或保存到数组中,需要注意的是递归的时候不要把根包含在内。
代码如下(已知先序和中序求后序):

#include<stdio.h>
#include
char a[30],b[30];
void fun(int r1, int l1, int r2, int l2)
{{//四个参数分别为:先序遍历的起始位置和结束位置和中序遍历的起始位置和结束位置
int i;
if(r1>l1))//如果已经到达叶子,返回
return;
for(i=r2;b[i]!=a[r1];++i);//找到根节点在中序遍历中的位置
fun(r1+1, l1-l2+i,r2,i-1);//递归求左子树的后序遍历,r1+1是指把根去掉之后的位置
fun(l1-l2+i+1,l1,i+1,l2);//递归求右子树的后序遍历
putchar(a[r1]);//在递归结束后输出根
}
int main(void)
{
int i;
while(scanf("%s%s",a,b)!=EOF)
{
int l=strlen(a);
fun(0,l-1,0,l-1);
puts("");
}
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

2、已知中序和后序求先序
后序序列的特点是最后一个是根,道理与已知先序和中序求后序一样,同样是递归,只是需要改变几个参数。附上代码和注释。
找到根后,分左右。
#include<stdio.h>
#include
char a[30],b[30];
void fun(int r1, int l1, int r2, int l2)
{
int i;
if(r1>l1)
return;
putchar(a[l1]);//由于是求先序遍历,所以要先输出根节点
for(i=r2;b[i]!=a[l1];++i);//对应的,后序序列的最后一个为根,所以根为a[l1],b[i]表示是在中序中找根
fun(r1,l1-l2+i-1,r2,i-1);//递归求左子树的先序遍历
fun(l1-l2+i,l1-1,i+1,l2);//递归求右子树的先序遍历,l1-1为去掉根后的位置
}
int main(void)
{
int i;
while(scanf("%s%s",a,b)!=EOF)
{//先输入后序,在输入中序
int l=strlen(a);
fun(0,l-1,0,l-1);
puts("");
}
return 0;
}

在这里插入图片描述
创建层次遍历。
需要根据下标来实现。
在左右递归的时候,加多一个参数
这个参数作为该结点的位置。
这样的下标会造成浪费
i 的子结点 为 2i+1 和 2i+2

所以数组开最大是 2^n -1
其中有空间不使用

 #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define MAXN 100000
    using namespace std;
     
    int s1[MAXN], s2[MAXN], s3[MAXN];
    int n;
    /*
    	p1 p2 q1 a2 表示两种遍历结果的起始位置
    	index表示当前根节点在二叉树中的位置(层次遍历) 
    */
    void f(int p1, int p2, int q1, int q2, int index)
    {
    	if(p1>p2 || q1>q2) return ;
    	
    	int i = p1 ;
    	while(s1[i] != s2[q2]) i++;
    	s3[index] = s1[i];
    	//left
    	f(p1, i-1, q1, q1+i-1-p1, 2*index+1);
    	//right
     	f(i+1, p2, q1+i-p1, q2-1, 2*index+2);
    	
    }
     
    int main()
    {
    	cin>>n;
    	for(int i=0; i<n; i++) cin>>s2[i];
    	for(int i=0; i<n; i++) cin>>s1[i];
    	memset(s3,0,sizeof(s3));
    	f(0, n-1, 0, n-1, 0);
    	//cnt用来记录有效数字的个数 
    	int cnt = 0; 
    	for(int i=0; i<=100000; i++) {
    		if(cnt==n)break;
    		if(s3[i]){
    			cnt++;
    			if(i)cout<<" ";
    			cout<<s3[i];
    		}
    	}
    	return 0;
    }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
赫夫曼是一种特殊的二叉,它的每个叶子节点都表示一个字符,并且每个字符对应的编码都是唯一的。赫夫曼常用于数据压缩。 赫夫曼的编码方式是按照缀编码进行编码,即一个字符的编码不能是另一个字符编码的缀。例如,如果字符 A 的编码是 10,那么字符 B 的编码不能是 1,因为这个编码是字符 A 的缀。 赫夫曼的解码方式是从根节点开始遍,根据编码的 0 或 1 分别选择左子或右子,直到到达叶子节点,找到对应的字符。 下面是一个赫夫曼的例子: ``` /\ / \ / \ /\ /\ / \ / \ a b c d ``` 假设字符 a 的编码为 0,字符 b 的编码为 10,字符 c 的编码为 110,字符 d 的编码为 111。那么,编码串 010110111 可以被解码成字符串 "abcbd"。 具体的解码过程如下: 1. 从根节点开始遍,根据第一个字符 0,选择左子。 2. 继续遍左子,根据第二个字符 1,选择右子。 3. 继续遍右子,根据第三个字符 0,选择左子。 4. 到达叶子节点,找到字符 a。 5. 回到根节点,根据第四个字符 1,选择右子。 6. 继续遍右子,根据第五个字符 1,选择右子。 7. 继续遍右子,根据第六个字符 0,选择左子。 8. 到达叶子节点,找到字符 b。 9. 回到根节点,根据第七个字符 1,选择右子。 10. 继续遍右子,根据第八个字符 1,选择右子。 11. 到达叶子节点,找到字符 d。 12. 回到根节点,根据第九个字符 1,选择右子。 13. 继续遍右子,根据第十个字符 1,选择右子。 14. 到达叶子节点,找到字符 c。 15. 读取完所有的字符,解码完成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值