链式二叉树OJ练习与递归

1322549e692b9adccbc35e45cfee0dc5_MD5

@TOC
09c14b0a4ca9dee7d971a33cd12b82bf_MD5

1. 二叉树销毁

  • 利用分而治之的思想可转化为:
    • 子问题:左右子树的销毁。
  • - 返回条件(最小规模子问题):左/右子树为空。
    先销毁根再销毁左右——后序查找

实现

1
2
3
4
5
6
7
8
9
10
11
void destory(BTNode* root)
{
if (root == NULL)
{
return 0;
}
destory(root->left);
destory(root->right);
free(root);
//形参改变不会改变实参,故在函数外置空
}

2. 二叉树查找值为x的值

  • 子问题:左/右子树是否为x
  • 返回条件
    • 找到x返回该地址;
    • 左子树找不到去右子树找;
    • 右子树找不到返回空;
  • 注意:要返回地址,所以每次栈帧销毁都要接收返回值,直到回归第一层。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BTNode* FreeFind(BTNode* root,BTDataType x)
{
//遍历->找到后即返回在函数中接收->前序
if (root == NULL)
{
return;
}
if (root->val == x)
{
return root;
}
BTNode* ret = NULL;
ret = FreeFind(root->left, x);
if (ret)
return ret;
ret = FreeFind(root->right, x);
if (ret)
return ret;
return NULL; //左右都没找到
}

3. 相同的树

题目:100.相同的树

给你两棵二叉树的根节点 pq ,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

  • 最小递归子问题:
    • 遍历
      • 根是否相同
      • 左右子树是否相同
  • 返回条件:
    • 同时为空返回true(子树相同)
      • 接下来有其中一端为NULL的情况,所以不可比较val
    • 有一个为空返回flase
    • 比较val是否相同,不相等则返回false
      - 相同返回true
  • 注意:在该题中要求子树末端也必须得相同

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };

*/
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);
}

![[Pasted image 20230916095543.png]]

4. 对称二叉树

  • 题目:101对称二叉树

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

  • 沿用上一题的方法,可以将其改为从第二层开始两子树的左右-右左子树相同的问题。
  • 最小递归子问题:
    • 左子树的左值是否等于右子树右值
    • 左子树的右值是否等于右子树左值
  • 返回条件:
    • 全为空返回false;
    • 相同返回true;
    • 不满足子问题返回false;

实现

1
2
3
4
5
6
7
8
9
10
11
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);
}

5. 二叉树的前\中\后序遍历

在线编程中属于接口型(另一个是io型,自己编程)

  • 题目分析
    1. int* returnSize 返回的是数组大小,形参的改变改变不了实参,故传递指针类型 。
    2. 题目值类型为int* ,由于开辟的空间名可自己定义,故返回数组名。
    3. 我们在内部定义局部变量来放malloc过来的空间
      开辟空间大小TreeSize
    4. 将树中元素按前序放入数组中,中序后序遍历与前序同理(前序方法在另一篇文章中已经讲过,故不在此赘述前中后序
      5.最后将n赋值给*TreeSize并返回数组a;

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int TreeSize(struct TreeNode* root)
{
return root==NULL?0:TreeSize(root->left)+TreeSize(root->right)+1;
}
void preorder(struct TreeNode* root,int* a,int* pi)
{
if(root==NULL)
return ;
a[(*pi)++]=root->val; //前序
preorder(root->left,a,pi);
// a[(*pi)++]=root->val; //中序
preorder(root->right,a,pi);
// a[(*pi)++]=root->val; //后序

}

int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
int n=TreeSize(root);
int* a=(int*)malloc(sizeof(int)*n);
int j=0;
//不能重复malloc申请空间,所以写一个子函数用来递归
preorder(root,a,&j);
*returnSize=n;
return a;
}

6. 翻转二叉树

题目:翻转二叉树

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

    最小递归子问题:左右两子树交换(Swap)
    返回条件:将每个节点遍历完后是NULL返回。前序遍历
    综上,该程序可大致理解为在前序遍历中的交换左右子树问题

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
void Swap(struct TreeNode** p1,struct TreeNode** p2)
{
struct TreeNode* tmp=NULL;
tmp=*p1;
*p1=*p2;
*p2=tmp;
}
void PreOrder(struct TreeNode* root)
{
if(root==NULL)
return ;
Swap(&(root->left),&(root->right));
PreOrder(root->left);
PreOrder(root->right);
}
struct TreeNode* invertTree(struct TreeNode* root)
{
PreOrder(root);
return root;
}

