二叉树遍历问题整理

二叉树遍历问题整理

如何建立二叉树?

我们可以通过已知先序和中序遍历或者后序和中序遍历建立起一颗二叉树。

代码及详细注释:

通过先序和中序建立二叉树:

#include 
#include 
#include 
using namespace std;
int n;	//存结点个数
string pre, in;	//pre:存先序遍历 in:存中序遍历
树结构体:btnode:树的内容  btree:指向树的指针
typedef struct node{
    char data;	//树的数据类型(当然可以自己定义)
    node *l, *r;	//树的左右孩子,通过指针指向他们的地址
}btnode, *btree;

//构建二叉树(参数:root 当前树的根节点(由先序遍历最开头的位置得出)  
//start:当前树的中序遍历的左端点   end:当前树的中序遍历的右端点)
btree build(int root, int start, int end){
    if(start > end)return NULL;	//遍历到叶子结点的左右孩子,返回NULL
    //在当前树的中序遍历中找根的位置(该树的根可以由先序遍历得出)
    int pos = start;	
    while(in[pos] != pre[root])pos++;		
    btree t = new btnode;	//为当前指针指向的地址建立一个新的节点
    t->data = pre[root];	//将根节点的值赋给当前节点
    //节点的左右孩子都是通过函数递归返回的时候建立连接起来的
    t->l = build(root + 1, start, pos - 1);
    t->r = build(root + (pos - start) + 1, pos + 1, end);
    return t;
}

int main()
{
    btree root;	//先建立一个指向二叉树根节点的指针
    cin>>n;
    cin>>pre>>in;
    root = build(0, 0, n - 1);
    return 0;
}

通过后序和中序建立二叉树:

#include 
#include 
#include 
using namespace std;
int n;
string post, in;	//post:存后序遍历 in:存中序遍历
typedef struct node{
    char data;
    node *l, *r;
}btnode, *btree;

//构建二叉树
btree build(int root, int start, int end){
    if(start > end)return NULL;
    int pos = start;
    while(in[pos] != post[root])pos++;
    btree t = new btnode;
    t->data = post[root];
    t->l = build(root - (end - pos) - 1, start, pos - 1);
    t->r = build(root - 1, pos + 1, end);
    return t;
}
int main()
{
    btree root;
    cin>>n;
    cin>>post>>in;
    root = build(n - 1, 0, n - 1);
    return 0;
}

对建立起的二叉树的一些操作:

//先序遍历: 
void fstorder(btree root){
	if(root != NULL){
		cout<<tmp->data<<" ";
		fstorder(root->l);
		fstorder(root->r);
	}
} 
//中序遍历: 
void middleorder(btree root){
	if(root != NULL){
		middleorder(root->l);
		cout<<tmp->data<<" ";
		middleorder(root->r);
	}			
}
//后序遍历: 
void lastorder(btree root){
	if(root != NULL){
		lastorder(root->l);
		lastorder(root->r);
		cout<<tmp->data<<" ";
	}		
} 
//按层次遍历
void levelorder(btree root){
    queue<btree>qu;
    qu.push(root);
    while(qu.size()){
        btree tmp = qu.front();
        qu.pop();
        cout<<tmp->data<<" ";
        if(tmp->l != NULL)qu.push(tmp->l);
        if(tmp->r != NULL)qu.push(tmp->r);
    }
}

//统计二叉树中叶子结点(没有孩子的结点)的总数:
void numofleadf(btree root,int &m){
	if(root != NULL){
		if(root->l == NULL && root->r == NULL)m ++;
		numofleadf(root->l, m);
		numofleadf(root->r, m);
	}
} 

//求树深 
int fstdepth(btree root){
	if(root == NULL)return 0;
	else{
		int dl=fstdepth(root->l);
		int dr=fstdepth(root->r);
		return 1+max(dl,dr);
	}
} 

//统计二叉树结点个数
int NodeCount ( btree root){
	int cnt = 0;
	if(root == NULL)return 0;
	else{
		int dl=NodeCount(root -> l);
		int dr=NodeCount(root -> r);
		return cnt=dl+dr+1;	
	}
}

