【数据结构和算法】--- 二叉树(4)--二叉树链式结构的实现(2)

简介: 【数据结构和算法】--- 二叉树(4)--二叉树链式结构的实现(2)

一、二叉树剩余函数

1.1二叉树的层序遍历

层序遍历: 除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。 可以参考下图:

由上图不难看出,我们要控制函数一层一层的遍历二叉树。且普通递归都是会先遍历整个左子树,然后才会进入右子树。这里可以引用队列(先进先出)的概念,思路如下:

先将根节点地址入队列,然后利用循环,每当有节点出队列时就将此节点的左孩子(root -> left)的地址和右孩子(root -> right)的地址入队列(当然入队列的节点不为空,需要判断一下)。当队列为空时(while(!QueueEmpty(&q))),就结束循环并销毁队列。主要还是利用队列的思想,只要想到就不难,代码如下:

//二叉树层序遍历
void BinaryTreeLevelOrder(TreeNode* root)
{
  Queue q;
  QueueInit(&q);
  //根节点入队列
  if (root)
    QueuePush(&q, root);
  while (!QueueEmpty(&q))
  {
      //出队列
    TreeNode* front = QueueFront(&q);
    QueuePop(&q);
    printf("%d ", front->val);
    //左右孩子判空,并入队列
    if (root->left)
      QueuePush(&q, root->left);
    if (root->right)
      QueuePush(&q, root->right);
  }
  putchar('\n');
  //销毁
    QueueDestroy(&q);
}

代码图解:

1.2判断二叉树是否为完全二叉树

这里先要介绍一下两种特殊的二叉树:

  1. 满二叉树: 一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2^k-1 则它就是满二叉树。
  2. 完全二叉树: 完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。(即如果一棵树只有右节点没有左节点,那不能成为完全二叉树)。


根据上面的介绍,不难发现如果想要判断二叉树是否为完全二叉树,还是要一层一层的遍历二叉树。 既然如此,那么此函数还是可以使用队列来实现。代码设计思路:

第一步: 同样可以先创建队列并初始化,将第一个根节点的地址先入队列。同样执行循环,将一个节点出队列时,并将此节点的左孩子地址(root->left)和右孩子地址(root->right)都入队列。不同的是如果遇到空节点(无左孩子或右孩子便是NULL)同样要进入队列,并以队列为空(while (!QueueEmpty(&q)))作为循环结束条件(事实上此循环无法通过此条件结束)。在循环内部,如果接收到的出队列的节点为空,同样结束循环(break)。

至于遇到空节点,为什么要结束循环?因为我们判断的是完全二叉树,在进行层序遍历时,不会出现两个有效节点间还有一个空节点的情况(可以参考完全二叉树结构来思考!)。

第二步: 进行队列后续节点判断,同样可以依靠循环来不断出队列节点,当队列所出的节点不是空时(front != NULL)就直接销毁队列,并返回false;如果队列遍历到最后都没有发现空节点,那么便结束循环,销毁队列并返回true。

代码如下:

//判断树是否为完全二叉树
bool BinaryTreeComplete(TreeNode* root)
{
  Queue q;
  QueueInit(&q);
  //根节点入队列
  if (root)
    QueuePush(&q, root);
  while (!QueueEmpty(&q))
  {
      //队首出队列
    TreeNode* front = QueueFront(&q);
    QueuePop(&q);
    if (front == NULL)
      break;
    //左右孩子入队列
    QueuePush(&q, root->left);
    QueuePush(&q, root->right);

  }
  //队列遇空,判断后续节点
  while (!QueueEmpty(&q))
  {
    TreeNode* front = QueueFront(&q);
    QueuePop(&q);
    //当还有非空节点时说明二叉树为非完全二叉树
    //销毁并返回false
    if (front != NULL)
    {
      QueueDestroy(&q);
      return false;
    }
  }
  //直到队列为空,认为找到非空节点
  //销毁队列并返回true
  QueueDestroy(&q);
  return true;
}

代码图解:

有关层序遍历的例题:

  1. 某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列(A)

A FEDCBA

B CBAFED

C DEFCBA

D ABCDEF

解: 解此题首先要搞清,后序遍历和中序遍历的特性,(后序:左子树,右子树,根;中序:左子树,根,右子树),根据题目所描述的后序和中序遍历相同,那么这棵二叉树必定没有右子树。

既然这样,那么可以依据后序遍历确定根节点为F,然后依次判断以后节点。

1.3二叉树销毁

关于二叉树的销毁,可以使用后续遍历的思想。 因为如果先释放上层节点,那么下层的节点将无法寻到。如:free(root);,那么root便会变为野指针,root->left来找寻左子树也是非法的。

//二叉树的销毁
void BinaryDestroy(TreeNode* root)
{
  if (!root)
    return;
  BinaryTreeDesTroy(root->left);
  BinaryTreeDesTroy(root->right);
  free(root);
}

二、二叉树的构建及遍历OJ题

二叉树的构建及遍历,牛客刷题:KY11 二叉树遍历

题目描述: 编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F###其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。