7. 另一颗子树

  • 题目: 另一颗子树

    给你两棵二叉树 rootsubRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false
    二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

题目分析:
最小子问题:

判断当前节点下的子树是否相等,见上文中100.相同的树 问题。
返回条件:
前序遍历每个节点,若相同则判断当下节点子树是否相等

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool IsSameTree(struct TreeNode* root, struct TreeNode* subRoot)
{
//全为空
if(root==NULL && subRoot==NULL)
return true;
//一个为空
if(root==NULL || subRoot==NULL)
return false;
//都不为空
if(root->val != subRoot->val)
return false;
return IsSameTree(root->left,subRoot->left)
&& IsSameTree(root->right,subRoot->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
if(root == NULL)
return false;
if(root->val==subRoot->val)
{
if(IsSameTree(root,subRoot))
return true;
}
return isSubtree(root->left,subRoot)
|| isSubtree(root->right,subRoot);
}

8. 层序遍历——队列

先进先出
引入队列结构,将int类型该为树的节点类型
.h在预处理阶段不会被处理。.h文件回去再.cpp文件展开,所以放在结构体声明下面

  • 实现过程

    root=1时,打印front=1,左右不为空,左2右3入队列,删除队头元素1
    root=2时,打印front=2,左右不为空,左4右5入队列,删除队头元素2
    root=3时,打印front=3,左右为空,删除队头元素3
    root=4时,打印front=4,右不为空,右6入队列,删除队头元素4
    ………………
    ![[Drawing 2023-09-23 16.03.53.excalidraw.png]]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void LevelOrder(BTNode* root)
{
Que q;
QueInit(&q);

if (root)
QuePush(&q, root);
while (!QueEmpty(&q))
{
BTNode* front = QueFront(&q);
printf("%d",front->val);
if (front->left)
QuePush(&q, front->left);
if (front->right)
QuePush(&q, front->right);
QuePop(&q);
}
printf("\n");
QueDestory(&q);
}

9. 是否为完全二叉树

完全二叉树:前n-1层都是连续的(走层序遍历非空节点连续)
入到空后面一定没有非空
遍历,有空且后面没有非空时为完全二叉树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
bool TreeComplete(BTNode* root)
{
Que q;
QueInit(&q);

if (root)
QuePush(&q, root);
while (!QueEmpty(&q))
{
BTNode* front = QueFront(&q);
//全部入队列,直到遇到NULL停止
if (front==NULL)
break;
QuePush(&q, front->left);
QuePush(&q, front->right);
QuePop(&q);
}
//继续入队,遇到非空即不是完全二叉树
while (!QueEmpty(&q))
{
BTNode* front = QueFront(&q);
QuePop(&q);
if (front != NULL)
{
QueDestory(&q);
return false;
}
}
QueDestory(&q);
return true;
}

10. 求树的高度

高度——就是最深的一层

1
2
3
4
5
6
7
8
//(1)
int treeheight(btnode* root)
{
if (root == null)
return 0;
return treeheight(root->left) > treeheight(root->right) ?
treeheight(root->left) + 1 : treeheight(root->right) + 1;
}

//多次调用计算,效率及其地下低下,因为在调用时进行了两次递归,非常差

1
2
3
4
5
6
7
8
9
10
//(2)
int Treeheight(BTNode* root)
{
if (root == NULL)
return 0;
int leftheight = Treeheight(root->left);
int rightheight = Treeheight(root->right);

return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
}
1
2
3
4
5
6
7
8
//(3)
int Treeheight(BTNode* root)
{
if (root == NULL)
return 0;

return fmax(Treeheight(root->left),Treeheight(root->right)) + 1;
}

fmax记得包含头文件#include<math.h>

11. [KY11 二叉树遍历]

题目 11. KY11 二叉树遍历
![[Pasted image 20230925193204.png]]

分析

  • 根据题目要求,对字符串先序遍历(根-左-右)创建二叉树,如图所示;
  • 当遇到空#时为空,返回NULL
    • 之后按照后续遍历将二叉树打印出来

![[Pasted image 20230925193117.png]]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <stdlib.h>

typedef char BTDataType;
//二叉树结构体
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
BTDataType val;
}BTNode;
//创建树结构
BTNode* CreatTree(char* str, int* pi)
{
if (str[*pi] == '#')
{
(*pi)++;
return NULL;
}
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
root->val = str[*pi];
(*pi)++;
//先序插入
root->left = CreatTree(str, pi);
root->right = CreatTree(str, pi);
return root;
}
//中序输出
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;
}

感谢未来大佬们的光顾,创作不易,还请给个免费的赞哦~~❤️❤️❤️