数据结构之二叉树的实现(二)

简介: 之前给大家介绍了有关堆的实现,那么这里小编就给大家带来今天的重头戏——二叉树的实现,以及介绍。

2.7 计算叶子节点个数

这里我们采取的也是分治的思想,相信在经过前两道题的讲解,大家对于分治思想已经有了初步的了解,那么我们这里的思考是,我们的叶子节点等于左子树+右子树的叶子节点个数,那么我们怎么判断叶子节点呢?在了解过叶子节点的特征我们可以知道,叶子节点没有孩子节点。


这里的代码如下:

int BinaryTreeLeafSize(BTNode* root)
{
  if (root == NULL)
  {
  return 0;
  }
  int leftn = BinaryTreeLeafSize(root->left);
  int rightn = BinaryTreeLeafSize(root->right);
  if (root->left == NULL && root->right == NULL)
  {
  return leftn+rightn + 1;
  }
  return leftn + rightn;
}

对于计算叶子节点的个数,为了避免递归的重复访问,我们还是采取用变量记录值的方法,这里返回图如下。



大家可以依据这个过程去理解递归。


2.8 计算树第k层的节点数

这里我们采用的思想还是分治思想,那么这里我们的思路如下

得到一棵树第k层节点数,相当于是我们得到左子树的第k-1层的节点数加上右子树的第k-1层节点数,那么我的代码如下:

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

那么我们这里还需要考虑到两个情况:


情况一:也就是我们的第k层大于树的深度,那么这里势必会访问到空节点,那么我们只需要在空节点就返回即可,如果k大于树的深度,那么我们返回的值也是0。如果是我们需要统计的那一层某个节点的某个孩子是空节点,那么我们返回的是那个空节点返回的值加上1,这里也是不影响我们统计第k层节点数的。


情况二:当k=1时,我们此时访问的也是第k层的节点,那么我们一访问到第k层的节点就需要返回1。


这里的返回图我浅显的给大家画一下



大家配合代码以及递归展开图理解一下。


2.9 以内容查询树内的某个节点

这里我相信大家已经猜到了,我们这里采用的还是分治思想,那么我们这里的思路是:从左右子树出发对节点进行访问,访问到我们的节点内容就返回该节点地址,否者返回空。


BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
  if (root == NULL)
  {
  return NULL;
  }
  BTNode* leftn = BinaryTreeFind(root->left, x);
  if (leftn)
  {
  return leftn;
  }
  BTNode* rightn = BinaryTreeFind(root->right, x);
  if (rightn)
  {
  return rightn;
  }
  return NULL;
}


那么我们这里开始访问的是左子树,这里每次对访问的节点进行记录,其次我们这里就会对记录值进行判断,如果为空,不进行返回,则说明我们访问到了空节点,如果不为空则说明我们已经找到了我们所需要的节点,那么这里就会返回我们所需要的值,如果将左右子树遍历我结束,还没有获得我们所查找值,那么就会返回空。


返回图如下:



大家可以自己画一画递归展开图理解一下。


2.10 层序遍历

对于层序遍历我们这里需要使用到我们之前编写的队列这个结构,我们这里的思路是,出上一层,入下一层。那么这里就能合理的将树按照层序遍历的方式进行数据访问。在使用之前我们需要进行一个操作就是我们需要构建一个存储树这种节点的队列,至于队列的介绍,大家可以翻看小编主页的另外一篇文章。那么这里的代码如下:


void BinaryTreeLevelOrder(BTNode* root)
{
  Queue q;
  QueueInit(&q);
  QueuePush(&q,root);
  while (!QueueEmpty(&q))
  {
  BTNode* front = QueueFront(&q);
  QueuePop(&q);
  printf("%d ", front->data);
  if (front->left)
  {
    QueuePush(&q, front->left);
  }
  if (front->right)
  {
    QueuePush(&q, front->right);
  }
  }
  QueueDestroy(&q);
}

但是这里由于我们存储的是树的节点,所以我们需要将队列的存储元素类型改为我们树的指针类型。


如下:


