【数据结构】树与二叉树遍历算法的应用(求叶子节点个数、求树高、复制二叉树、创建二叉树、二叉树存放表达式、交换二叉树每个结点的左右孩子)

求叶子节点个数、求树高、复制二叉树、创建二叉树、二叉树存放表达式、交换二叉树每个结点的左右孩子

  1. 遍历二叉树的应用都是基于递归法实现的,递归的知识点以及例题可以参考文章: 【数据结构与算法】递推法和递归法解题(递归递推算法典型例题)
  2. 树与二叉树知识点可参考:【数据结构】树与二叉树(递归法先序、中序、后序、层次遍历二叉树、二叉树的建立以及求树高的方法)

注:本文二叉树的结构体定义为(采用二叉链表的定义):
在这里插入图片描述

//采用二叉链表的定义
typedef struct TreeNode{
	int data;//数据域
	TreeNode *rchild;//右孩子指针
	TreeNode *lchild;//左孩子指针
}TreeNode, *BiTree;
//注意结构体名称别写错!!!

应用一:统计二叉树中叶子结点个数的算法

写法一:使用静态变量

  • 函数使用了一个静态局部变量 count 来统计叶子节点的数量。
  • 每次调用函数时,count 的值都会保持,并且在遇到叶子节点时自增。
int LeafCount(BiTree T){
	static int count=0; // 静态局部变量,保持在函数调用之间的值
	if(T){
		if((T->lchild==NULL)&&(T->rchild==NULL)){
			count++; // 每当遇到叶子节点,count自增
		}
		else{
			LeafCount(T->lchild);
			LeafCount(T->rchild);
		}
	}
	return count; // 返回叶子节点数
}

写法二:传入 count 作为参数

  • 函数将 count 作为参数传递,并通过引用进行修改。
int LeafCount(BiTree T, int &count) {
    if (T) {
        if (T->lchild == NULL && T->rchild == NULL)
            count++;
        else {
            LeafCount(T->lchild, count);
            LeafCount(T->rchild, count);
        }
    }
    return count;
}

写法三:不使用额外变量

  • 函数直接通过递归计算叶子节点的数量,不使用额外的变量。
  • 当遇到空树,返回 0;当遇到叶子节点,返回 1;否则,递归计算左右子树的叶子节点数,并返回它们的和。
int LeafCount3(BiTree T){  
	if (!T) return 0;	// 空树叶子结点个数为0
  	if (!T->lchild && !T->rchild) return 1; // 找到一个叶节点
  	return LeafCount3(T->lchild) + LeafCount3(T->rchild);	// 否则为左右叶子结点个数之和
}

应用二:二叉树存放表达式

二叉树存放表达式 :

波兰式: +*dj/e+hi
中缀表示: d*j+e/(h+i)
逆波兰式: dj*ehi+/+

在这里插入图片描述

应用三:求二叉树的高

求二叉树深度算法(后序遍历)
m a x { 左子树深度 + 1 ,右子树深度 + 1 } max\{左子树深度+1,右子树深度+1\} max{左子树深度+1,右子树深度+1}

算法实现原理:

  1. 函数 Get_Height 接收一个指向树节点的指针 node 作为参数,表示要计算高度的二叉树的根节点。

  2. 首先,检查传入的节点是否为空。如果是空节点(即 node == NULL),那么树的高度为 0,因此函数直接返回 0。

  3. 如果节点不为空,则进入递归过程。它通过分别递归调用 Get_Height 函数来计算左子树和右子树的高度。

  4. 递归的终止条件就是当遇到叶子节点时,即左子节点和右子节点都为 NULL,这时高度为 0。

  5. 在每一层递归中,通过比较左子树的高度和右子树的高度,选择其中较大的一个,然后加上根节点,就得到了以当前节点为根的子树的高度。

  6. 最终,根据左右子树的高度计算出的最大高度加上根节点,就是整棵树的高度。

  7. 然后,返回return Tree_Height这个树的高度。

//求二叉树的高
int Get_Height(TreeNode* node) {
    if (node == NULL) return 0;//终止条件:如果为空节点,返回0,表示高度为0
    int Left_Height = Get_Height(node->lchild);//递归求左子树高度
    int Right_Height = Get_Height(node->rchild);//递归求右子树高度
    int Tree_Height = 1 + (Left_Height > Right_Height?Left_Height:Right_Height);//计算树高
    return Tree_Height;
}

