目录
- 单值二叉树
- 题目描述
- 题目思路及代码
- 相同的树
- 题目描述
- 题目思路及代码
- 对称二叉树
- 题目描述
- 题目思路及代码
- 另一棵树的子树
- 题目描述
- 题目思路及代码
- 二叉树的前序遍历
- 题目描述
- 题目思路及代码
- 二叉树的构建与遍历
- 题目描述
- 题目思路及代码
单值二叉树
题目描述
题目思路及代码
- 这道题的要求就是每个节点的值都要相等
- 如果我们想要整个二叉树相等,就要让二叉树的子树都相等。想要先判断子树,我们就用先序遍历。先遍历左子树,然后遍历右子树。然后将其每个子树的父节点和他们的孩子节点的值进行比较是否相等。当所有左子树和右子树都比较完了,那么整颗二叉树就是一颗单值二叉树。
- 如果当左右孩子的值其中一个不等于父节点的值,返回false
- -如果遇到节点为NULL,说明从根节点到该节点的值都相同
注意:
下面是代码:
/**
- Definition for a binary tree node.
- struct TreeNode {
- int val;
- struct TreeNode *left;
- struct TreeNode *right;
- };
*/
typedef struct TreeNode TreeNode;
bool isUnivalTree(struct TreeNode* root) {
if(root == NULL)
{
return true;
}
if(root->left && root->left->val != root->val || root->right && root->right->val != root->val)
{
return false;
}
return isUnivalTree(root->left) && isUnivalTree(root->right);
}
相同的树
题目描述
题目思路及代码
- 我们不光要求值相同,结构也要相同,也就是节点的位置也相同。
- 那我们就可以先从结构入手,如果两颗二叉树都是空树,那么他们一定结构并且值相同。直接返回true
- 如果两颗二叉树其中有一个不是空树,而另一个事空树,结构肯定不同,直接返回false。
- 结构判断完了,就判断值,两个指针分别从两颗二叉树的祖宗节点同时开始遍历他们的左右子树,如果A二叉树和B二叉树的左子树节点有一个节点不相同返回false,如果左子树节点都相同,但是右子树节点不相同返回false。
代码如下:
typedef struct TreeNode TreeNode;
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);
}
- 如果左子树或右子树一直没找到不相同的节点,直到空节点两个肯定结构和值相同,说明左子树或右子树是一颗相同的二叉树就返回true。也就是第一个if
对称二叉树
题目描述
题目思路及代码
- 是不是和上一道相同的二叉树有异曲同工之处。
- 就是把A二叉树的左子树节点与B二叉树的右子树节点比较,把A二叉树的右子树节点和B二叉树的左子树节点比较即可。就是改变了比较位置。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
typedef struct TreeNode TreeNode;
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->right) && isSameTree(p->right, q->left);
}
bool isSymmetric(struct TreeNode* root) {
return isSameTree(root->left, root->right);
}
- 可以看到就是你走左,我走右。你走右,我走左
另一棵树的子树
题目描述
题目思路及代码
- 如果想要找另一个二叉树是否为这一个二叉树的子树,那么就要先找到与那个二叉树祖宗节点相等的节点。然后再两个二叉树同时递归比较。
- 如果节与子树的祖宗节点相同,那么直接调用判断两个数是否相等的方法进行比较,不用再往子树递归。
- 同理,如果第一个节点不和子树祖宗节点相等,那就先递归左子树,在左子树中找是否有相等的节点。如果没有,就递归右子树寻找
- 如果递归遍历左右子树过程中,遍历到节点为NULL,说明左子树或右子树遍历完没找到与子树相同的节点。则左或右子树中没有匹配的节点。直接返回false。
typedef struct TreeNode TreeNode;
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){
if(root == NULL)
{
return false;
}
if(isSameTree(root,subRoot))
{
return true;
}
return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
- 这里||运算连接左右子树递归,就是左子树可能找不到,但是右子树可能找到。并且如果左子树找到了,右子树就不用找了。
二叉树的前序遍历
题目描述
题目思路及代码
- 注意读题,我们是需要把二叉树的节点,按前序遍历的顺序存储在一个数组中。
- 这个数组需要我们自己动态开辟,而如果要知道开辟多大空间,我们就需要二叉树一共有几个节点,这就可以用到链式二叉树讲的求节点个数算法。
- 在前序遍历递归过程中,我们需要控制数组的下标来决定存储节点的位置,这时候下标最好作为一个参数传递,在每次函数递归开辟函数栈帧的时候,用指针来接受下标,保证形参一定会改变实参,从而影响到上一次递归的函数栈帧的下标发生改变。
/**
* 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().
*/
typedef struct TreeNode TreeNode;
int BinaryTreeSize(TreeNode* root)
{
if(root == NULL)
{
return 0;
}
return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}
void _preorderTravelsal(TreeNode* root, int *arr, int* pi)
{
if(root == NULL)
{
return;
}
arr[(*pi)++] = root->val;
_preorderTravelsal(root->left, arr, pi);
_preorderTravelsal(root->right, arr, pi);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
//先求出二叉树节点个数
*returnSize = BinaryTreeSize(root);
//动态开辟空间
int* returnArr = (int*)malloc(sizeof(int) * (*returnSize));
//为避免在原函数递归重复开辟空间和调用方法,写一个子函数
int i = 0;
_preorderTravelsal(root, returnArr, &i);
return returnArr;
}
- 中序遍历和后序遍历一样的思路,把arr[*(pi)++] = root->val,放在递归函数中间或递归函数之后。
二叉树的构建与遍历
题目描述
题目思路及代码
- 按题目所说,我们需要将用户输入的先序遍历字符串按先序遍历的方式创建一个二叉树。然后我们再对构建的二叉树进行中序遍历。
- 这就要看怎么构建了:
第一种构建方式:
- 很明显C作为根节点a的右孩子并不满足是一个二叉树。
我们再来看看另一个构建方法:
- 按照如果不是构建到空节点就一直按照根左右的顺序,先构建根节点,然后构建左孩子结点,构建到空节点后,空节点不可能作为子树的根节点,返回子树根节点开始构建右孩子节点。从下往上构建子树,最后构建完所以子树就是一颗先序遍历顺序构建的二叉树。
TreeNode* CreatNode(char* arr, int* pi)
{
if(arr[*pi] == '#')
{
(*pi)++;
return NULL;
}
TreeNode* root = buyNode(arr[(*pi)++]);//构建节点
root->left = CreatNode(arr, pi);
root->right = CreatNode(arr, pi);
return root;
}
- 这里为空节点就回上一次递归开始构建右孩子节点/但是不要忘了将这个遍历先序序列数组的指针后移,便于下一次的访问
- 我们构建的同时就要开辟空间来存储节点,既然节点这么多。我们不妨封装一个函数来开辟空间存储节点。
TreeNode* buyNode(char x)
{
TreeNode* newnode = (TreeNode*)malloc(sizeof(TreeNode));
newnode->val = x;
newnode->left = newnode->right = NULL;
return newnode;
}
- 构建完成就可以中序遍历了.
void InOrder(TreeNode* root)
{
if(root == NULL)
{
return;
}
InOrder(root->left);
printf("%c ",root->val);
InOrder(root->right);
}
下面是整体代码:
#include<stdio.h>
#include<stdlib.h>
typedef struct TreeNode
{
char val;
struct TreeNode* left;
struct TreeNode* right;
}TreeNode;
TreeNode* buyNode(char x)
{
TreeNode* newnode = (TreeNode*)malloc(sizeof(TreeNode));
newnode->val = x;
newnode->left = newnode->right = NULL;
return newnode;
}
TreeNode* CreatNode(char* arr, int* pi)
{
if(arr[*pi] == '#')
{
(*pi)++;
return NULL;
}
TreeNode* root = buyNode(arr[(*pi)++]);
root->left = CreatNode(arr, pi);
root->right = CreatNode(arr, pi);
return root;
}
void InOrder(TreeNode* root)
{
if(root == NULL)
{
return;
}
InOrder(root->left);
printf("%c ",root->val);
InOrder(root->right);
}
int main()
{
char arr[100];
scanf("%s", arr);
//创建二叉树
int i = 0;
TreeNode* root = CreatNode(arr, &i);
InOrder(root);
return 0;
}