【C数据结构】二叉树基础和简单练习题

本文深入介绍了树和二叉树的基本概念,包括节点度、叶节点、父节点等。详细讲解了二叉树的前序、中序、后序遍历及其性质,并提供了相应的C语言实现。此外,还涵盖了搜索二叉树、完全二叉树的判断、平衡二叉树的检测以及二叉树的层序遍历。通过实例展示了如何解决相关算法问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、树的基础概念

1、学会看树中的树

在数据结构中,树对比现实中的树,根在上,枝干在下。
并且树在连线中不会构成回路。

下面就是一颗数据结构所表示的树,它以A为根节点,D、E、F为叶节点。
如果以A为根看,那么整个就是一颗以A为根的树。

从A向下看,
当以B为根看,那么就有以B为根的子树(包括B、D、E节点)
以C为根看,那么就有以C为根的子树(包括C、F节点),并且还可以继续往下分,比如最后将D为根,那么它本身就是一个树。

2、树的基础概念

  • 节点的度:一个节点有多少个子树。(下面A节点的度为6)
  • 叶节点或终端节点:没有子节点的结点,度为0.(比如下面的BCHIPQKLMN)
  • 父节点:若节点有子结点,这节点就是父节点。(A有子节点,所以A是父节点)
  • 孩子节点:一个节点含有的子树的根节点为该节点的子节点
  • 兄弟结点:来自同一个父节点
  • 树的度:树中节点最大的度,就是树的度(6嘛)
  • 节点的层次:从根开始为第一层,根的子节点为第二层,以此类推
  • 树的高度或深度:树中节点的最大层数。(该树高度为4)
  • 结点的祖先:从根到该节点所经分支上的所有节点。(比如P的祖先有JEA)
    在这里插入图片描述

二、二叉树的概念和结构

1、二叉树的特点

  • 每个结点最多有两颗子数,既二叉树不存在度数大于2的节点
  • 二叉树的子树有左右之分,其子树的次序不能颠倒。

2、二叉树的前序、中序、后序排列

前序(先根):根 左子树 右子树
中序(中根):左子树 根 右子树
后序(后根):左子树 右子树 根

在这些顺序遍历中,最重要的就是要将大问题化成小问题,小问题一直划分到不能分。

比如在这颗树的前序中:


先看一整颗树(大问题),根为A,有着分别以B为根的左子树和以C为根的右子树,而前序是先根再左子树,所以来到以B为根的左子树(小问题),B又有以D和以E为根的左右子树,这次遍历到D,D之后没有了,所以遍历到E,E之后也没有了,所以以B为根的左子树遍历完了,之后在开始以C为根的右子树。

所以整个过程可以表示为:
A->(B->(D->NULL->NULL)->(E->NULL->NULL))->(C->(F->NULL->NULL)->NULL)
而中序和后序可以表示为:
中序:
((NULL->D->NULL)->B->(NULL->E->NULL))->A->((NULL->F->NULL)->C->NULL)
后序:
((NULL->NULL->D)->(NULL->NULL->E)->B)->((NULL->NULL->F)->NULL->C)->A

如果将一个节点看成链表中的一个结点,对于前中后序的遍历,似乎可以用递归实现,因为限定条件很明显就是结点为不为空,并且每个结点都可以当作以自己为根的树。

typedef char BTDatatype;

typedef struct BinaryTreeNode {
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
	BTDatatype data;
}BT;

//前序遍历打印
void PrevOrder(BT* root) 
{
	if (root == NULL) {
		printf("NULL ");
		return;
	}
	//前中后序的打印顺序,只要在这三行进行调换就行了。
	printf("%c ", root->data);//先打印根
	PrevOrder(root->left);//再打印左子树
	PrevOrder(root->right);//再打印右子树
}

3、二叉树的性质

首先让我们来认识一下
满二叉树和完全二叉树

满二叉树的每一次结点都达到最大值。
完全二叉树最后一层结点没有达到最大值,且最后一层的结点是连续的。

满二叉树中:           完全二叉树中:

若高度为h

结点总数为:(2^h)-1       结点总数为: [2^(h-1) +1,2^h-2] 并且只能取整
最后一层结点树为:2^(h-1)      最后一层结点树为:(0,2^(h-1)) 并且只能取整

推出:h=log2(N+1)         假设缺少X个节点 h=log2(N+1+X)


可以得出以下重要的性质:

  • 对于任意一个二叉树,如果度为0的结点有N0个,度为2的结点有N1个,那么一样会有N0=N1+1。
  • 在完全二叉树中,度为1的结点数一定是1或者0。

4、搜索二叉树

其中,任意一棵树,左子树都要比根小,右子树都要比根大。

因为凭借它的这个性质,就可以找一个数,而且,如果左右子树均匀的话,那么时间复杂度就是O(LogN),查找高度次。

