【数据结构与算法】two X 树的遍历以及功能实现(下)

简介: 【数据结构与算法】two X 树的遍历以及功能实现(下)
求二叉树的高度(BTreeHeight)

先比较一下以下的哪种代码更优:

方案一:

      在递归调用BTreeHeight函数之后,并没有对返回值进行保存和比较,而是直接返回了当前节点的左子树和右子树的高度中较大的一个加1。这样的实现虽然能够得到正确的结果,但是效率较低。因为在计算左子树的高度和右子树的高度时,每次都会重复递归调用 BTreeHeight函数

int BTreeHeight(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  return BTreeHeight(root->left) > BTreeHeight(root->right) ?
    BTreeHeight(root->left) + 1 : BTreeHeight(root->right) + 1;
}

为了更好地理解方案一的解释,这里写出一个不用三目运算符,但是等价于上述代码的代码

画图理解:

这里其实用到的是分治算法,通常分为三个步骤:

  1. 分解(Divide):将原问题划分为若干个规模较小的子问题。这一步通常在递归的过程中进行,直到问题足够简单,可以直接求解
  2. 解决(Conquer):递归地解决子问题。对于每个子问题,如果它的规模足够小,可以直接求解;否则,继续递归地将子问题划分为更小的子问题。
  3. 合并(Combine):将子问题的解合并得到原问题的解。这一步通常在递归的回溯过程中进行。

89b3c3b2c2e7416bbd7651dc5b2e17be.png

代码实现:

int BTreeHeight(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  int h = 0;
  if (BTreeHeight(root->left) > BTreeHeight(root->right))
  {
      h = BTreeHeight(root->left) + 1;
  }
  else
  {
    h = BTreeHeight(root->right) + 1;
  }
  return h;
}


力扣执行:

4147ec23044c4aa98ac67e85d8344300.png

方案二:

c9382f982ef04d588a585f1ef55fe95b.png

使用了两个变量leftHeightrightHeight分别保存了左子树和右子树的高度。通过递归调用BTreeHeight函数分别计算左子树和右子树的高度,并将结果保存在这两个变量中。然后比较leftHeightrightHeight的值,将较大值加1作为当前节点的高度返回。这样的实现避免了重复计算,提高了效率。

int BTreeHeight(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  int leftHeight = BTreeHeight(root->left);
  int rightHeight = BTreeHeight(root->right);
  return leftHeight > rightHeight ? leftHeight + 1 : rightHeight+ 1;
}


力扣执行:

f72d670ade4340d99a7268dcbd8b6808.png

二叉树第k层节点个数(BTreeLevelKSize)

子问题:

转换成左子树的第k-1层和右子树的右子树的第k-1层

结束条件:

  • k == 1且节点不为空
  • 节点为空

eedde8dc750e4df3909764c298de00ee.png

代码实现:

int BTreeLevelKSize(BTNode* root, int k)
{
  assert(k > 0);
  if (root == NULL)
    return 0;
  if (k == 1)
    return 1;
  return BTreeLevelKSize(root->left, k - 1) 
         + BTreeLevelKSize(root->right, k - 1);
}


二叉树查找值为x的节点(BTreeFind)

       思路:就如果根节点为空,直接返回NULL,如果找到了就返回x这个节点的地址。

从根节点开始遍历,先从左子树开始找,继续循环上述思路,如果节点不为NULL,但是节点不为x,那也是返回NULL,注意这个NULL是返回上一层的,谁调用它就返回给此函数。之后找右子树,也是一样的思路。

      左子树整体找完之后,从右子树整体开始找,重复上述过程。特别强调一个点,return返回的时候不会return到最外层,一定是逐层逐层返回的,没有跳跃的一个过程

画出递归展开图:

16fef971fff142c988eecbd1adc452f4.png

代码实现:

BTNode* BTreeFind(BTNode* root,BTDataType x)
{
  if (root == NULL)
    return NULL;
  if (root->data == x)
    return root;
  int ret1 = BTreeFind(root->left, x);
  if (ret1)
    return ret1;
  int ret2 = BTreeFind(root->right, x);
  if (ret2)
    return ret2;
  return NULL;
}


二叉树的层序遍历(LevelOrder)

层序遍历是用队列实现的,所以要使用队列的结构体,以下的结构体都要用到

typedef struct BTNode* QDataType;//注意这个地方的类型
typedef struct QueueNode
{ 
  QDataType data;
  struct QueueNode* next;
}QNode;
typedef struct Queue
{
  QNode* phead;
  QNode* ptail;
  int size;
}Queue;//表示队列整体,一个是出数据,一个是入数据.
typedef int BTDataType;
typedef struct BinaryTreeNode
{
  BTDataType data;
  struct BinaryTreeNode* left;
  struct BinaryTreeNode* right;
}BTNode;


画处解析图:

队列:先进先出

核心思路:上一层出时带下一层进队列

f90a027510264a93a139c3d9c7857a6e.png

画处解析图:

队列:先进先出

核心思路:上一层出时带下一层进队列

980ac4df898a47a8a6b16d99c6d5ecb4.png

代码实现:

