C语言植物大战数据结构二叉树递归

" 梧桐更兼细雨,到黄昏、点点滴滴。"

c语言朱武大战数据结构专栏

c语言植物大战数据结构快速排序图文示例

c语言植物大战数据结构希尔排序算法

c语言植物大战数据结构堆排序图文示例

前言

本篇用c语言递归来实现二叉树的基本操作。主要用到分治思想

1.本篇文章和代码旨在用于链式二叉树基本操作的复习。主要是递归的应用。

2.深刻理解二叉树是递归定义的这一概念。

分治递归思想:

1.把大问题分割为不可再分割的子问题。。

2.然后一步一步的返回

一、二叉树的遍历算法

二叉树的精髓在于遍历。遍历掌握了后,剩下的问题迎刃而解。

1.构造二叉树

“工欲善其事必利其器”

1.所以先创建一个结构体。

2.手动先构造一颗如图所示的二叉树。

typedef int btdatatype;//定义二叉树结构体typedef struct binarytreenode{<!--{c}%3c!%2d%2d%20%2d%2d%3e-->int data;//节点数据struct binarttreenode* left;//左子树struct binarttreenode* right;//右子树}btnode;//构造一棵二叉树btnode* buybtnode(btdatatype x){<!--{c}%3c!%2d%2d%20%2d%2d%3e-->btnode* node = (btnode*)malloc(sizeof(btnode));if (node == null){<!--{c}%3c!%2d%2d%20%2d%2d%3e-->printf("malloc fail\n");exit(-1);}node->data = x;node->left = null;node->right = null;return node;}btnode* creatbinarytree(){<!--{c}%3c!%2d%2d%20%2d%2d%3e-->btnode* node1 = buybtnode(1);btnode* node2 = buybtnode(2);btnode* node3 = buybtnode(3);btnode* node4 = buybtnode(4);btnode* node5 = buybtnode(5);btnode* node6 = buybtnode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;}int main(){<!--{c}%3c!%2d%2d%20%2d%2d%3e-->btnode* tree = creatbinarytree();return 0;}typedef int btdatatype;
//定义二叉树结构体
typedef struct binarytreenode
{
	int data;//节点数据
	struct binarttreenode* left;//左子树
	struct binarttreenode* right;//右子树
}btnode;
//构造一棵二叉树
btnode* buybtnode(btdatatype x)
{
	btnode* node = (btnode*)malloc(sizeof(btnode));
	if (node == null)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	node->data = x;
	node->left = null;
	node->right = null;
	return node;
}
btnode* creatbinarytree()
{
		btnode* node1 = buybtnode(1);
		btnode* node2 = buybtnode(2);
		btnode* node3 = buybtnode(3);
		btnode* node4 = buybtnode(4);
		btnode* node5 = buybtnode(5);
		btnode* node6 = buybtnode(6);
		node1->left = node2;
		node1->right = node4;
		node2->left = node3;
		node4->left = node5;
		node4->right = node6;
		return node1;
}
int main()
{
	btnode* tree = creatbinarytree();
	return 0;
}

2.前序遍历(递归图是重点.)

遍历顺序:根 左子树 右子树

思路:

1.把每个节点都想成是一棵树。

2.当树为空时。

3.当树不为空时,先遍历左子树,后遍历右子树

注意:前中后序遍历不同处只在printf打印的顺序的位置。

// 二叉树前序遍历
void preorder(btnode* root)
{
	if (root == null)
	{
		printf("null ");
		return;
	}
	//打印在前
	printf("%d ", root->data);
	preorder(root->left);
	preorder(root->right);
}

打印结果:

1 2 3 null null null 4 5 null null 6 null null

递归分析图:

递归题目的万能的解法。就是画递归图。

二叉树的所有题目,假如你不会,赶快 画递归图 吧

由于递归太庞大,图片太小看不清,所以我把左子树和右子树分开又截了图

1.红线部分代表压栈递归。

2.绿线部分代表 返回

左子树

右子树

3.中序遍历

遍历顺序:左子树 根 右子树

void inorder(btnode* root)
{
	if (root == null)
	{
		printf("null ");
		return;
	}
	inorder(root->left);
	//打印在中间
	printf("%d ", root->data);
	inorder(root->right);
}

打印结果

null 3 null 2 null 1 null 5 null 4 null 6 null

4.后序遍历

遍历顺序:左子树 右子树 根