5、求二叉树的结点数、叶结点数和二叉树的最大深度

//求树的结点个数
int TreeSize(BT* root) 
{
	if (root == NULL) 
	{
		return 0;
	}
	//不为0先加1,然后再往左子树看,左子树看完看右子树。
	return 1 + TreeSize(root->left) + TreeSize(root->right);
}

//求树的叶结点个数
int TreeLeafSize(BT* root) 
{
	if (root->left == NULL && root->right == NULL) //叶节点左右都为空
	{
		return 1;
	}
	//不为叶节点继续先往左子树走。
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

//求树的最大深度
int maxDepth(struct TreeNode* root){
    
    if(root==NULL)
    return 0;
    
    //用变量保存左树深度
    int leftDepth=maxDepth(root->left);

	//用变量保存右树深度
    int rightDepth=maxDepth(root->right);
	
	//如果左树大于右树深度,则大的深度加1。
    return leftDepth>rightDepth?leftDepth+1:rightDepth+1;
}

6、二叉树的层序遍历

层序遍历顾名思义,就是从第一层开始将数据一层一层从左到右顺序往下遍历。

如果需要对二叉树进行层序遍历,使用递归深度优先肯定是不合适的,所以这里考虑到广度优先,从头到尾进行操作,但又因为二叉树结构问题难以实现非递归遍历,所以通过队列结构来储存二叉树中数据是一个选择。

在这里插入图片描述
代码实现

//二叉树层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root != NULL)
	{
		QueuePush(&q, root);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%d ", front->data);
		
		
		if (front->left != NULL)
		QueuePush(&q, front->left);
		if (front->right != NULL)
		QueuePush(&q, front->right);

		QueuePop(&q);
	}

	QueueDestroy(&q);
}

如何判断一个树是否是完全二叉树?
完全二叉树有一个特点,就是任何空结点后不会有其它结点,抓住这个特点,在层序遍历后,如果遇到空结点,那么看队列空结点后是否有非空结点,没有那么就是完全二叉树。

代码实现:

bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root != NULL)
	{
		QueuePush(&q, root);
	}

	//入队
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);

		if (front == NULL)
		{
			break;
		}

		QueuePush(&q, front->left);
		QueuePush(&q, front->right);

		QueuePop(&q);
	}
	
	//找非空结点
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueueDestroy(&q);
			return false;
		}
	}

	QueueDestroy(&q);
	return true;

}

三、简单练习题

1、平衡二叉树

【leetcode】平衡二叉树
在这里插入图片描述
很简单的思路,只要确定任何左右两个子树的深度,然后相减看看是否大于1就行了。

int maxDepth(struct TreeNode* root)
{
    
    if(root==NULL)
    {
    	return 0;
    }
    
    int leftDepth=maxDepth(root->left);

    int rightDepth=maxDepth(root->right);

    return leftDepth>rightDepth?leftDepth+1:rightDepth+1;
}

bool isBalanced(struct TreeNode* root)
{
    if(root==NULL)
    {
        return true;
    }

    int leftDepth=maxDepth(root->left);
    int rightDepth=maxDepth(root->right);
	
	//先得判断最大的左子树和右子树深度差,然后再通过递归判断小的左子树和右子树的深度差。
    return abs(leftDepth-rightDepth)<2 && isBalanced(root->left) && isBalanced(root->right);
}

2、二叉树遍历

【nowcoder】二叉树遍历
在这里插入图片描述
先通过输入前序序列,还原二叉树,再通过中序打印。

#include<stdio.h>
#include<stdlib.h>
typedef char BTDataType;

typedef struct BinaryTree
{
    struct BinaryTree* left;
    struct BinaryTree* right;
    BTDataType val;
}BTNode;

BTNode* CreatTree(char* a,int* pi)
{
    if(a[*pi]=='#')
    {
        (*pi)++;//注意下标需要加一
        return NULL;
    }
    
    //利用链表,还原树。
    BTNode* newnode=(BTNode*)malloc(sizeof(BTNode));
    if(newnode==NULL)
    {
        exit(-1);
    }
    newnode->val=(a[*pi]);
    (*pi)++;
    newnode->left=CreatTree(a,pi);
    newnode->right=CreatTree(a,pi);
    return newnode;
}

//中序打印
void InOrder(BTNode* root)
{
    if(root==NULL)
    {
        return;
    }
    InOrder(root->left);
    printf("%c ",root->val);
    InOrder(root->right);
}


int main()
{
	//先输入二叉树的前序序列
    char str[100];
    scanf("%s",str);
    
    int i=0;//定义字符串下标,方便访问
    BTNode* root=CreatTree(str,&i);//因为字符串的下标值需要改变,所以传地址
    InOrder(root);//中序打印
    
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值