void LevelOrder(BTNode* root)
{
  Queue q;//在函数内部,定义了一个队列q,并通过调用QueueInit函数对队列进行初始化。
  QueueInit(&q);
    //如果根节点root不为空,将根节点入队,即调用QueuePush函数将root指针插入到队列q中。
  if (root)
    QueuePush(&q,root);
  while (!QueueEmpty(&q))
  {
    BTNode* front = QueueFront(&q);//首先通过调用QueueFront函数获取队列q的队首元素,并将其赋值给指针变量front
    QueuePop(&q);//调用QueuePop函数将队首元素出队
    printf("%d ", front->data);//通过printf函数打印front指向的节点的数据值
    //如果front的左子节点不为空,将左子节点入队,
    //即调用QueuePush函数将front->left指针插入到队列q中   
        if (front->left)
      QueuePush(&q, front->left);//后面这个参数是一个值,不是地址
    //如果front的右子节点不为空,将右子节点入队,
    //即调用QueuePush函数将front->right指针插入到队列q中。
    if (front->right)
      QueuePush(&q, front->right);//后面这个参数是一个值,不是地址
//循环体内的操作完成后,继续下一次循环,直到队列q为空。
  }
//最后,打印换行符表示层序遍历结束,
//并调用QueueDestroy函数销毁队列q,释放内存。
  printf("\n");
  QueueDestroy(&q);
}


二叉树的销毁(BTDestroy)

      解析:要先判断根节点本身是否为空,为空就不销毁,返回。

       销毁是用到后序遍历的,因为中途删掉了根节点,那么左右指针就找不到了,所以后序遍历适合实现二叉树的销毁。

画图:


void BTDestroy(BTNode* root)
{
  if (root == NULL)
  {
    return NULL;
  }
  BTDestroy(root->left);
  BTDestroy(root->right);
  free(root);
}
判断一棵树是否为完全二叉树(BinaryTreeComplete)

非完全二叉树

7c3a8ef8dc2d40709c33e4ead8cd438d.png

完全二叉树

       思路一样的就不画动图了,只要前面遇到一次空,立即跳出循环,停止插入元素,然后检查后面的元素是否为空,后面全是空就是完全二叉树了。

ade67aa8f2ce4b2098c02dc11b6ce878.png

以下这个二叉树就不是完全二叉树了

c09506d68e684be0924ac2700681dee6.png

代码实现:

bool BTreeComplete(BTNode* root)
{
  Queue q;
  QueueInit(&q);
  if (root)
    QueuePush(&q,root);
  while (!QueueEmpty(&q))//遍历树直到找到第一个空节点
  {
  BTNode* front= QueueFront(&q);
  QueuePop(&q);
  //遇到空就跳出
  if (front == NULL)
  {
    break;
  }
  QueuePush(&q, front->left);
  QueuePush(&q, front->right);
  }
  // 检查后面的节点有没有非空
  // 有非空,不是完全二叉树
  while (!QueueEmpty(&q))//由于中途遇到空,所以跳出循环,这次循环是为了检查后面元素是否为空
  {
    BTNode* front = QueueFront(&q);
    QueuePop(&q);
    if (front)
    {
      QueueDestroy(&q);
      return false;
    } 
  }
  QueueDestroy(&q);
  return true;
}


执行:

b243a2d5ebd54dfc8d5305d68bd6ba98.png

  本篇到此结束,感谢来访!

相关文章
|
1天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
31 16
|
24天前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
16 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
21天前
|
Java C++
【数据结构】探索红黑树的奥秘:自平衡原理图解及与二叉查找树的比较
本文深入解析红黑树的自平衡原理,介绍其五大原则,并通过图解和代码示例展示其内部机制。同时,对比红黑树与二叉查找树的性能差异,帮助读者更好地理解这两种数据结构的特点和应用场景。
23 0
|
24天前
|
存储 算法
数据结构与算法学习十六:树的知识、二叉树、二叉树的遍历(前序、中序、后序、层次)、二叉树的查找(前序、中序、后序、层次)、二叉树的删除
这篇文章主要介绍了树和二叉树的基础知识,包括树的存储方式、二叉树的定义、遍历方法(前序、中序、后序、层次遍历),以及二叉树的查找和删除操作。
20 0
|
12天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
30天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于MSER和HOG特征提取的SVM交通标志检测和识别算法matlab仿真
### 算法简介 1. **算法运行效果图预览**:展示算法效果,完整程序运行后无水印。 2. **算法运行软件版本**:Matlab 2017b。 3. **部分核心程序**:完整版代码包含中文注释及操作步骤视频。 4. **算法理论概述**: - **MSER**:用于检测显著区域,提取图像中稳定区域,适用于光照变化下的交通标志检测。 - **HOG特征提取**:通过计算图像小区域的梯度直方图捕捉局部纹理信息,用于物体检测。 - **SVM**:寻找最大化间隔的超平面以分类样本。 整个算法流程图见下图。
|
9天前
|
人工智能 算法 数据安全/隐私保护
基于遗传优化的SVD水印嵌入提取算法matlab仿真
该算法基于遗传优化的SVD水印嵌入与提取技术,通过遗传算法优化水印嵌入参数,提高水印的鲁棒性和隐蔽性。在MATLAB2022a环境下测试,展示了优化前后的性能对比及不同干扰下的水印提取效果。核心程序实现了SVD分解、遗传算法流程及其参数优化,有效提升了水印技术的应用价值。
|
10天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于贝叶斯优化CNN-LSTM网络的数据分类识别算法matlab仿真
本项目展示了基于贝叶斯优化(BO)的CNN-LSTM网络在数据分类中的应用。通过MATLAB 2022a实现,优化前后效果对比明显。核心代码附带中文注释和操作视频,涵盖BO、CNN、LSTM理论,特别是BO优化CNN-LSTM网络的batchsize和学习率,显著提升模型性能。
|
15天前
|
存储
基于遗传算法的智能天线最佳阵列因子计算matlab仿真
本课题探讨基于遗传算法优化智能天线阵列因子,以提升无线通信系统性能,包括信号质量、干扰抑制及定位精度。通过MATLAB2022a实现的核心程序,展示了遗传算法在寻找最优阵列因子上的应用,显著改善了天线接收功率。