链式二叉树基本操作

前序遍历

前序遍历,又叫先前遍历

遍历顺序: 根  左子树 右子树  注意"根"位置 根在前

​
// 前序遍历:根 左子树 右子树  注意"根"位置 根在前
void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

​

先序遍历递归展开图

中序遍历

中序遍历 

遍历顺序:  左子树 根 右子树  注意"根"位置 根在中间

​
// 中序遍历:左子树 根 右子树  注意"根"位置 根在中
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

​

中序遍历递归展开图

后序遍历

前序遍历,

遍历顺序:  左子树 右子树 根  注意"根"位置 根在后


// 后序遍历:左子树 右子树 根  注意"根"位置 根在最后
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

​

获取结点总个数

// 可以使用迭代思想
思路:如为空 结点个数 为0

如 不为空  结点总个数  左子树结点个数+右子树结点个数+(自己)

​
int TreeSize(BTNode* root)
{                                 
	return root == NULL ? 0 
		: TreeSize(root->left) 
		+ TreeSize(root->right) + 1;
}

​


获取二叉树总叶子结点

 如一个结点的 左子树结点为空 并且 右子树也为空 那这个结点就是叶子节点 
 拆分问题 
 1.空树   没有叶子结点
 2.叶子结点   左子树为空并且右子树为空 叶子结点+1
 3.分支结点 继续

​
int TreeeLeafSize(BTNode* root)
{
	//空结点   
	if (root == NULL) // 没有叶子结点
		return 0;
	//叶子结点  左子树为空并且右子树为空 那这个结点就是叶子节点  
	if (root->left == NULL && root->right == NULL)///叶子结点 
		return 1;
	//分支结点 迭代————    叶子结点的总个数 = 左叶子结点个数+ 右叶子结点个数
	return TreeeLeafSize(root->left) + TreeeLeafSize(root->right);
}

​

获取第k层结点个数

思路:当对于父结点的第k层  == 相当于两个孩子结点第k-1层结点之和

int TreeLeveSize(BTNode* root, int k)
{
	if (root == NULL)//树为空直接返回
		return 0;
	if (k == 1)
		return 1;
	// 当对于父结点的第k层  == 相当于两个孩子结点第k-1层结点之和
	return TreeLeveSize(root->left, k - 1) 
		+ TreeLeveSize(root->right, k - 1);
}

递归展开图

二叉树查找 值为x的结点

拆分问题

1.先判断根结点

2.再去左子树找

3.再去右子树找

BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)// 空树
		return NULL;
	if (root->val == x) //先判断根结点
		return root;

	BTNode* left =TreeFind(root->left, x); //左子树找
	if (left)
		return left; //不为空说明找到了
	BTNode* right = TreeFind(root->right, x); //右子树找
	if (right)
		return right;//不为空说明找到了

	
	return NULL; //返回NULL不一定找不到
}

单值二叉树

/拆分问题
 1.空树
 2.根 和左右子树判断---- 迭代 -- 走 更新左右子树  

/拆分问题
// 1.空树
// 2.根 和左右子树判断 迭代 -- 走

bool isUnivalTree(struct TreeNode* root){

    if(root == NULL)
        return true;
    
    // 当前结点的左孩子的结点不为空 就和 当前结点判断
    if(root->left && root->left->val != root->val)
        return false;
     // 当前结点的右孩子的结点不为空 就和 当前结点判断
    if(root->right && root->right->val != root->val)
        return false;
    
    // 左子树 判断完继续 判断 右子树
    return isUnivalTree(root->left) && isUnivalTree(root->right);
}

判断两颗树是否相同


拆分问题;必须满足 两个树在结构上相同,并且节点具有相同的值

1. 根 和 根比较
2. 左子树 和 左子树 比较
3. 右子树 和 右子树 比较

 

​
//拆分问题;必须满足 两个树在结构上相同,并且节点具有相同的值
//
//1. 根 和 根比较
//2. 左子树 和 左子树 比较
//3. 右子树 和 右子树 比较

bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
    // 两课树 一起走到 空 说明 这两颗树再结构上是相同的,并且结点具有相同的值
    if(p == NULL && q == NULL)
        return true;
    // 一颗树为空 一颗树不为空 不满足(两个树在结构上相同)
    if((p == NULL && q != NULL)||(p != NULL && q == NULL))
        return false;
    
    //不满足(节点具有相同的值)
    if(p->val != q->val)
        return false;

    // //2. 左子树 和 左子树 比较 完 继续比较 右子树 和 右子树 
    return isSameTree(p->left,q->left) && isSameTree(p->right, q->right);
}