应用四:复制二叉树

复制二叉树采用后序遍历的方法来实现的,具体实现原理如下:

  1. 结点生成GetTreeNode函数):

    • GetTreeNode函数用于创建一个二叉树的结点。
    • 它接收三个参数:
      • item:表示结点的数据值。
      • lptr:表示左子树的指针。
      • rptr:表示右子树的指针。
    • 在函数内部,它首先分配一个新的结点(Q)的内存空间。
    • 然后,将传入的数据值、左子树指针和右子树指针分别赋值给新结点的对应字段。
    • 最后,返回新创建的结点。
  2. 后序遍历CopyTree函数):

    • CopyTree函数用于复制一个二叉树。
    • 如果需要复制的二叉树的结点为空,直接返回NULL
    • 否则,递归地复制左子树和右子树:
      • 如果左子树非空,调用CopyTree函数复制左子树。
      • 如果右子树非空,调用CopyTree函数复制右子树。
    • 创建一个新的结点,将原结点的数据值、复制的左子树和复制的右子树分别赋值给新结点的对应字段。
    • 返回复制后的新二叉树。

代码示例:

// 生成一个二叉树的结点
TreeNode* GetTreeNode(int item, TreeNode *lptr , TreeNode *rptr ){
	BiTree Q;
    if ( !(Q = (TreeNode*)malloc(sizeof(TreeNode))) ) {
    	exit(1);
    }
    Q-> data = item;
    Q-> lchild = lptr;    
    Q-> rchild = rptr;
    return Q;
}

//后序遍历 
TreeNode *CopyTree(TreeNode *T) {  
	//如果需要复制的二叉树的结点为空 就直接退出 
	BiTree newlptr=NULL;
	BiTree newrptr=NULL;
	BiTree newT=NULL;
	
    if (!T)    return NULL;
    if (T->lchild ) 
        newlptr = CopyTree(T->lchild);//复制左子树
    if (T->rchild ) 
		newrptr = CopyTree(T->rchild);//复制右子树
    
    newT = GetTreeNode(T->data, newlptr, newrptr);
    return newT;
} // CopyTree

注意:在给定的代码中,CopyTree 函数用于复制一个二叉树。它会递归地复制二叉树的每个节点,并且生成一个新的二叉树。在这个过程中,新的二叉树和原始二叉树的节点是独立的,它们的内存地址不同。这是因为 CopyTree 函数中调用 GetTreeNode 函数,该函数每次都会动态分配内存以创建新的节点。

所以,复制后的二叉树和原始二叉树之间的节点地址没有任何关系,它们是相互独立的。修改复制后的二叉树的节点不会影响原始二叉树。

应用五:二叉树的建立

以先序序列定义一棵二叉树:输入所要建立的二叉树的先序序列,建立二叉链表。
在输入的二叉树的先序序列中,加入空树明确表示"#"

在这里插入图片描述

按先序遍历序列建立二叉树的二叉链表
已知先序 序列为:ABC##DE##F###

二叉树的建立的算法:

typedef struct TreeNode{
	int data;//数据域
	TreeNode *rchild;//右孩子指针
	TreeNode *lchild;//左孩子指针
}TreeNode, *BiTree;

//二叉树的建立的算法(按先序遍历序列建立)
void CreateBiTree(BiTree &T) {
	char ch; 
    scanf("%c",&ch);
    if (ch=='#') T = NULL;
    else {
        T = (TreeNode *)malloc(sizeof(TreeNode)); 
        T->data = ch;      // 生成根结点
        CreateBiTree(T->lchild); // 构造左子树
        CreateBiTree(T->rchild); // 构造右子树
    }
}

应用六:交换二叉树每个结点的左右孩子

遍历二叉树算法的变式:

  1. Swap 函数(先序遍历):

    • 从根节点开始,先交换当前节点的左右孩子。
    • 然后递归地对左子树和右子树执行相同的操作。
  2. Swap2 函数(中序遍历):

    • 与先序遍历不同,中序遍历中需要先对左子树进行操作,然后交换当前节点的左右孩子,最后对右子树进行操作。
    • 但是这个实现方式是错误的,因为在交换左子树之后,对右子树进行操作时,右子树的结构已经发生了变化,导致结果错误。
  3. Swap3 函数(后序遍历):

    • 与先序遍历类似,但是是在遍历完左右子树之后再交换当前节点的左右孩子。
    • 这样可以保证在交换左右孩子时,左右子树的结构不会被改变。

