1.单值二叉树
最容易想到的方法就是挨个遍历,只要有一个值不相等,那么就必定不是单值二叉树。而层序遍历便非常契合这种方式。只不过还是那句话,在C语言的库中是没有队列的,所以我们必须手动构造队列。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
//链表的结点
typedef struct TreeNode* QueueData;
typedef struct QueueNode
{
QueueData val;
struct QueueNode* next;
}QueueNode;
//存储头、尾结点地址的指针
typedef struct HeadTail
{
QueueNode* head;
QueueNode* tail;
int size;//记录队列有几个元素
}HeadTail;
void QueueInit(HeadTail* p);//初始化队列
void QueuePush(HeadTail* p,QueueData x);//进入队列
QueueData QueueHead(HeadTail* p);//获取队头元素
QueueData QueueTail(HeadTail* p);//获取队尾元素
void QueuePop(HeadTail* p);//删除操作,出队
bool QueueEmpty(HeadTail* p);//检查队列是否为空
int QueueSize(HeadTail* p);//获取队列元素个数
void QueuePopTail(HeadTail* p);
void QueueDestroy(HeadTail* p);//销毁队列
//初始化
void QueueInit(HeadTail* p)
{
assert(p);
p->head = p->tail = NULL;
p->size = 0;
}
//队列尾插
void QueuePush(HeadTail* p, QueueData x)
{
assert(p);
//创建一个新结点
QueueNode* newnode = (QueueNode*)calloc(1, sizeof(QueueNode));
assert(newnode);
newnode->val = x;
newnode->next = NULL;
//如果链表内没有结点,头尾指针指向同一个结点
if (p->head == NULL)
{
p->head = newnode;
p->tail = newnode;
}
//否则尾指针需要变化
else
{
p->tail->next = newnode;
p->tail = newnode;
}
p->size++;
}
//获取队头元素
QueueData QueueHead(HeadTail* p)
{
assert(p);
assert(!QueueEmpty(p));
return p->head->val;
}
//获取队尾元素
QueueData QueueTail(HeadTail* p)
{
assert(p);
assert(!QueueEmpty(p));
return p->tail->val;
}
//删除、出队
void QueuePop(HeadTail* p)
{
assert(p);
assert(!QueueEmpty(p));
//如果链表只有一个结点
if (p->head == p->tail)
{
free(p->head);
p->head = p->tail = NULL;
}
//否则进行头删
else
{
QueueNode* cur = p->head;
p->head = p->head->next;
free(cur);
cur = NULL;
}
p->size--;
}
//检测队列是否为空
bool QueueEmpty(HeadTail* p)
{
assert(p);
return p->head == NULL && p->tail == NULL;
}
//获取队列元素个数
int QueueSize(HeadTail* p)
{
assert(p);
return p->size;
}
//销毁队列
void QueueDestroy(HeadTail* p)
{
assert(p);
QueueNode* cur = p->head;
while (cur)
{
QueueNode* del = cur;
cur = cur->next;
free(del);
}
}
bool isUnivalTree(struct TreeNode* root){
HeadTail q;
QueueInit(&q);//初始化
//如果根节点为空,自然没有值可以比较了,所以返回true
if(root == NULL)
{
return true;
}
//不为空则入队
QueuePush(&q,root);
//以第一个根节点为基准与其他节点比较
struct TreeNode* func=root;
while(!QueueEmpty(&q))
{
struct TreeNode* tmp = QueueHead(&q);
QueuePop(&q);
//如果值不相等,则返回false
if(func->val != tmp->val)
{
return false;
}
if( tmp->left != NULL)
{
QueuePush(&q,tmp->left);
}
if(tmp->right != NULL)
{
QueuePush(&q,tmp->right);
}
}
QueueDestroy(&q);
//否则返回true
return true;
}
我们还可以使用递归遍历。其原理就是比较每棵树的根节点和左右子树的根节点的值,但凡出现一对不相等的值,那么必定不是单值二叉树。
bool isUnivalTree(struct TreeNode* root){
if(root == NULL)
{
return true;
}
//确保左子树不为空才进入比较
if(root->left != NULL)
{
if(root->val != root->left->val)
{
return false;
}
}
//确保右子树不为空才进入比较
if(root->right != NULL)
{
if(root->val != root->right->val)
{
return false;
}
}
//递归进入下一颗树的对比
return isUnivalTree(root->left) && isUnivalTree(root->right);
}
2.相同的树
我们同样可以采取层序遍历的思路来走。但是因为过于麻烦,我们还是采用递归的方法去解题。如何判断两颗树相同是非常简单的。首先就是比较两颗树中的同一个节点位置是否同时存在或同时不存在,如果不符合两种情况就必定不是相同的树了。其次便是比较值。
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
//判断是否同时不存在
if(p==NULL && q==NULL)
{
return true;
}
//不是同时存在或同时不存在则直接返回false
else if(p==NULL || q==NULL)
{
return false;
}
//如果同时存在,但值不相等,返回false
if(p->val != q->val)
{
return false;
}
//如果同时存在并且值相等,继续往下递归
return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
3.对称二叉树
我们以一个例子来确定什么情况下才算对称二叉树。
//我们将判断树是否相同的代码拷贝过来并做一些修改
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
//判断是否同时不存在
if(p==NULL && q==NULL)
{
return true;
}
//不是同时存在或同时不存在则直接返回false
else if(p==NULL || q==NULL)
{
return false;
}
//如果同时存在,但值不相等,返回false
if(p->val != q->val)
{
return false;
}
//如果同时存在并且值相等,继续往下递归
//注意这里是修改过的
return isSameTree(p->left,q->right) && isSameTree(p->right,q->left);
}
bool isSymmetric(struct TreeNode* root){
if(root == NULL)
{
return true;
}
return isSameTree(root->left,root->right);
}
4.二叉树的前序遍历
可以看到题目的要求与我们自己写的前序遍历不太一样,但这并不影响我们的解题。
void PreTraval(struct TreeNode* root,int* a,int* pi)
{
if(root == NULL)
{
return NULL;
}
a[*pi]=root->val;
(*pi)++;
PreTraval(root->left,a,pi);
PreTraval(root->right,a,pi);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
//题目要求最大空间是 100
int* ret = (int*)malloc(sizeof(int)*100);
//数组下标
int i=0;
//使用传址调用
PreTraval(root,ret,&i);
*returnSize=i;
return ret;
}
那么中序遍历、后序遍历我就省略了,大家只需要改一下数组赋值的位置即可。
5.另一棵树的子树
这道题的思路非常简单,我们看图解:
我们可以观察代码来深度理解我们的思路。
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
//判断是否同时不存在
if(p==NULL && q==NULL)
{
return true;
}
//不是同时存在或同时不存在则直接返回false
else if(p==NULL || q==NULL)
{
return false;
}
//如果同时存在,但值不相等,返回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);
}
6.二叉树遍历
我们要确定一件事,那便是前序遍历是可以确定根节点的。我们可以试着逆向思维。前序遍历,返回条件是根节点的左右子树都为空,那么我们遍历是为了访问,那么构建便是为了链接。那么这就很简单了。
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef char BinaryData;
typedef struct Node
{
BinaryData val;
struct Node* left;
struct Node* right;
}Node;
Node* BinaryCreate(char* str,int* pi)
{
if(str[*pi] == '#')
{
(*pi)++;
return NULL;
}
//如果前序遍历的结果不为空,则创建一个节点
//用来存储前序遍历的结果
Node* root = (Node*)malloc(sizeof(Node));
assert(root);
root->val=str[*pi];
(*pi)++;
//往下递归构建二叉树
root->left=BinaryCreate(str,pi);
root->right=BinaryCreate(str,pi);
return root;
}
void MidTraval(Node* root)
{
if(root == NULL)
{
return;
}
MidTraval(root->left);
printf("%c ",root->val);
MidTraval(root->right);
}
int main()
{
char str[101]={0};
scanf("%s",str);
getchar();
int i=0;//数组下标
Node* root = BinaryCreate(str,&i);//构建二叉树
MidTraval(root);
return 0;
}