【C/C++ 数据结构】-二叉树(2)

作者:学Java的冬瓜
博客主页:☀冬瓜的主页🌙
专栏:【C/C++ 数据结构和算法】

在这里插入图片描述

前言

今天的这篇博客,主要是二叉树的相关习题,包括笔试选择题,OJ题。对于二叉树的基本概念、求二叉树的特征,二叉树的性质,请看上一篇博客。

链接:
上篇博客:二叉树的基本概念及基本特征的求法

一、二叉树遍历选择题

1、前序+中序=》二叉树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、后序+中序=》二叉树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、OJ题

LeetCode题

题1:二叉树的前序遍历

链接:
LeetCode144.二叉树的前序遍历

法一:利用指针计算返回数组大小
int TreeSize(struct TreeNode* root)
{
    return root==NULL? 0 : TreeSize(root->left)+TreeSize(root->right)+1;
}

void _PrevOrder(struct TreeNode* root, int* a, int* pi)
{
    if(root==NULL)
    {
        return;
    }

    a[*pi] = root->val;
    (*pi)++;

    _PrevOrder(root->left, a, pi);
    _PrevOrder(root->right, a,pi);
}

//注意1:下面的struct TreeNode* root是输入型参数,int* returnSize是输出型参数
//因为只返回a不确定这个数组到底有多大,所以再设置一个输出型参数。
//达到返回两个数据的作用,从而确定空间的大小
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{    
    //注意2:先计算这棵树的节点个数,从而确定数组大小
    int size=TreeSize(root);
    
    int* a = (int*)malloc(size*sizeof(int));
    
    //注意3:防止每次递归都创建a空间,所以不调用自己,另外用一个函数实现递归遍历
    int i=0;
    _PrevOrder(root, a, &i);
    
    //注意4:设置输出型参数,返回两个数据,从而确定数组大小
    *returnSize=i;
    return a;
}
法二:利用全局变量计算数组大小

注意:
1、因为有多组测试用例,每次使用完全局变量,都需要置0
2、如果用局部变量统计数组个数,那么递归中归的时候i是上一层的i,而不是下一层计算过后的i。

int TreeSize(struct TreeNode* root)
{
    return root==NULL? 0 : TreeSize(root->left)+TreeSize(root->right)+1;
}

int i=0;
void _PrevOrder(struct TreeNode* root, int* a)
{
    if(root==NULL)
    {
        return;
    }

    a[i] = root->val;
    i++;

    _PrevOrder(root->left, a);
    _PrevOrder(root->right, a);
}


int* preorderTraversal(struct TreeNode* root, int* returnSize){
    //注意重点1:因为有多组测试用例,所以用全局变量统计数组元素个数
    //在每一个用例使用完后,这个全局变量都需要置0
    i=0;

    int size=TreeSize(root);
    int* a = (int*)malloc(size*sizeof(int));

    _PrevOrder(root, a);

    *returnSize=i;
    return a;
}

题2:二叉树的中序遍历

链接:
LeetCode94.二叉树的中序遍历

说明:和前序遍历一样,用了指针计算数组大小,然后去访问