通过上述分析,正确的交换方式是采用先序或后序遍历,中序遍历方式不适合这个场景。

Swap 函数(先序遍历):

//前序
void Swap(BiTree& T){
	//(先序遍历) 
	if(T){
	//根节点 
		if(T->lchild||T->rchild){
			BiTree p;
			p= T->lchild;
			T->lchild = T->rchild;
			T->rchild = p;
		}
		Swap(T->lchild);
		Swap(T->rchild);
	}
}

Swap 函数(中序遍历)××× 不可行:

//中序的不行 
void Swap2(BiTree& T){
	//(中序遍历) 
	
	if(T){
	//根节点 
		Swap2(T->lchild);
		if(T->lchild||T->rchild){
			BiTree p;
			p= T->lchild;
			T->lchild = T->rchild;
			T->rchild = p;
		}
		Swap2(T->rchild);
		
	}
}

Swap 函数(后序遍历):

//后序
void Swap3(BiTree& T){
	//(后序遍历) 
	
	if(T){
	//根节点 
		Swap3(T->lchild);
		Swap3(T->rchild);
		if(T->lchild||T->rchild){
			BiTree p;
			p= T->lchild;
			T->lchild = T->rchild;
			T->rchild = p;
		}
	}
	
	
}

应用七:判断是否为平衡二叉树

应用八:判断是否为排序二叉树

求叶子节点个数、求树高、复制二叉树、创建二叉树完整代码示例

输入示例:ABC##DE##F###

#include<iostream>
using namespace std;


typedef struct TreeNode{
	int data;//数据域
	TreeNode *rchild;//右孩子指针
	TreeNode *lchild;//左孩子指针
}TreeNode, *BiTree;


//二叉树的建立的算法(按先序遍历序列建立)
void CreateBiTree(BiTree &T) {
	char ch; 
    scanf("%c",&ch);
    if (ch=='#') T = NULL;
    else {
        T = (TreeNode*)malloc(sizeof(TreeNode));
        T->data = ch;      // 生成根结点
        CreateBiTree(T->lchild); // 构造左子树
        CreateBiTree(T->rchild); // 构造右子树
    }
}

//求叶子结点 
int LeafCount(BiTree T){
	static int count=0;//或者定义成全局变量 
	/*这里使用局部静态变量*/
	if(T){
		
		if((T->lchild==NULL)&&(T->rchild==NULL)){
			//判断T是否为叶子结点
			count++;
			//每次递归调用
			//当T为叶子时,count自加
		}
		else{
			//递归:求T左、右子树上的叶子结点数

			LeafCount(T->lchild);
			LeafCount(T->rchild);
		}
		
	}
	return count;
}

//求叶子节点的写法三 
int LeafCount3(BiTree T){  
	if (!T) return 0;	
      //空树叶子结点个数为0
  	if (!T->lchild && !T->rchild) return 1; 
      //若根节点没有左右孩子叶子结点个数为1
  	return LeafCount3(T->lchild) + LeafCount3(T->rchild);	
      //否则为左右叶子结点个数之和
}



//求树高 
int Get_Height(BiTree node){//递归 求树高 
	if(node==NULL) return 0;
	else{
		int Left_Height = Get_Height(node->lchild);
		int Right_Height = Get_Height(node->rchild);
		int Tree_Height = 1 + (Left_Height > Right_Height?Left_Height:Right_Height);//计算树高
    	return Tree_Height;
	}
	
}
//先序遍历 
void xxbl(BiTree T){
    if(T){//递归调用的结束条件
        printf("%c",T->data);//访问结点
        xxbl(T->lchild);//遍历左子树
        xxbl(T->rchild);//遍历右子树
    }
    
}
//中序遍历 
void zxbl(BiTree T){
    if (T) {
        zxbl(T->lchild); 
        printf("%c",T->data); 
        zxbl(T->rchild);
    }
}
//后序遍历 
void hxbl(BiTree T){
    if(T){
        hxbl(T->lchild); 
        hxbl(T->rchild); 
        printf("%c",T->data);
    }
}

// 
// 生成一个二叉树的结点
TreeNode* GetTreeNode(int item, TreeNode *lptr , TreeNode *rptr ){
	BiTree Q;
    if ( !(Q = (TreeNode*)malloc(sizeof(TreeNode))) ) {
    	exit(1);
    }
    Q-> data = item;
    Q-> lchild = lptr;    
    Q-> rchild = rptr;
    return Q;
}

