【数据结构与算法】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

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

相关文章
|
7月前
|
机器学习/深度学习 存储 Kubernetes
【重磅发布】AllData数据中台核心功能:机器学习算法平台
杭州奥零数据科技有限公司成立于2023年,专注于数据中台业务,维护开源项目AllData并提供商业版解决方案。AllData提供数据集成、存储、开发、治理及BI展示等一站式服务,支持AI大模型应用,助力企业高效利用数据价值。
|
8月前
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
221 3
 算法系列之数据结构-Huffman树
|
8月前
|
存储 自然语言处理 数据库
【数据结构进阶】AVL树深度剖析 + 实现(附源码)
在深入探讨了AVL树的原理和实现后,我们不难发现,这种数据结构不仅优雅地解决了传统二叉搜索树可能面临的性能退化问题,还通过其独特的平衡机制,确保了在任何情况下都能提供稳定且高效的查找、插入和删除操作。
657 19
|
10月前
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
345 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
10月前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
292 12
|
10月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
189 10
|
10月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
428 3
|
1月前
|
机器学习/深度学习 算法 机器人
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
182 0
|
1月前
|
数据采集 分布式计算 并行计算
mRMR算法实现特征选择-MATLAB
mRMR算法实现特征选择-MATLAB
139 2
|
2月前
|
传感器 机器学习/深度学习 编解码
MATLAB|主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性
MATLAB|主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性
191 3

热门文章

最新文章