​

层序遍历

利用队列先进先出的思想
先进 根结点 出 根结点 带入(根结点的左右孩子不为空进队列) --迭代 走


void BinaryTreeLevelOrder(BTNode* root)
{
	Queue p1;

	QueueInit(&p1);

	QueuePush(&p1, root);

	while (!QueueEmpty(&p1))
	{
		BTNode* front = QueueFront(&p1);
		printf("%c ", front->val);
		QueuePop(&p1);//在这里删除的是队列的数据 没有破坏二叉树 ,所以还是可以找到 当前结点左右孩子的 

		//左孩子不为空进队列
		if (front->Lchild)
			QueuePush(&p1, front->Lchild);
		//右孩子不为空进队列
		if (front->Rchild)
			QueuePush(&p1, front->Rchild);
	}

	QueueDestroy(&p1);
}

判断二叉树是否是完全二叉树

思路: 
利用队列先进先出的特点
先进 根结点 出 根结点 带入根结点的左右孩子(根结点的左右孩子为空也进队列) --迭代 走
遇到空 判断队列剩余的数据是否为空 如果剩余的数据全部为空 是完全二叉树 反之 不是 完全二叉树

bool BinaryTreeComplete(BTNode* root)
{
	Queue p1;

	QueueInit(&p1);

	if (root != NULL)
		QueuePush(&p1, root);

	while (!QueueEmpty(&p1))
	{
		BTNode* front = QueueFront(&p1);
		
		if (front == NULL)
			break;

		QueuePop(&p1);

		QueuePush(&p1, front->Lchild);
		QueuePush(&p1, front->Rchild);
	}

	while (!QueueEmpty(&p1))
	{
		BTNode* front = QueueFront(&p1);

		if (front != NULL)
		{
			QueueDestroy(&p1);
			return false;
		}
		QueuePop(&p1);

	}
	QueueDestroy(&p1);
	return true;
}

判断对称二叉树

判断二叉树是否为对称二叉树 ,则判断它的左右子树是否镜像 。因为镜像是对称的,所以它的左右子树遍历规则是相反的

// 因为对称二叉树是 镜像的所以 左子树 和右子树 判断规则是相反的



bool isSameTree(struct TreeNode* left, struct TreeNode* right)
{
    // 两课树 一起走到 空 说明 这两颗树再结构上是相同的,并且结点具有相同的值
    if(left == NULL && right == NULL)
        return true;
    // 一颗树为空 一颗树不为空 不满足(两个树在结构上相同)
    if((left == NULL && right != NULL)||(left != NULL && right == NULL))
        return false;

    if(left->val != right->val)
        return false;

    // 左子树:遍历顺序 根 左 右   右子树:遍历顺序 根 右 左   若两次遍历成功 是 对称二叉树
    return isSameTree(left->left,right->right) && isSameTree(left->right, right->left);
}


bool isSymmetric(struct TreeNode* root)
{
    if(root == NULL)
        return true;
    
    return isSameTree(root->left, root->right);
}

判断二叉树是否是另一棵树的子树

判断subRoot是否为 root的子树,如果 root中包含和subRoot相同的结构并且结点值相同,就说明subRoot是root的子树, root 和subRoot 必须不为空

如下图 subRoot 就是root的子树:

思路:依次判断root的每一个结点都需要与subRoot判断, 当root结点的值 和 subRoot相等 ,并且root子树的结构和 subRoot 的结构一样 subRoot是root的子树

bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
    // 两课树 一起走到 空 说明 这两颗树再结构上是相同的,并且结点具有相同的值
    if(p == NULL && q == NULL)
        return true;
    // 一颗树为空 一颗树不为空 不满足(两个树在结构上相同)
    if((p == NULL && q != NULL)||(p != NULL && q == NULL))
        return false;
    
    //不满足(节点具有相同的值)
    if(p->val != q->val)
        return false;

    // //2. 左子树 和 左子树 比较 完 继续比较 右子树 和 右子树 
    return isSameTree(p->left,q->left) && isSameTree(p->right, q->right);
}
//root 中每个结点 都需要和 subRoot 判断

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){

    if(root == NULL)
        return false;
    
    if(root->val  == subRoot->val)
    {
        if(isSameTree(root, subRoot)) // 相同返回true
        {
            return true;
        }
    }

   return isSubtree(root->left, subRoot)|| isSubtree(root->right,subRoot);

}