void postorder(btnode* root)
{
	if (root == null)
	{
		printf("null ");
		return;
	}
	postorder(root->left);
	postorder(root->right);
	//打印在最后
	printf("%d ", root->data);
}

打印结果

null null 3 null 2 null null 5 null null 6 4 1

5.层序遍历

思路:

借助先进先出的性质,上一层节点出的时候,带下一层的节点进去。

1.先把根入队列。

2.根节点出来的时候,左右孩子进去。

// 层序遍历
void levelorder(btnode* root)
{
	//初始化队列,注意队列里面存的是 指针类型。
	queue q;
	queueinit(&q);
	//如果树不为空开始入队
	if (root)
	{
		queuepush(&q, root);
	}
	//树不为空开始出对头数据,同时入队左子树和右子树,直到队列为空。
	while (!queueempty(&q))
	{
		btnode* front = queuefront(&q);
		queuepop(&q);
		printf("%d ", front->data);
		//如果还有左右子树,继续入队,否则不入队
		if (front->left)
		{
			queuepush(&q, front->left);
		}
		if (front->right)
		{
			queuepush(&q, front->right);
		}
	}
	//记得销毁队列
	printf("\n");
	queuedestory(&q);
}

二、二叉树遍历算法的应用

1.求节点个数

思想:把大问题逐步分割为子问题。

思路:

1.树为空时返回0个节点。(树为空不意味着才开始就是空树,而是递归到最后一个为null的树返回)

2.树不为空时返回自己的1个节点+上一颗树返回的节点的个数。

// 二叉树节点个数
int binarytreesize(btnode* root)
{
	//当树为空时
	if (root == null)
		return 0;
	//当树不为空时
	return binarytreesize(root->left) +
		binarytreesize(root->right) + 1;
}

2.求叶子节点个数

思路:

1.树为null时,返回0.

2.两颗子树都不为null时,返回1.

3.不满足以上两种情况,继续递归左右子树。

// 二叉树叶子节点个数
int binarytreeleafsize(btnode* root)
{
	//当树为空时
	if (root == null)
		return 0;
	//当两棵 子 树都为空时
	if (root->left == null && root->right == null)
		return 1;
	/*程序都到这一行, 意味着树不满足返回的情况,
	所以继续递归 左子树和 右子树。*/
	return binarytreeleafsize(root->left)+
		binarytreeleafsize(root->right);
}

3.求第k层节点个数

思想:求上图第3层节点个数。

1.站在第1层来看,就是求第3层节点的个数

2.站在第2层的角度来看,就是求第2层节点的个数

3.站在第3层的角度来看,就是求第1层节点的个数

思路:

1.当树为空时返回0

2.当k为1时返回1。

3.不满足1和2,继续递归左右子树。

// 二叉树第k层节点个数
int binarytreelevelksize(btnode* root, int k)
{
	//当树为空时
	if (root == null)
		return 0;
	//当k为1时
	if (k == 1)
		return 1;
	//程序能走到这一行,说明树不为空,k也不为1.继续递归
	return binarytreelevelksize(root->left, k-1)+
	binarytreelevelksize(root->right, k - 1);
}

4.查找值为x的节点

思想:

1.把最小规模的问题写在最前面作为限制

2.不满足最小规模的问题,则继续递归。将问题一步一步拆分为不可分割的子问题。

// 二叉树查找值为x的节点
btnode* binarytreefind(btnode* root, btdatatype x)
{
	//当树为空时
	if (root == null)
		return null;
	//当树的值等于x时
	if (root->data == x)
		return root;
	/*走到这一行,说明不满足以上条件。
	开始递归左右子树,如果找到了,直接一步一步往回返*/
	btnode* a = binarytreefind(root->left, x);
	if (a)
	{
		return a;
	}
	btnode* b = binarytreefind(root->right, x);
	if (b)
	{
		return b;
	}
	//没有x,则返回空
	return null;
}

5.二叉树销毁

思路:相当于二叉树的后序遍历。

先把左右子树遍历完后,开始遍历根,对根进行free。

// 二叉树销毁
void binarytreedestory(btnode* root)
{
	if (root == null)
		return;
	binarytreedestory(root->left);
	binarytreedestory(root->right);
	//free掉根
	free(root);
}

6.前序遍历构建二叉树

思路:

对一串字符进行先序遍历,递归遍历二叉树,当遇见#时开始返回 连接 树。

通过前序遍历的数组"abd##e#h##cf##g##"构建二叉树