问题整理:

一、知二叉树先序和中序遍历求后序遍历

二叉树遍历问题整理_第1张图片

方一、直接递归找后序遍历
分析:

知道二叉树的先序和中序遍历,求其后序遍历:

​ 对于先序遍历的第一个元素是整个二叉树的根节点,对应的,我们可以在中序遍历中找到该元素的位置,因为中序遍历的顺序是先左节点,再根节点,再是右节点,所以在中序遍历中,该元素位置左边的元素都是该根节点左子树中的元素,该元素位置右边的元素都是该根节点右子树中的元素,同样也可以根据左右元素的数量知道左子树和右子树元素的数量。因为对于先序遍历,是按照根左右的顺序,所以我们可以在先序遍历中分别找出左子树和右子树来,在先序遍历中,当前根节点后面紧接着长度为左子树元素数量的元素都是左子树中的元素,且第一个元素是左子树的根节点,同理在左子树元素后长度为右子树元素数量的元素是右子树的元素,且开头就是该右子树的根节点,求出这两棵子树的根节点,重复递归执行上述操作(在先序遍历中都可以找到一段是左右子树的先序遍历),每次到无法递归时候,就递归返回,并输出当前元素的值,该返回的值的顺序就是对二叉树后序遍历的顺序。

为什么这样返回的值就是后序遍历顺序?

​ 因为我们是根据先序遍历和中序遍历每次都把一颗二叉树分成左右子树,然后去递归它的左右子树,直到不能递归了,就从其左右子树返回上来,并输出当前元素的值,这不就是后序遍历的顺序,先左节点,再右节点,最后根节点。

代码:
#include 
#include 
#include 
using namespace std;
int n;
string pre, in;
void solve(int l1, int r1, int l2, int r2){
    //递归到叶子结点,就递归返回
    if(l1 > r1)return;
   
    //在中序遍历中找到该树的根节点
    int pos = l2;
    while(in[pos] != pre[l1])pos++;
    //遍历左子树
    //更新先序遍历为当前树的左子树:左子树的元素范围为[l1 + 1, l1 + pos - l2 + 1 - 1]   左子树的元素数量为pos - l2(根据中序树根位置得出)
    //更新中序遍历为当前数的左子树:左子树的元素范围为[l2, pos - 1]   不难理解,就是取中序遍历中当前树根的左边元素
    solve(l1 + 1, pos - l2 + l1, l2, pos - 1);
    //遍历右子树
    //更新先序遍历为当前树的右子树:右子树的元素范围为[r1 - (r2 - pos) + 1, r1]   右子树的元素数量为r2 - pos(根据中序树根位置得出)
    //更新中序遍历为当前数的右子树:右子树的元素范围为[pos + 1, r2]   不难理解,就是取中序遍历中当前树根的右边元素
    solve(r1 - (r2 - pos) + 1, r1, pos + 1, r2);
    cout<<pre[l1];	//后序遍历,在递归返回的时候输出
}
int main()
{
    cin>>n;
    cin>>pre>>in;
    solve(0, n - 1, 0, n - 1);
    return 0;
}
/*
知道二叉树的先序和中序遍历,求后序遍历

思路:

对于先序遍历来说,第一个遍历到的肯定是该数的根结点,在中序中找根的位置,然后就可以区分左右子树,然后不断递归
左右子树,直到叶子结点,输出结点信息,递归返回的时候输出他们的根结点,即可得到后序遍历。

*/
运行结果

请添加图片描述

优化代码:
#include 
#include 
#include 
using namespace std;
int n;
string pre, in;
void solve(int root, int l1, int r1){
    if(l1 > r1)return;
    int pos = l1;
    while(in[pos] != pre[root])pos++;
    solve(root + 1, l1, pos - 1);
    solve(root + (pos - l1) + 1, pos + 1, r1);
    cout<<pre[root];	
}
int main()
{
    cin>>n;
    cin>>pre>>in;
    solve(0, 0, n - 1);
    return 0;
}
方二、通过先序和后序建立二叉树,然后再去求后序遍历
#include 
#include 
#include 
using namespace std;
int n;
string pre, in;
typedef struct node{
    char data;
    node *l, *r;
}btnode, *btree;

