C语言描述数据结构 —— 二叉树(4)基础OJ题

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;
}

  • 34
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小龙向钱进

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值