数据结构第三讲学习笔记
浙江大学慕课数据结构第三章树
二叉树
有左右之分的度为2的树,有三种结点(孩子数0,1,2),树的边N=结点树M-1。因此n0+n1+n2 -1= n1+2*n2,这样叶子的数就等于孩子数为2结点数+1。
用数组表示二叉树
可以先将二叉树按层次顺序编号,结点i左孩子序号为2i,但是对缺的结点会造成空间浪费.
用链表表示
是较好的方式
left_child | data | right_child |
---|
在遍历二叉树的过程可以看作从父节点进入树递归的过程,每个父结点都有三次遍历到的机会,而先,中,后序可以看作第几次输出的过程(1,2,3)。
视频中讲述的中序遍历的非递归方法实际上也是递归的一种思想,递归本就可以看作是把上层函数存在堆栈中,等下层调用完成后再重新出栈。后序实际上是深度遍历,而层序遍历实际是广度遍历。
在遍历表达式树的时候通过先,中,后序方法可以得到先,中,后缀表达式(中缀需要加括号)。
课后练习:03-树3 Tree Traversals Again
题目给出了一个中序遍历二叉树所用栈的出栈入栈过程,需要输出树的后序遍历顺序,而这个出栈入栈的过程实际上暗藏了先序遍历的顺序(入栈的顺序),因为之前说过每个父结点都有三次遍历到的机会,而先,中,后序可以看作第几次遍历(1,2,3)的时候输出。所以按照递归的思想可以通过先序和中序求后序:
标准答案C++代码如下
#include<cstdio>
#include<stack>
#include<string>
#include<iostream>
using namespace std;
const int maxn=100;
int pre[maxn], in[maxn], post[maxn];
void solve(int PreL, int inL, int postL, int n){
if(n==0)return;
if(n==1){
post[postL]=pre[PreL];
return;
}
int i;
int root=pre[PreL];
post[postL+n-1]=root;
for(i=0; i<n; i++){
if(in[inL+i]==root)break;
}
int L=i, R=n-i-1;
solve(PreL+1, inL, postL, L);//1,0,0,3
solve(PreL+1+L, inL+L+1, postL+L, R);//4,4,3,2
}
int main(){
int n, num, i=0, j=0;
scanf("%d", &n);
string s;
stack<int> st;
for(int z=0; z<2*n; z++){
cin>>s;
if(s=="Push"){
scanf("%d\n", &num);
st.push(num);
pre[i++]=num;//1 234 56
}else{
num=st.top();
st.pop();
in[j++]=num;//324 1 65
}
}
solve(0,0,0,n);//root pos in pre, tree pos in mid,first pos of tree,num of tree node
for(int i=0; i<n; i++){
printf("%d", post[i]);
if(i!=n-1)printf(" ");
}
return 0;
}
首先通过两个数组收集了先序和中序的结点顺序,而后序的求法只需要按左子树->右子树->父结点顺序递归完成即可。过程就是先确定子树在后置数组中结点的存放范围【a,b】,a是左子树的左节点存放位置,b是root存放的位置,这里可以看见solve函数有四个参数,其中n就是子树所包含结点的个数,PreL是在前序数组中子树root的位置, inL是是子树的root在中序数组中的位置, postL就是上面的a,而b就是a+n-1。这样递归就相当于为每个结点分配好了数组中的位置,最后将后序数组打印出来即可。