当前位置: 首页 > news >正文

数据结构:二叉树oj练习

在讲今天的题目之前,我们还需要讲一下二叉树的以下特点:

对任意一颗二叉树,如果度为0的节点个数是n0,度为2的节点个数是n2,则有n0=n2+1.

证明:二叉树总的节点个数是n,那么有n=n0+n1+n2

           二叉树的度为n-1=n1*1+n2*2

结合上面两个式子,就有:n0=n2+1

完全二叉树中,度为1的结点数只有0或1两种可能

说明:因为完全二叉树的结点是连续编号的,最后一层的结点要么是满的,要么缺少右边的若干结点,所以度为1的结点最多有1个。

选择题

题目1

某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( )
A 不存在这样的二叉树
B 200
C 198
D 199

解释:叶子结点的个数等于度为2的节点的个数+1,所以结果是200

题目2

在具有 2n 个结点的完全二叉树中,叶子结点个数为( )
A n
B n + 1
C n - 1
D n / 2

度为2的节点个数=度为0的节点个数-1,即n2=n0-1

由于是完全二叉树,度为1的节点个数只有0或1两种可能。

如果度为1的节点个数为1,那么1+n0+n0-1=2n,就能得到n0=n,即A可选

如果度为1的节点个数为0,那么n0+n0-1=2n,就会得到n0=(2n+1)/ 2,这是一个小数,所以不合理。

综上,这道题的答案选A。

题目 3

一棵完全二叉树的结点数为 531 个,那么这棵树的高度为( )
A 11
B 10
C 8
D 12

我们知道,二叉树第k层节点个数为2^(k-1),前k层节点个数为2^(k)-1

假设完全二叉树的高度为h,那么完全二叉树节点个数的范围是: 2^(h-1)再到2^(h)-1.

这个范围是咋来的呢?首先第一个范围,既然一共有h层,那么根据公式,前h-1层就一共有2^(h-1)-1个节点,而第h层最少有1各节点,所以前h层应该至少是有2^(h-1)个节点。当这个树为满二叉树时,节点个数达到最大,前h层节点个数一共是:2^(h)-1。

结合这个范围,我们就可以求得答案是10,即选B

题目 4

一个具有 767 个结点的完全二叉树,其叶子结点个数为( )
A 383
B 384
C 385
D 386

假设节点个数是n,那么结合结论,n=2*n0-1+n1,我们知道,完全二叉树的节点个数要么是0,要么是1,带入公式中我们可以得到,n0=384,此时n1=0

题目 5

二叉树的先序遍历和中序遍历如下:前序遍历:EFHIGJK;中序遍历:HFIEJKG。则二叉树后续遍历序列:()

如果我们知道前序遍历+中序遍历  或者  中序遍历+后序遍历,就可以推导出二叉树的结构。

题目 6

设一棵二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为____。 A. adbce

B. decab

C. debac

D. abcde

编程题

144. 二叉树的前序遍历 - 力扣(LeetCode)