typedef struct BinaryTreeNode* QDataType;//存储类型修改成树的结构的指针类型
typedef struct QListNode
{
  struct QListNode* _pNext;
  QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
  QNode* _front;
  QNode* _rear;
  int size;
}Queue;

这里具体的过程我举例一组例子给大家看看具体过程:



这里大家配合代码理解一下。


2.11 判断是否为完全二叉树

由于这里我们判断一棵树是二叉树或者为完全二叉树,那么我们就要利用完全二叉树的特殊性,这里我们可以发现我们利用层序遍历,完全二叉树的空节点是连续的,那么我们就可以利用这点来判断完全二叉树。


但是这里我们与上面的层序遍历不一致,这里由于我们要判断空是否连续,所以我们需要把空节点也入队列,那么这里我们入队列的判断条件就与我们上方的层序遍历不一致。


这里先给大家看代码,我再给大家讲解代码思路


bool BinaryTreeComplete(BTNode* root)
{
  //思路完全二叉树按层序走,非空节点一定是连续的
  Queue q;
  QueueInit(&q);
  QueuePush(&q, root);
  BTNode* front;
  while (!QueueEmpty(&q))//入队列中的的节点,如果访问到空节点就结束循环
  {
  front = QueueFront(&q);
  QueuePop(&q);
  if (front == NULL)
  {
    break;
  }
  QueuePush(&q, front->left);
  QueuePush(&q, front->right);
  }
  while (!QueueEmpty(&q))//访问到空节点的元素,就不断出队列,直到队列为空还没有访问到除空节点 
                             //以外的节点,说明是完全二叉树,否则不是
  {
  QueuePop(&q);
  front = QueueFront(&q);
  if (front != NULL)
  {
    QueueDestroy(&q);
    return false;
  }
  }
  QueueDestroy(&q);
  return true;
}

这里我给大家分别举一个例子,大家理解一下:


普通二叉树:


完全二叉树:


这里我们发现我们遇到NULL后出队列的所有元素都是空节点,因此这时完全二叉树。


3. 功能测试

int main()
{
  BTNode* root = CreatTree();
  preorder(root);
  printf("\n");
  Inorder(root);
  printf("\n");
  PostOrder(root);
  printf("\n");
  printf("%d", TreeSize(root));
  printf("\n");
  printf("%d", TreeHeight(root));
  printf("\n");
  printf("%d", TreeLevel(root, 4));
  printf("%p\n", BinaryTreeFind(root, 5));
  printf("%p\n", BinaryTreeFind(root, 50));
  printf("叶子节点个数是%d\n", BinaryTreeLeafSize(root));
  BinaryTreeLevelOrder(root);
  printf("\n");
  printf("%d", BinaryTreeComplete(root));
  return 0;
}

这里我们利用我们构建的二叉树将所有我们实现的功能进行测试,这里我们看结果。


相关文章
|
1月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
82 4
|
1月前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
132 8
|
2月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
32 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
2月前
|
存储 算法 搜索推荐
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
这篇文章主要介绍了顺序存储二叉树和线索化二叉树的概念、特点、实现方式以及应用场景。
37 0
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
|
2月前
|
Java
【用Java学习数据结构系列】震惊,二叉树原来是要这么学习的(二)
【用Java学习数据结构系列】震惊,二叉树原来是要这么学习的(二)
31 1
|
2月前
|
算法 Java C语言
【用Java学习数据结构系列】震惊,二叉树原来是要这么学习的(一)
【用Java学习数据结构系列】震惊,二叉树原来是要这么学习的(一)
29 1
|
2月前
|
存储
【数据结构】二叉树链式结构——感受递归的暴力美学
【数据结构】二叉树链式结构——感受递归的暴力美学
|
2月前
|
存储 编译器 C++
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
|
2月前
|
存储 算法 调度
数据结构--二叉树的顺序实现(堆实现)
数据结构--二叉树的顺序实现(堆实现)
|
2月前
【高阶数据结构】二叉树进阶探秘:AVL树的平衡机制与实现详解(三)
【高阶数据结构】二叉树进阶探秘:AVL树的平衡机制与实现详解