#include <stdio.h>
#include <stdlib.h>
typedef struct btnodetree
{
    struct btnodetree* left;
    struct btnodetree* right;
    char val;
}btnode;
//创建二叉树
btnode* createtree(char* a, int* pi)
{
	//如果树为#则返回null
    if(a[*pi] == '#')
    {
        (*pi)++;
        return null;
    }
    //否则构建节点,同时让pi++,以便继续递归
    btnode* root = (btnode*)malloc(sizeof(btnode));
    root->val = a[(*pi)++];
    //构建左右子树
    root->left = createtree(a, pi);
    root->right = createtree(a, pi);
    //构建完后返回根节点。
    return root;
}
//中序遍历打印。
void inorder(btnode* root)
{
    if(root == null)
        return;
    inorder(root->left);
    printf("%c ", root->val);
    inorder(root->right);
}
int main()
{
    char a[100];
    scanf("%s", a);
    int i = 0;
    btnode* tree = createtree(a, &i);
    inorder(tree);
    return 0;
}

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

思路:

1.层序遍历,空节点也进队列

2.出到空节点以后,出队列中所有数据,如果全是空,则是完全二叉树

8.求二叉树的深度

思路:二叉树的最大深度等价于:左右子树的最大深度 + 1

int maxdepth(struct treenode* root)
{
    if(root == null) 
    {
        return 0;
    }
    size_t left = maxdepth(root->left) + 1;
    size_t right = maxdepth(root->right) + 1;
    if(right > left) 
    {
        return right;
    }
    return left;
}
//判断二叉树是否是完全二叉树
bool btreecomplete(btnode* root)
{
	queue q;
	queueinit(&q);
	if (root)
		queuepush(&q, root);
	while (!queueempty(&q))
	{
		btnode* front = queuefront(&q);
		queuepop(&q);
		if (front == null)
			break;
		queuepush(&q, front->left);
		queuepush(&q, front->right);
	}
	while (!queueempty(&q))
	{
		btnode* front = queuefront(&q);
		queuepop(&q);
		//空后面出到非空,那说明不是完全二叉树
		if (front)
			return false;
	}
	//否则是完全二叉树
	return true;
}

三、二叉树leetcode题目

以下题目均属于leetcode的 简单 题目

1.单值二叉树

如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。

只有给定的树是单值二叉树时,才返回 true;否则返回 false。

思想:

1.看一棵树的三个部分是否相同,相同则继续递归下一颗树,直到树为空。

bool isunivaltree(struct treenode* root)
{
    //当树为空时。
    if(root == null)
    {
        return true;
    }
    //当右树不为空,并且 根 != 左树
    //当右树不为空,并且 根 != 右树时
    if(root->left != null && root->val != root->left->val)
    return false;
    if(root->right != null && root->val != root->right->val)
    return false;
    //能走到这一行,说明第一层树的值相同了。接着递归左右子树。
    return isunivaltree(root->left) && 
            isunivaltree(root->right);
}

2. 检查两颗树是否相同

给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。

bool issametree(struct treenode* p, struct treenode* q)
{
    //当两树都为空时
    if(p == null && q== null)
        return true;
    //当其中一个树为空时
    if(p == null || q == null)
        return false;
    //走到这里说明两树存在,比较两树的值
    if(p->val != q->val)
        return false;
    //走到这里说明两树的根节点相同,继续递归,直到判断完左右子树为止。
    return issametree(p->left, q->left) 
    && issametree(p->right, q->right);
}

3. 对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。

bool issym(struct treenode* q, struct treenode* p)
{
      //当只有一个根节点时
    if(q == null && p == null)
         return true;
    //当其中一个子树为空时
    if(q == null ||p ==null)
         return false;
    //程序走到一这行,说明左右节点存在。当两个根节点不相等时
    if(q->val != p->val)
    return false;
    //走到这一步说明左右节点相同,开始递归左右子树
    return issym(q->left, p->right) && issym(q->right, p->left); 
}
bool issymmetric(struct treenode* root)
{
    //当是空树时
    if(root == null)
        return true;
    return issym(root->left, root->right);
}

4.另一颗树的子树

思路:

用到了上一题判断两棵树是否相同的思想。