//构建二叉树
btree build(int root, int start, int end){
    if(start > end)return NULL;
    int pos = start;
    while(in[pos] != pre[root])pos++;
    btree t = new btnode;
    t->data = pre[root];
    t->l = build(root + 1, start, pos - 1);
    t->r = build(root + (pos - start) + 1, pos + 1, end);
    return t;
}
void lastorder(btree root){
    if(root != NULL){
        lastorder(root->l);
        lastorder(root->r);
        cout<<root->data;
    }
}
int main()
{
    btree root;
    cin>>n;
    cin>>pre>>in;
    root = build(0, 0, n - 1);
    lastorder(root);
    return 0;
}

二、知二叉树后序和中序遍历求先序遍历

方一、直接递归找先序遍历
分析:

二叉树遍历问题整理_第2张图片

知道二叉树的后序和中序遍历,求其先序遍历:

​ 原理其实和知道二叉树的先序和中序求后序是一个道理,在中序中找到根,然后分成左右子树,因为先序遍历的顺序是根左右,所以在递归前就应该输出当前元素。按照后序遍历是左右根的顺序,因此对于一棵树的后序遍历,前面是左子树元素,之后是右子树元素(左右子树的长度我们可以通过中序遍历找到根的位置后,其左边元素的数量得出左子树元素个数,右边元素的数量得出右子树原数的个数),最后是该数的根元素。

代码:
#include 
#include 
#include 
using namespace std;
int n;
string in, post;
void solve(int l1, int r1, int l2, int r2){
    //递归到叶子结点,就递归返回
    if(l1 > r1)return;
    
    //在中序遍历中找到该树的根节点
    int pos = l2;
    while(in[pos] != post[r1])pos++;
    cout<<post[r1];//先序遍历,在递归开始的时候输出
    //遍历左子树
    //更新先序遍历为当前树的左子树:左子树的元素范围为[l1, l1 + pos - l2 - 1]   左子树的元素数量为pos - l2
    //更新中序遍历为当前数的左子树:左子树的元素范围为[l2, pos - 1]   不难理解,就是取中序遍历中当前树根的左边元素
    solve(l1, l1 + pos - l2 - 1, l2, pos - 1);
    //遍历右子树
    //更新先序遍历为当前树的右子树:右子树的元素范围为[l1 + pos - l2, r1 - 1]   
    //更新中序遍历为当前数的右子树:右子树的元素范围为[pos + 1, r2]   不难理解,就是取中序遍历中当前树根的右边元素
    solve(l1 + pos - l2, r1 - 1, pos + 1, r2);
}
int main()
{
    cin>>n;
    cin>>post>>in;
    solve(0, n - 1, 0, n - 1);
    return 0;
}
/*
知道二叉树的后序和中序遍历,求先序遍历
*/
运行结果:

请添加图片描述

优化代码:
#include 
#include 
#include 
using namespace std;
int n;
string in, post;
void solve(int root, int l1, int r1){
    if(l1 > r1)return;
    int pos = l1;
    while(in[pos] != post[root])pos++;
    cout<<post[root];
    solve(root - (r1 - pos) - 1, l1, pos - 1);
    solve(root - 1, pos + 1, r1);
}
int main()
{
    cin>>n;
    cin>>post>>in;
    solve(n - 1, 0, n - 1);
    return 0;
}

方二、通过后序和中序建立二叉树,然后再去求前序遍历
#include 
#include 
#include 
using namespace std;
int n;
string post, in;
typedef struct node{
    char data;
    node *l, *r;
}btnode, *btree;