int TreeSize(struct TreeNode* root)
{
    return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

void _PrevOrder(struct TreeNode* root, int* a, int* pi)
{
    if (root == NULL)
        return;


    _PrevOrder(root->left, a, pi);

    a[*pi] = root->val;
    (*pi)++;

    _PrevOrder(root->right, a, pi);
}

int* inorderTraversal(struct TreeNode* root, int* returnSize)
{
    int size = TreeSize(root);
    int* a = (int*)malloc(size * sizeof(int));

    int i = 0;
    _PrevOrder(root, a, &i);

    *returnSize = i;
    return a;
}

题3:二叉树的后序遍历

链接:
LeetCode145.二叉树的后序遍历

说明:和前序遍历一样,用了指针来表示数组大小,然后去访问

int TreeSize(struct TreeNode* root)
{
    return root==NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

void _PostOrder(struct TreeNode* root, int* a, int* pi)
{
    if(root==NULL)
        return;

    _PostOrder(root->left, a, pi);
    _PostOrder(root->right, a, pi);

    a[*pi] = root->val;
    (*pi)++;
}

int* postorderTraversal(struct TreeNode* root, int* returnSize){
    int size = TreeSize(root);
    int* a = (int*)malloc(size*sizeof(int));

    int i=0;
    _PostOrder(root, a, &i);

    *returnSize = i;
    return a;
}

题4:平衡二叉树

链接:
LeetCode110.平衡二叉树

说明:平衡二叉树通俗来讲就是这棵树的节点分布均匀。在规律上:
一棵平衡二叉树,每个结点的左右子树的高度差的绝对值不超过1

思路

  1. 根节点开始判断,左右子树的高度差,不大于1,然后再进入子树的检查
  2. 遇到NULL前全都符合高度差不大于1,则开始归。如果高度差大于1,那么直接层层返回false
  3. 如果递归完都符合,那就是到了最开始的根节点处的return那一行,都为真。即return true。完全符合条件。
int maxDepth(struct TreeNode* root)
{
    if(root==NULL)
        return 0;

    int leftdepth = maxDepth(root->left);
    int rightdepth = maxDepth(root->right);

    return leftdepth>rightdepth ? leftdepth+1 : rightdepth+1;
}

//判断是否是平衡二叉树:需要计算当前节点的左右子树的高度差是否小于2,然后利用分治原理
bool isBalanced(struct TreeNode* root){
    //注意3:
    //1>当从树的根节点检查到叶节点都符合条件,
    //然后检查到叶节点的下一层为NULL,那么这棵子树是符合条件的
    //2>如果最开始的根节点root==NULL,那也是返回true
    if(root==NULL)
        return true;

    //注意1:当前节点不为空,就计算它的左右子树高度差
    int leftdepth = maxDepth(root->left);
    int rightdepth = maxDepth(root->right);

    //注意2:判断左右子树的高度差是否符合条件
    //不符合就直接返回false,符合条件则进入递归,进行子树的检查
    return (abs(leftdepth-rightdepth) < 2 
    && isBalanced(root->left) 
    && isBalanced(root->right));
}

牛客题

题5:二叉树的遍历

链接:
牛客.二叉树的遍历

要求:根据前序遍历(+#表示空)的字符串,创建二叉树,并完成中序遍历

思路

  1. pi(传址调用)来作为字符串下标,防止字符归时重复访问。
  2. 先判断当前字符,不是#,开始创建树的节点,然后该节点的left和right=递归创建树函数,开始进入递归。
  3. 每次当前节点的左右子树都创建完(以NULL为叶子节点的left和right)后。就返回当前节点,让上一层的调用来接收
  4. 如果遇到了#,说明这棵树在这个节点的方向已经完成,return NULL,让上一层调用接收。
#include <stdio.h>
#include <stdlib.h>

typedef char TDataType;
typedef struct TreeNode
{
    TDataType val;
    struct TreeNode* left;
    struct TreeNode* right;
}TreeNode;

//重点1:
//创建二叉树,要用到递归,除了传二叉树的根节点,还要用pi(统计)来保证str字符串在归时
//str指针指向的字符不会回到之前的字符
//从而保证一个一个字符的访问,每个字符只访问一次
TreeNode* CreatTree(char* str, int* pi)
{
    //重点5:当遇到字符#时,表示上一个节点的当前子树为NULL
    //完成最开始的递归中的递,然后归,然后可能递可能归。
    if(str[*pi] == '#')
    {
        (*pi)++;
        return NULL;
    }
    
    //重点2:当str字符串当前字符为有效字符(不为#)时,创建树的节点,
    //并把当前字符赋值给节点的val
    TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
    if(root==NULL)
    {
        printf("malloc fail\n");
        exit(-1);
    }
    
    root->val = str[*pi];
    (*pi)++;
    
    //重点3:创建新的节点后,开始进入递归,创建这个节点的左右子树
    root->left = CreatTree(str, pi);
    root->right = CreatTree(str, pi);
    
    //重点4:当前这个节点及其左右子树创建完成后,返回该节点
    return root;
}

void InOrder(TreeNode* root)
{
    if(root==NULL)
    {
        return;
    }
    
    InOrder(root->left);
    printf("%c ",root->val);
    InOrder(root->right);
}

int main()
{
    char str[100];
    scanf("%s",str);
    int i = 0;
    TreeNode* tree = CreatTree(str, &i);
    InOrder(tree);
    return 0;
}

最后:大家国庆快乐啊!今天女篮虽然没有打赢,但她们站在赛场上的那一刻,我就觉得已经是胜利了,真的很棒!希望祖国更加繁荣富强,也希望大家都越来越好!

### 回答1: C/C++ 是应用广泛的编程语言,其在数据结构应用方面也十分重要。面试相关的 C/C++ 数据结构问题主要围绕数组、链表、二叉树和图等方面。以下是一些常见问题及其解答: 1. 如何反转一个单向链表? 答:可以使用三个指针来实现:cur 代表当前节点,pre 代表上一个节点,next 代表下一个节点。每次遍历时,将 cur 的 next 指向 pre,然后将三个指针分别向后移动即可。 2. 如何判断两个链表是否相交,并找出相交的点? 答:可以分别遍历两个链表,得到各自的长度。然后让长的链表先走 n 步,使得两个链表剩余的长度相等。接下来同时遍历两个链表,比较节点是否相同即可找出相交的点。 3. 如何判断一个二叉树是否为平衡二叉树? 答:可以计算每个节点的左右子树深度差,如果任何一个节点的深度差大于1,则此树不是平衡二叉树。可以使用递归实现,每次计算当前节点的深度,然后递归判断其左右子树是否平衡。 4. 如何实现图的深度优先搜索(DFS)和广度优先搜索(BFS)算法? 答:DFS 可以使用递归实现。从某个节点开始,逐个访问其未被访问的邻接节点,并将其标记为已访问。然后对每个未被访问的邻接节点递归调用 DFS 函数。BFS 可以使用队列实现。从某个节点开始,将其加入队列,并标记为已访问。然后从队列弹出节点,并访问其所有未被访问的邻接节点,并将其加入队列。重复此过程直到队列为空。 以上是一些常见的 C/C++ 数据结构面试问题及其解答。在面试,除了掌握相关算法数据结构知识外,还需多做练习和积累经验,才能更好地应对各种面试问题。 ### 回答2: C语言是一种用于编写系统级程序的高级编程语言,具有简单、高效、灵活等特点,是许多操作系统、编译器等软件的首选语言,也是许多企业在进行面试时重点考察的技能。在C/C++数据结构面试题,经常会涉及到各种数据结构相关的算法和应用,测试面试者的算法思维能力和实现能力。 其,常见的数据结构包括链表、栈和队列、二叉树、搜索树、哈希表等。在面试时,会常常涉及代码设计和实现,比如实现链表的插入、删除、查找操作,实现二叉树的遍历、查找操作等。 此外,在数据结构面试,还经常涉及排序和查找算法,如冒泡排序、快速排序、归并排序、二分查找、哈希查找等。同时,面试者还需要解决一些较为复杂的算法问题,如图的最短路径问题,最小生成树问题等。 总之,C/C++数据结构面试题涵盖了运用数据结构的各种算法和实现方法,需要面试者具备扎实的编程基础和算法思维能力。在备战面试时,可以多做练习,熟悉常用的数据结构算法,提高理解和实现能力,从而更好地应对面试挑战。 ### 回答3: 面试过程常见的C/C++数据结构面试题有很多。以下就介绍几个常见的题目并给出解答。 1. 求两个有序数组的位数 题目描述:给定两个升序排列的整形数组,长度分别为m和n。实现一个函数,找出它们合并后的位数。时间复杂度为log(m+n)。 解答:这个问题可以使用二分法求解。首先,我们可以在两个数组分别选出所谓的间位置,即(i+j)/2和(k+l+1)/2,其i和j分别是数组A的起始和结束位置,k和l分别是数组B的起始和结束位置。判断A[i+(j-i)/2]和B[k+(l-k)/2]的大小,如果A的间元素小于B的间元素,则位数必定出现在A的右半部分以及B的左半部分;反之,则必定出现在A的左半部分以及B的右半部分。以此类推,每一次都可以删去A或B的一半,从而达到对数级别的时间复杂度。 2. 堆排序 题目描述:对一个长度为n的数组进行排序,时间复杂度为O(nlogn)。 解答:堆排序是一种常用的排序算法,在面试也经常被考察。堆排序的具体过程是首先将数组构建成一个最大堆或最小堆,然后不断将堆顶元素与最后一个元素交换,并将最后一个元素从堆剔除。这样,每次剔除后,堆都会重新调整,使得剩下的元素仍然保持堆的性质,直到堆只剩下一个元素为止。 3. 链表反转 题目描述:反转一个单向链表,例如给定一个链表: 1->2->3->4->5, 反转后的链表为: 5->4->3->2->1。 解答:链表反转题目也是非常常见,其思路也比较简单。遍历链表,将当前节点的next指针指向前一个节点,同时记录当前节点和前一个节点,直至遍历到链表末尾。 以上这三个问题分别从二分法、堆排序和链表三个方面介绍了常见的C/C++数据结构面试题,希望能帮助面试者更好地准备面试。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

学Java的冬瓜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值