二叉树的深度遍历

前序遍历  

遍历规则:根  左子树  右子树 ,前序遍历 中序遍历 后序遍历它们除了遍历规则其余都一样

注意,接下来所要说的深度遍历与前面有所不同,前面说到的深度遍历是将一棵二叉树遍历,并将遍历结果打印屏幕上(较简单)。而下面说到的深度遍历是将一棵二叉树进行遍历,并将遍历结果存储到一个动态开辟的数组中,将数组作为函数返回值进行返回。

思路:
 1.首先计算二叉树中结点的个数,便于确定动态开辟的数组的大小。
 2.遍历二叉树,将遍历结果存储到数组中。
 3.返回数组。
 

 // 思路
 //1. 遍历二叉树 获取总结点的个数 ,确定开辟空间的大小
 //2.遍历二叉树 把二叉树所有结点的值 存放到数组
// 3. 返回数组 

int TreeSize(struct TreeNode* root)
{
    return root == NULL ? 0 :TreeSize(root->left) +TreeSize(root->right)+1;
}
2.遍历二叉树 把二叉树所有结点的值 存放到数组
void prev_order(struct TreeNode* root, int*a, int* pi)
{
    if(root == NULL) // 树为空直接返回
        return ;

    a[(*pi)++] =root->val; // 先存放根结点的值 存放进数组

    prev_order(root->left, a,pi); // 左子树的结点存放进数组
    prev_order(root->right, a,pi); // 右子树的结点存放进数组

}

int* preorderTraversal(struct TreeNode* root, int* returnSize){

   
    *returnSize = TreeSize(root); //返回 数组的大小

    int* a = malloc(sizeof(int)* (*returnSize));

    int i =0;
    prev_order(root, a,&i); //把二叉树中所有结点存放进数组

    return a;
}

中序遍历

遍历规则: 左子树 根 右子树

​

// 思路
 //1. 遍历二叉树 获取总结点的个数 ,确定开辟空间的大小
 //2.遍历二叉树 把二叉树所有结点的值 存放到数组
// 3. 返回数组 

// //1. 遍历二叉树 获取总结点的个数 ,确定开辟空间的大小
int TreeSize(struct TreeNode* root)
{
    return root == NULL ? 0 :TreeSize(root->left) +TreeSize(root->right)+1;
}

// //2.遍历二叉树 把二叉树所有结点的值 存放到数组
void In_order(struct TreeNode* root, int* a,int *pi)
{
    if(root == NULL) //树为空直接返回
        return ;
        return ;

    In_order(root->left, a,pi);//先存放左子树的结点存放进数组
    a[(*pi)++] = root->val; //根的结点存放进数组
    In_order(root->right, a,pi);//右子树的结点存放进数组

}

int* inorderTraversal(struct TreeNode* root, int* returnSize){

     *returnSize =  TreeSize(root);//返回 数组的大小
      
    int*a =  malloc(sizeof(int)*(*returnSize));
    int i=0;
    In_order(root,a,&i);// //把二叉树中所有结点存放进数组


    return a;//3. 返回数组 
}

​

后序遍历

遍历规则: 左子树  右子树 根

/ 思路
 //1. 遍历二叉树 获取总结点的个数 ,确定开辟空间的大小
 //2.遍历二叉树 把二叉树所有结点的值 存放到数组
// 3. 返回数组 

 //1. 遍历二叉树 获取总结点的个数 ,确定开辟空间的大小
int TreeSize(struct TreeNode* root)
{
    return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}


 //2.遍历二叉树 把二叉树所有结点的值 存放到数组
void post_order(struct TreeNode* root, int* a,int *pi)
{
    if(root == NULL)
        return ;

    post_order(root->left, a,pi);//先存放左子树的结点存放进数组
    post_order(root->right, a,pi);//再放右子树的结点存放进数组
    a[(*pi)++] = root->val;//根的结点存放进数组

}


int* postorderTraversal(struct TreeNode* root, int* returnSize){

      *returnSize =  TreeSize(root);//返回 数组的大小
      
    int*a =  malloc(sizeof(int)*(*returnSize));
    int i=0;
    post_order(root,a,&i);//把二叉树中所有结点的值存放进数组


    return a;// 3. 返回数组 
}

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值