解此题首先要创建二叉树的节点(值val,指向左孩子指针left,指向右孩子指针right)。然后根据字符串arr中的数据来,连接各个节点,需要注意的是要使用前序遍历来连接。至于为什么要传pi的地址? 因为在递归过程中需要取到字符串arr不同下标位置的字符,传地址,在递归过程便可任意改变pi的值。代码如下:

#include <stdio.h>
#include<stdlib.h>

typedef char BTDataType;
//二叉树节点
typedef struct BinaryTree
{
    BTDataType val;
    struct BinaryTree* left;
    struct BinaryTree* right;
}BTNode;
//读取字符串,并连接二叉树各个节点
BTNode* BinaryTreeCreat(char* arr,int* pi)
{
    if(arr[*pi] == '#')
    {
        (*pi)++;
        return NULL;
    }
    BTNode* node = (BTNode*)malloc(sizeof(BTNode));
    if(node == NULL)
    {
        perror("malloc()::fail");
        exit(-1);
    }
    node->val = arr[*pi];
    (*pi)++;
    node->left = BinaryTreeCreat(arr, pi);
    node->right = BinaryTreeCreat(arr, pi);
    return node;
}
//中序遍历打印
void BinaryTreeInOrder(BTNode* root)
{
    if(root == NULL)
        return;
    BinaryTreeInOrder(root->left);
    printf("%c ",root->val);
    BinaryTreeInOrder(root->right);
}

int main() {
    char arr[101];
    scanf("%s",arr);
    int pi = 0;
    BTNode* tree = BinaryTreeCreat(arr,&pi);
    BinaryTreeInOrder(tree);
    return 0;
}


目录
相关文章
|
3月前
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
95 9
 算法系列之数据结构-二叉树
|
3月前
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
107 3
 算法系列之数据结构-Huffman树
|
3月前
|
算法 Java
算法系列之数据结构-二叉搜索树
二叉查找树(Binary Search Tree,简称BST)是一种常用的数据结构,它能够高效地进行查找、插入和删除操作。二叉查找树的特点是,对于树中的每个节点,其左子树中的所有节点都小于该节点,而右子树中的所有节点都大于该节点。
102 22
|
4月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
136 29
|
4月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
184 25
|
4月前
|
存储 人工智能 算法
C 408—《数据结构》算法题基础篇—数组(通俗易懂)
408考研——《数据结构》算法题基础篇之数组。(408算法题的入门)
171 23
|
20天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化TCN-LSTM时间卷积神经网络时间序列预测算法matlab仿真
本内容展示了一种基于粒子群优化(PSO)与时间卷积神经网络(TCN)的时间序列预测方法。通过 MATLAB2022a 实现,完整程序运行无水印,核心代码附详细中文注释及操作视频。算法利用 PSO 优化 TCN 的超参数(如卷积核大小、层数等),提升非线性时间序列预测性能。TCN 结构包含因果卷积层与残差连接,结合 LSTM 构建混合模型,经多次迭代选择最优超参数,最终实现更准确可靠的预测效果,适用于金融、气象等领域。
|
16天前
|
算法 数据安全/隐私保护
基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,支持对文字,灰度图,彩色图,语音进行加解密
本项目实现了一种基于Logistic Map混沌序列的数字信息加解密算法,使用MATLAB2022A开发并包含GUI操作界面。支持对文字、灰度图像、彩色图像和语音信号进行加密与解密处理。核心程序通过调整Logistic Map的参数生成伪随机密钥序列,确保加密的安全性。混沌系统的不可预测性和对初值的敏感依赖性是该算法的核心优势。示例展示了彩色图像、灰度图像、语音信号及文字信息的加解密效果,运行结果清晰准确,且完整程序输出无水印。
基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,支持对文字,灰度图,彩色图,语音进行加解密
|
16天前
|
算法
基于PSO粒子群优化的多无人机路径规划matlab仿真,对比WOA优化算法
本程序基于粒子群优化(PSO)算法实现多无人机路径规划,并与鲸鱼优化算法(WOA)进行对比。使用MATLAB2022A运行,通过四个无人机的仿真,评估两种算法在能耗、复杂度、路径规划效果及收敛曲线等指标上的表现。算法原理源于1995年提出的群体智能优化,模拟鸟群觅食行为,在搜索空间中寻找最优解。环境建模采用栅格或几何法,考虑避障、速度限制等因素,将约束条件融入适应度函数。程序包含初始化粒子群、更新速度与位置、计算适应度值、迭代优化等步骤,最终输出最优路径。
|
26天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化TCN时间卷积神经网络时间序列预测算法matlab仿真
本内容介绍了一种基于PSO(粒子群优化)改进TCN(时间卷积神经网络)的时间序列预测方法。使用Matlab2022a运行,完整程序无水印,附带核心代码中文注释及操作视频。TCN通过因果卷积层与残差连接处理序列数据,PSO优化其卷积核权重等参数以降低预测误差。算法中,粒子根据个体与全局最优位置更新速度和位置,逐步逼近最佳参数组合,提升预测性能。