//构建二叉树
btree build(int root, int start, int end){
    if(start > end)return NULL;
    int pos = start;
    while(in[pos] != post[root])pos++;
    btree t = new btnode;
    t->data = post[root];
    t->l = build(root - (end - pos) - 1, start, pos - 1);
    t->r = build(root - 1, pos + 1, end);
    return t;
}
void fstorder(btree root){
	if(root != NULL){
		cout<<root->data;
		fstorder(root -> l);
		fstorder(root -> r);
	}
} 
int main()
{
    btree root;
    cin>>n;
    cin>>post>>in;
    root = build(n - 1, 0, n - 1);
    fstorder(root);
    return 0;
}

三、知二叉树后序和中序遍历求层序遍历

方一、通过后序和中序建立二叉树,然后再去求层次遍历
代码:
#include 
#include 
#include 
#include 
using namespace std;
int n;
int post[35];
int in[35];
typedef struct node{
    int data;
    node *l, *r;
}btnode, *btree;

//构建二叉树
btree build(int root, int start, int end){
    if(start > end)return NULL;
    int pos = start;
    while(in[pos] != post[root])pos++;
    btree t = new btnode;
    t->data = post[root];
    t->l = build(root - (end - pos) - 1, start, pos - 1);
    t->r = build(root - 1, pos + 1, end);
    return t;
}
void fstorder(btree root){
	if(root != NULL){
		cout<<root->data;
		fstorder(root -> l);
		fstorder(root -> r);
	}
} 
void bfs(btree root){
	queue<btree>qu;
    qu.push(root);
    while(qu.size()){
        btree tmp = qu.front();
        qu.pop();
        cout<<tmp->data<<' ';
        if(tmp->l != NULL)qu.push(tmp->l);
        if(tmp->r != NULL)qu.push(tmp->r);
    }
} 
int main()
{
    btree root;
    cin>>n;
    for(int i = 0; i < n; i ++)cin>>post[i];
    for(int i = 0; i < n; i ++)cin>>in[i];
    root = build(n - 1, 0, n - 1);
    bfs(root);
    return 0;
}
方二、通过哈希表构建树
代码:
#include 
#include 
#include 
#include 
#include 
using namespace std;
unordered_map<int, int>l, r, pos;
int post[35];
int in[35];
int n;
void bfs(int root){
    queue<int>qu;
    qu.push(root);
    while(qu.size()){
        int tmp = qu.front();
        qu.pop();
        cout<<tmp<<" ";
        if(l.count(tmp))qu.push(l[tmp]);
        if(r.count(tmp))qu.push(r[tmp]);
    }
}
int build(int l1, int r1, int l2, int r2){
    int root = post[r1];
    int k = pos[root];
    //有左子树
    if(l2 < k)l[root] = build(l1, l1 + k - l2 - 1, l2, k - 1);
    if(r2 > k)r[root] = build(l1 + k - l2, r1 - 1, k + 1, r2);
    return root;
}
int main()
{
    cin>>n;
    for(int i = 0; i < n; i ++)cin>>post[i];
    for(int i = 0; i < n; i ++){
        cin>>in[i];
        pos[in[i]] = i;
    }
    int root = build(0, n - 1, 0, n - 1);
    bfs(root);
    return 0;
}

四、知完全二叉树后序遍历求层次遍历

思路分析:

根据我们后序遍历去递归,知道递归到要输出的位置,我们输入即可。当然如果知道先序和中序,也可以求出它的层次遍历。

代码:
#include 
#include 
#include 
using namespace std;
int tree[35];
int n;
void solve(int i){
    if(i > n)return;
    //只先序,放这里输入	scanf("%d", &tree[i]);
    solve(2 * i);
    //只中序,放这里输入	scanf("%d", &tree[i]);
    solve(2 * i + 1);
    scanf("%d", &tree[i]);	//知后序,放这里输入	scanf("%d", &tree[i]);
}
int main()
{
    cin>>n;
    solve(1);
    for(int i = 1; i <= n; i ++){
        if(i != 1)cout<<" ";
        cout<<tree[i];
    }
    return 0;
}

你可能感兴趣的:(知识点整理,数据结构题解,c++,算法)