前言
学习完二叉树,就要做练习题,二叉树中的练习题都是比较简单的题目。所以说出这篇博客纯粹就是练习写博客,免得太长时间没写,手生疏了。至于为什么不偷懒,那是因为我觉得有点闲,一直玩也很无聊,就卷一下,假装努力让自己休息的心安理得。所以这又是人类心理中的非常恶心的一部分,但是我为什么又要把它讲出来呢?纯粹就是像写小剧场一样,想取悦一下读者。总之就是目的不单纯,而且非常的无聊,想要找乐子,所以水了一篇。反正我估计也没有谁会看前言什么的。所以,如果在评论区没有听到任何和这里讲的东西有关的话题的话,就说明其实是没有人人认真看博客的。这又是无聊人类的无聊的证明,完全就是想整点活。
一. 单值二叉树
Oj链接https://leetcode-cn.com/problems/univalued-binary-tree/
1. 题目描述
如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。
只有给定的树是单值二叉树时,才返回 true
;否则返回 false
。
示例 1:
输入:[1,1,1,1,1,null,1] 输出:true
示例 2:
输入:[2,2,2,5,2] 输出:false
提示:
- 给定树的节点数范围是
[1, 100]
。 - 每个节点的值都是整数,范围为
[0, 99]
。
2. 题解
题目也很简单,给你一颗二叉树,如果二叉树中每个节点中存储的数据都一样就返回true,不是就返回false。
我们可以分解问题,将一整颗树分解为,左子树、右子树和根。如果根为空,就说明该子树一定是单值二叉树。如果在一颗树中,左右子树都是单值二叉树,那么就只需要观察根节点,与它的左右节点就可以了。如果都相等那么说明该树也是单值二叉树,反之有一个不相等就说明不是返回false,整个树就都直接返回false。
分析完毕,进入代码阶段。
3. 代码
该结构相当于递归板的前序遍历。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
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);
}
代码无误:
二. 二叉树最大深度
OJ链接https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
这个题其实在之前博客中有过描述,也提供过代码,这里还是简单的再说一次。
1. 题目描述
给定一个二叉树 root
,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:3
提示:
- 树中节点的数量在
[0, 104]
区间内。 -100 <= Node.val <= 100
2. 题解
分别记录左右子树的高度,选择出最大的高度,然后该树的高度等于这个数加一。
易错点:如果没有记录左右子树高度,而是重新计算一遍,那么代码的时间复杂度会达到O(N)。那么就会运行就会超时,所以记录高度变得很重要。
3. 代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
// int max(int x, int y)
// {
// return x > y ? x : y;
// }
int maxDepth(struct TreeNode* root) {
if(root == NULL)
{
return 0;
}
int left = maxDepth(root->left); // 左子树高度
int right = maxDepth(root->right); // 右子树高度
return left > right ? left + 1 : right + 1;
}
代码无误:
三. 二叉树的前序遍历
这道题也是之前博客里有的,主要遍历思想都讲过了。
OJ链接https://leetcode-cn.com/problems/binary-tree-preorder-traversal/
1. 题目描述
给你二叉树的根节点 root
,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3] 输出:[1,2,3]
示例 2:
输入:root = [] 输出:[]
示例 3:
输入:root = [1] 输出:[1]
示例 4:
输入:root = [1,2] 输出:[1,2]
示例 5:
输入:root = [1,null,2] 输出:[1,2]
提示:
- 树中节点数目在范围
[0, 100]
内 -100 <= Node.val <= 100
2. 题解
和之前前序遍历不同的是我们需要把遍历的结果放到数组里而不是直接打印,因此我们需要确认数组得到大小,我们可以先后续遍历一次树确定树的大小,然后再前序遍历将数据存入。如果想不用递归的方式,也可以使用栈记录中间没有遍历过的节点,最后从栈里取出来,就能够用循环的方式,而不是递归。说简单也确实不难。
3. 代码
/**
* 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) // 确认树的大小
{
if(root == NULL)
{
return 0;
}
return TreeSize(root->left) + TreeSize(root->right) + 1;
}
void _preorderTraversal(int* a, struct TreeNode* root, int* i)// 实际上的前序遍历
{
if(root == NULL)
{
return;
}
a[*i] = root->val;
++*i;
_preorderTraversal(a, root->left, i);
_preorderTraversal(a, root->right, i);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int size = TreeSize(root);
int i = 0;
int* ret = (int*)malloc(sizeof(int) * size);
if(ret == NULL)
{
perror("malloc fail");
return ret;
}
_preorderTraversal(ret, root, &i);
*returnSize = size; // 返回数组的大小
return ret; // 返回数组指针
}
运行结果:
四. 检查两颗树是否相同
OJ链接https://leetcode-cn.com/problems/same-tree/
1. 题目描述
给你两棵二叉树的根节点 p
和 q
,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
示例 1:
输入:p = [1,2,3], q = [1,2,3] 输出:true
示例 2:
输入:p = [1,2], q = [1,null,2] 输出:false
示例 3:
输入:p = [1,2,1], q = [1,1,2] 输出:false
提示:
- 两棵树上的节点数目都在范围
[0, 100]
内 -104 <= Node.val <= 104
2. 题解
分别同时遍历两个数相同位置的节点,让然后比较最后全部相同返回true,有一个不同返回false。
根据根节点是否为空的判断:(1)都为空则表示,该节点相同。(2)有一个不为空,另一个为空表示,该节点不相同。
如果节点都存在,就比较节点中存储的数据是否相同,并且向下遍历左右子树,直到遍历完成所有节点返回true,或者途中发现有不相等的节点返回false。
3. 代码
/**
* 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) // 都为空返回true
{
return true;
}
else if(p == NULL || q == NULL) // 其中一个不为空,返回false
{
return false;
}
else if(p->val != q->val) // 都存在,但节点不相等返回false
{
return false;
}
else // 节点值相等, 继续比较左右子树
{
return isSameTree(p->left, q->left)
&& isSameTree(p->right, q->right);
}
}
运行结果:
五. 对称二叉树
OJ链接https://leetcode-cn.com/problems/symmetric-tree/
1. 题目描述
给你一个二叉树的根节点 root
, 检查它是否轴对称。
示例 1:
输入:root = [1,2,2,3,4,4,3] 输出:true
示例 2:
输入:root = [1,2,2,null,3,null,3] 输出:false
提示:
- 树中节点数目在范围
[1, 1000]
内 -100 <= Node.val <= 100
2. 题解
比较左右子树是否对称,就相当于把左右子树分为两个树,然后就和第四题一样比较节点就可以了。只是右树传的节点需要和左树传的节点相反,直接将第四题的代码复制过来加以修改即可。
3. 代码
/**
* 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;
}
else if(p == NULL || q == NULL)
{
return false;
}
else if(p->val != q->val)
{
return false;
}
else
{
return isSameTree(p->left, q->right)
&& isSameTree(p->right, q->left);
}
}
bool isSymmetric(struct TreeNode* root) {
return isSameTree(root->left, root->right);
}
运行结果:
六. 另一颗树的子树
OJ链接https://leetcode-cn.com/problems/subtree-of-another-tree/
1. 题目描述
给你两棵二叉树 root
和 subRoot
。检验 root
中是否包含和 subRoot
具有相同结构和节点值的子树。如果存在,返回 true
;否则,返回 false
。
二叉树 tree
的一棵子树包括 tree
的某个节点和这个节点的所有后代节点。tree
也可以看做它自身的一棵子树。
示例 1:
输入:root = [3,4,5,1,2], subRoot = [4,1,2] 输出:true
示例 2:
输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2] 输出:false
提示:
root
树上的节点数量范围是[1, 2000]
subRoot
树上的节点数量范围是[1, 1000]
-104 <= root.val <= 104
-104 <= subRoot.val <= 104
2. 题解
首先我们需要遍历root树,找到值和subRoot树头节点相同的节点,然后查找子树是否相同。
3. 代码
/**
* 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;
}
else if(p == NULL || q == NULL)
{
return false;
}
else if(p->val != q->val)
{
return false;
}
else
{
return isSameTree(p->left, q->left)
&& isSameTree(p->right, q->right);
}
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
if(root == NULL)
{
return false;
}
if(root->val == subRoot->val && isSameTree(root, subRoot))
{
return true;
}
return isSubtree(root->left, subRoot)
|| isSubtree(root->right, subRoot);
}
运行结果:
七. 二叉树的构建及遍历
细心的同学可能发现了,这个题我又又又编过了,前序遍历建造树,小case好吧。
1. 题目描述
描述
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
输入描述:
输入包括1行字符串,长度不超过100。
输出描述:
可能有多组测试数据,对于每组数据, 输出将输入字符串建立二叉树后中序遍历的序列,每个字符后面都有一个空格。 每个输出结果占一行。
示例1
输入:
abc##de#g##f###
复制输出:
c b e g d f a
2. 题解
前序遍历,遇到‘#’表示空,需要注意的是,用递归遍历节点的话需要将数组的指针用指针传递,否则函数结束之后,数组的位置没有改变会遍历错误。
3. 代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>
typedef char BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
// 申请节点
BTNode* BuyNode(BTDataType x)
{
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
if(newnode == NULL)
{
perror("BuyNode malloc fail");
exit(1);
}
newnode->_data = x; // 将数据置入
newnode->_left = newnode->_right = NULL; //初始化指针
return newnode;
}
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
if(a[*pi] == '#')
{
++*pi;
return NULL;
}
BTNode* node = BuyNode(a[*pi]);
++*pi;
node->_left = BinaryTreeCreate(a, n, pi);
node->_right = BinaryTreeCreate(a, n, pi);
return node;
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
assert(root);
if(*root == NULL)
{
return;
}
//后序遍历
BinaryTreeDestory(&((*root)->_left));
BinaryTreeDestory(&((*root)->_right));
free(*root); // 删除根节点空间并置空
*root = NULL;
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
if(root == NULL)
{
return;
}
BinaryTreeInOrder(root->_left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
int main() {
char front[110] = {0};
while (scanf("%s", front) != EOF) {
int size = strlen(front);
BTNode* bt;
int i = 0;
bt = BinaryTreeCreate(front, size, &i);
BinaryTreeInOrder(bt); // 中序遍历
printf("\n");
BinaryTreeDestory(&bt);
}
return 0;
}
在遍历完树的时候,用完顺便销毁,非常不错。
运行结果:
作者结语
终于又到了作者说骚话的环节了,太激动了。代码啥的之前都写过了,就是copy过来的。二叉树遍历的思路都是相同的,学会了三种遍历之后,这上边的题目都是小意思,没什么难度。所以如果有人问,二叉树OJ怎么样?请回答:“非常简单”。
下一篇就是排序的部分了。这几天我先写个大概出来,然后下周发出来。总之就是“非常简单”。