这道题就是简单的前序遍历,我们之前在二叉树的实现方法中已经写过了,只需要将代码稍加修改就可以咯:

 void preorder(struct TreeNode* root,int*res, int* returnSize){if(root==NULL){return ;}//先存根节点res[(*returnSize)++]=root->val;preorder(root->left,res,returnSize);preorder(root->right,res,returnSize);}
int* preorderTraversal(struct TreeNode* root, int* returnSize) 
{*returnSize=0;//树中节点的数目在100内int*res=(int*)malloc(sizeof(int)*100);//前序遍历preorder(root,res,returnSize);return res;
}

145. 二叉树的后序遍历 - 力扣(LeetCode)

void postorder(struct TreeNode* root, int* res, int* returnSize)
{if (root == NULL){return;}postorder(root->left, res, returnSize);postorder(root->right, res, returnSize);res[(*returnSize)++] = root->val;
}int* postorderTraversal(struct TreeNode* root, int* returnSize) {*returnSize = 0;//树中节点的数目在100内int* res = (int*)malloc(sizeof(int) * 100);//后序遍历postorder(root, res, returnSize);return res;
}

94. 二叉树的中序遍历 - 力扣(LeetCode)

 void inorder(struct TreeNode* root, int* res, int* returnSize)
{if (root == NULL){return;}inorder(root->left, res, returnSize);res[(*returnSize)++] = root->val;inorder(root->right, res, returnSize);}int* inorderTraversal(struct TreeNode* root, int* returnSize) {*returnSize = 0;//树中节点的数目在100内int* res = (int*)malloc(sizeof(int) * 100);//中序遍历inorder(root, res, returnSize);return res;}

965. 单值二叉树 - 力扣(LeetCode)

这一道题的简单思路也是递归,如果一棵树是单值树,那么他的子树也是单值树,那我们就可以把大问题拆分成若干个子问题了:

bool isUnivalTree(struct TreeNode* root) 
{if(root==NULL){return true;}if(root->left){if(root->left->val!=root->val){return false;}}if(root->right){if(root->right->val!=root->val){return false;}}return  isUnivalTree(root->left) &&  isUnivalTree(root->right);
}

我们可以模拟一下递归过程:

100. 相同的树 - 力扣(LeetCode)

这道题的思路也是利用递归,如果两颗树的根节点相同,那就只需要再比较两颗树的左右子树是否相同即可:

bool isSameTree(struct TreeNode* p, struct TreeNode* q) 
{if(p==NULL&&q==NULL){return true;}if(p==NULL){return false;}if(q==NULL){return false;}if(p->val!=q->val){return false;}//表示根节点不为空且指向的值相等return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}

我们可以模拟一下递归过程:

101. 对称二叉树 - 力扣(LeetCode)

如图,判断一个树是否是对称二叉树,就是要比较根节点的左右子树,我们可以把根节点的左右子树看成两棵独立的二叉树,问题就转换为比较这两棵树是否对称。

两棵树是否互为对称树,就是比较树A上某个节点的左孩子节点的值与树B上对应节点的右孩子节点的值以及树A上某个节点的右孩子节点的值与树B上对应节点的左孩子节点的值是否相等,如果相等的话,就继续递归比较孩子节点的值。

 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;}//两棵树根节点相同,再比较树A的左孩子与树B的右孩子是否相同,以及树A的右孩子与树B的左孩子是否相同return isSameTree(p->left,q->right)&&isSameTree(p->right,q->left);}
bool isSymmetric(struct TreeNode* root)
{return  isSameTree(root->left,root->right);
}

模拟递归过程:

572. 另一棵树的子树 - 力扣(LeetCode)

这一体的思路比较好想,我们可以先比较subroot是否与整棵树相同,如果相同,那就可以直接返回了,如果不相同,说明subroot可能是树的子树,就需要遍历整棵树的节点,看是否存在以某一个节点为根的树与subroot相同:

bool isSameTree(struct TreeNode* p, struct TreeNode* q) 
{if(p==NULL&&q==NULL){return true;}if(p==NULL){return false;}if(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) 
{if(subRoot==NULL){return true;}if(root==NULL){return false;}if(isSameTree(root,subRoot)){return true;}return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
}

二叉树遍历_牛客题霸_牛客网

我们之前说过,如果知道前序遍历+中序遍历  或者  中序遍历+后序遍历,就可以推导出二叉树的结构。但是现在题目中只给了我们前序遍历的结果,我们还能根据这个去构建二叉树的结构吗?

在这一题中是可以的,因为题中给我们的字符串是带有'#'的,这表示空指针,前序遍历的顺序是按照根节点->左子树->右子树,当我们遇到‘#’的时候,就无法继续往下遍历,而是要回到原来子树的根,这没说这可能有点抽象,我们利用题目中给的测试样例来手动还原二叉树的结构:

利用遍历结果构建二叉树听起来好像挺难的,但其实就是对二叉树进行遍历,我们在写前序遍历的代码的时候,将访问二叉树的节点的操作写成了打印二叉树的节点值,在这里,无非就是让我们在前序遍历过程中将访问二叉树的节点的操作写成了创建节点呀,具体思路还是没有变呀,那我们就继续写一下代码吧:

//二叉树的构建与遍历
#include<stdio.h>
#include<stdlib.h>
//二叉树的节点的定义
typedef struct btnode {char data;struct btnode* left;struct btnode* right;
} btnode;btnode* buynode(char x) {btnode* newnode = (btnode*)malloc(sizeof(btnode));newnode->data = x;newnode->left = newnode->right = NULL;return newnode;
}//最后返回根节点
//pi表示指向的字符在字符数组中的下标,由于形参的改变需要影响实参,所以我们这里采用传址调用
btnode* precreattree(char* ch, int* pi) {if (ch[*pi] == '#') {(*pi)++;return NULL;}//先创建根节点btnode* root = buynode(ch[(*pi)++]);//再创建左子树root->left = precreattree(ch, pi);//创建右子树root->right = precreattree(ch, pi);//返回根节点return root;
}
//中序遍历树的节点
void inorder(btnode* root) {if (root == NULL) {return;}//先遍历左子树inorder(root->left);//再访问根节点printf("%c ", root->data);//再遍历右子树inorder(root->right);
}
int main() {char ch[100];//输入字符串scanf("%s", ch);//输入的字符串是二叉树前序遍历的结果,我们需要根据这个结果去创建二叉树int i = 0;btnode* root = precreattree(ch, &i);//中序遍历inorder(root);return 0;
}

今天的内容还是比较丰富的,这些算法题也是比较经典的,能看到这里的兄弟们我真的觉得你们很棒,大家在自己的电脑上也要好好把代码敲一下,把图画一下,把思路整理一下哦!!

http://www.lryc.cn/news/626025.html

相关文章:

  • Linux------《零基础到联网:CentOS 7 在 VMware Workstation 中的全流程安装与 NAT 网络配置实战》
  • Apache ShenYu网关与Nacos的关联及如何配合使用
  • AJAX (一)
  • C# DevExpress控件安装使用教程
  • 【学习】Linux 内核中的 cgroup freezer 子系统
  • 【自动化运维神器Ansible】Playbook调用Role详解:从入门到精通
  • 常用css
  • 【C++】C++ 的护身符:解锁 try-catch 异常处理
  • 用java语言完成手写mybatis框架(第2章)
  • 借助AI将infoNES移植到HarmonyOS平台的详细方案介绍
  • Linux操作系统编程——进程间的通信
  • 极海APM32F107V6 gpio模拟串口
  • 决策树算法学习总结
  • 【Vivado TCL 教程】从零开始掌握 Xilinx Vivado TCL 脚本编程(三)
  • UML常见图例
  • 一文精通 Swagger 在 .NET 中的全方位配置与应用
  • Java NIO 核心精讲(上):Channel、Buffer、Selector 详解与 ByteBuffer 完全指南
  • 【3-3】流量控制与差错控制
  • Linux资源管理
  • JUC之CompletableFuture【上】
  • Orbbec---setBoolProperty 快捷配置设备行为
  • 设备树下的LED驱动实验
  • 从数据表到退磁:Ansys Maxwell中N48磁体磁化指南
  • 谷歌为什么要将Android的页面大小(Page Size)从传统的4KB升级至16KB
  • Go 进阶学习路线
  • 测试 Next.js 应用:工具与策略
  • 仲裁器设计(三)-- Weighted Round Robin 权重轮询调度
  • ASP4644稳压器的特性分析与系统测试方法研究
  • GPT-4.1旗舰模型:复杂任务的最佳选择及API集成实践
  • 【RustFS干货】RustFS的智能路由算法与其他分布式存储系统(如Ceph)的路由方案相比有哪些独特优势?