//后序遍历 
TreeNode *CopyTree(TreeNode *T) {  
	//如果需要复制的二叉树的结点为空 就直接退出 
	BiTree newlptr=NULL;
	BiTree newrptr=NULL;
	BiTree newT=NULL;
	
    if (!T)    return NULL;
    if (T->lchild ) { 
        newlptr = CopyTree(T->lchild);//复制左子树
	} 
	if (T->rchild ){ 
		newrptr = CopyTree(T->rchild);//复制右子树
    } 
    newT = GetTreeNode(T->data, newlptr, newrptr);
    return newT;
} // CopyTree


int main(){
	 
	BiTree T;
	//例如输入:ABC##DE##F### 来创建二叉树 
	CreateBiTree(T);
	cout<<"先序:"; 
	xxbl(T);
	cout<<endl;
	cout<<"中序:"; 
	zxbl(T);
	cout<<endl;
	cout<<"后序:"; 
	hxbl(T);
	cout<<endl;
	cout<<"树高为:" ;
	cout<<Get_Height(T)<<endl;
	cout<<"叶子节点的个数为:";
	cout<<LeafCount3(T)<<endl; 
	
	
	
	//复制二叉树
	BiTree Q;
	Q=CopyTree(T); 
	cout<<"复制后的二叉树 以先序输出:"; 
	xxbl(Q);
	cout<<endl;
	//修改一下Q 左子树根节点的数据 观察原本T 是否改变 
	Q->lchild->data='Z';
	cout<<"修改的二叉树 Q以先序输出:"; 
	xxbl(Q);
	cout<<endl;
	
	cout<<"T先序:"; 
	xxbl(T);
	cout<<endl;
	return 0; 
} 

感谢您的阅读!

  1. 遍历二叉树的应用都是基于递归法实现的,递归的知识点以及例题可以参考文章: 【数据结构与算法】递推法和递归法解题(递归递推算法典型例题)
  2. 树与二叉树知识点可参考:【数据结构】树与二叉树(递归法先序、中序、后序、层次遍历二叉树、二叉树的建立以及求树高的方法)
  • 32
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
以下是关于创建二叉树二叉树叶子节点二叉树的深度的回答: 1. 创建二叉树 创建二叉树的方法有很多,这里提供一种基于Python的实现方法。我们可以定义一个二叉树节点类,然后通过递归的方式创建二叉树。 ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def create_tree(nums): if not nums: return None mid = len(nums) // 2 root = TreeNode(nums[mid]) root.left = create_tree(nums[:mid]) root.right = create_tree(nums[mid+1:]) return root ``` 上述代码中,我们定义了一个TreeNode类,包含节点值val、左子节点left和右子节点right。create_tree函数接收一个列表nums作为参数,返回一个二叉树的根节点。在函数内部,我们首先判断nums是否为空,如果为空则返回None。否则,我们找到nums的中间位置mid,将mid位置的元素作为根节点的值,然后递归地创建左子和右子。 2. 二叉树叶子节点 二叉树叶子节点是指没有子节点的节点。我们可以通过递归的方式遍历整个二叉树统计叶子节点个数。 ```python def count_leaves(root): if not root: return 0 if not root.left and not root.right: return 1 return count_leaves(root.left) + count_leaves(root.right) ``` 上述代码中,count_leaves函数接收一个二叉树的根节点root作为参数,返回二叉树叶子节点个数。在函数内部,我们首先判断root是否为空,如果为空则返回0。然后判断root是否为叶子节点,如果是则返回1。否则,递归地计算左子和右子叶子节点个数,并将它们相加。 3. 二叉树的深度 二叉树的深度是指从根节点到最远叶子节点的最长路径上的节点数。我们可以通过递归的方式遍历整个二叉树统计二叉树的深度。 ```python def max_depth(root): if not root: return 0 left_depth = max_depth(root.left) right_depth = max_depth(root.right) return max(left_depth, right_depth) + 1 ``` 上述代码中,max_depth函数接收一个二叉树的根节点root作为参数,返回二叉树的深度。在函数内部,我们首先判断root是否为空,如果为空则返回0。然后递归地计算左子和右子的深度,并将它们的最大值加1返回。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chen_devy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值