bool issametree(struct treenode* p, struct treenode* q)
{
    //当两树都为空时
    if(p == null && q== null)
        return true;
    //当其中一个树为空时
    if(p == null || q == null)
        return false;
    //走到这里说明两树存在,比较两树的值
    if(p->val != q->val)
        return false;
    //走到这里说明两树的根节点相同,继续递归
    return issametree(p->left, q->left) 
    && issametree(p->right, q->right);
}
bool issubtree(struct treenode* root, struct treenode* subroot)
{
    //递归结束条件。当根为空时,并不是说明没有节点,可能是所有的子树都遍历过了。然后不相等返回false
    if(root == null)
    return false;
//走到这里说明子树不为空,开始比较子树和sub相同不。
    bool a = issametree(root, subroot);
    if(a)
    return a;
    //走到这里说明不相同,继续递归左子树和右子树,其中一个相同就返回true。
    return issubtree(root->left, subroot) || issubtree(root->right, subroot);
}

5.二叉树的前序遍历

题目思路

1.求节点个数,开辟数组大小。

2.前序遍历存放到数组中

 int treesize(struct treenode* root)
 {
     if(root == null)
        return 0;
     return treesize(root->left) + treesize(root->right)+1; 
 }
 void preorder(int* a, struct treenode* root, int* i)
 {
     if(root == null)
     {
          return;
     }
     a[(*i)++] = root->val;
     preorder(a,root->left, i);
     preorder(a,root->right, i);
 }
int* preordertraversal(struct treenode* root, int* returnsize)
{
    //计算树有几个节点,然后开辟相应的空间
    int size = treesize(root);
    int* a = (int*)malloc(sizeof(int)* size);
    int i = 0;//设置下标i
    *returnsize = size;//需要返回的数组大小
    //前序遍历依次存放到数组中。
    preorder(a, root, &i);
    return a;
}

6.反转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

我犯的bug:只是对二叉树里面的值进行交换,但是无法避免空指针。一直都是空指针的错误,因为root总会为空,root->data总会遇见空指针

所以以后尽量要多想着交换地址。

void _inverttree(struct treenode* root)
{
    if(root)
    {
        struct treenode* tmp = root->left;
        root->left = root->right;
        root->right = tmp;
        _inverttree(root->left);
        _inverttree(root->right);
    }
}
struct treenode* inverttree(struct treenode* root)
{
    _inverttree(root);
    return root;
}

以上就是c语言植物大战数据结构二叉树递归的详细内容,更多关于c语言二叉树递归的资料请关注其它相关文章!

相关推荐:

Python 中 nonlocal 变量在递归中未按预期更新的根本原因解析

本文深入剖析递归函数中nonlocal变量更新失效的典型陷阱:当res+=recurse(i+1)(即右值直接参与赋值)时,res仅被最后一步覆盖为首个返回值;而正确写法需确保res的读取发生在递归“回溯”阶段,即其值已被深层调用更新之后。 本文深入剖析递归函数中`nonlocal`变量更新失效的典...

Python 中 nonlocal 变量在递归中未按预期更新的原因解析

本文深入剖析递归函数中使用nonlocal变量时,因赋值表达式求值顺序不同(l-valuevsr-value位置)导致结果异常的根本原因,并通过对比代码揭示执行时序与变量读写时机的关键影响。 本文深入剖析递归函数中使用nonlocal变量时,因赋值表达式求值顺序不同(l-valuevsr-value...

如何递归重命名嵌套字典中的键名(基于映射字典)

本文介绍一种通用、健壮的递归方法,用于根据指定的键映射字典(key_dict)批量重命名嵌套字典中任意层级的键名,支持多级嵌套映射,避免浅层匹配错误与空字典返回问题。 本文介绍一种通用、健壮的递归方法,用于根据指定的键映射字典(key_dict)批量重命名嵌套字典中任意层级的键名,支持多级嵌套映射,...

Python如何实现图的深度优先遍历_基于栈结构或递归函数实现

递归DFS栈溢出风险高因CPython默认递归深度仅1000,长链或环易触发RecursionError;隐式栈不可控,闭包和帧对象加剧内存开销;图应优先用显式栈实现。 递归实现DFS时,为什么栈溢出风险比预期高? Python默认递归深度限制是1000层,图中存在长链或环状结构时极易触发Recur...

如何在嵌套字典中按层级关系精准递归提取元素值

本文提供一种健壮的递归方法,用于在任意深度的嵌套字典中,依据「主键(mainkey)→子键(subkey)→目标元素(element)」的逻辑路径查找值,支持非连续嵌套结构,避免传统线性遍历的漏匹配问题。 本文提供一种健壮的递归方法,用于在任意深度的嵌套字典中,依据「主键(mainkey)→子键(s...