【初阶数据结构篇】实现链式结构二叉树(二叉链)上篇

简介: 先构建根结点,再对左右子树构建,每次需要时申请一个结点空间即可,否则返回空指针。

实现链式结构二叉树(二叉链)上篇

前言


二叉树基本概念


代码位置


[gitee](BinaryTree · petrichor/2024-summer-c-language - 码云 - 开源中国 (gitee.com))


二叉链的概念与结构

概念

⽤链表来表⽰⼀棵⼆叉树,即⽤链来指⽰元素的逻辑关系。通常的⽅法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别⽤来给出该结点左孩⼦和右孩⼦所在的链结点的存储地址。


结构

typedef int BTDataType;
typedef struct BinaryTreeNode
{
  BTDataType data;
  struct BinaryTreeNode* left;
  struct BinaryTreeNode* right;
}BTNode;


回顾二叉树


⼆叉树分为空树和⾮空⼆叉树,⾮空⼆叉树由根结点、根结点的左⼦树、根结点的右⼦树组成的


根结点的左⼦树和右⼦树分别⼜是由⼦树结点、⼦树结点的左⼦树、⼦树结点的右⼦树组成的,因此,⼆叉树定义是递归式的,后续链式⼆叉树的操作中基本都是按照该概念实现的。


二叉链的实现


BinaryTree.h(其中方法会一一讲到)


  • 定义二叉链结构
  • 将存储数据类型重命名(方便之后替换->例如我们要求二叉链内存储char类型数据,只用改一行代码即可)
  • 所写的函数的声明,声明的时候参数只需要类型就可以了,名字加不加都一样


#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义二叉树的链式结构
//二叉树结点的结构
typedef int BTDataType;
typedef struct BinaryTreeNode
{
  int data;
  struct BinaryTreeNode* left;
  struct BinaryTreeNode* right;
}BTNode;

//前序遍历
void PreOrder(BTNode* root);
//中序遍历
void InOrder(BTNode* root);
//后序遍历 
void PostOrder(BTNode* root);

// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root);

// ⼆叉树结点个数
//void BinaryTreeSize(BTNode* root, int* psize);

// ⼆叉树叶子结点个数
int BinaryTreeLeafSize(BTNode* root);

// ⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k);

//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root);

// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root);

//层序遍历
void LevelOrder(BTNode* root);

//判断二叉树是否为完全二叉树
bool BinaryTreeComplete(BTNode* root);


test.c

  • 用来测试我们写的函数(函数的调用)
  • 这一部分就是自己写的时候用的测试用例,随便什么都行


最好是写一个方法测试一次,不然找错误的时候会很痛苦😜


#define _CRT_SECURE_NO_WARNINGS 1
#include"Binary.h"

BTNode * buyNode(BTDataType x)
{
  BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
  if (newnode == NULL)
  {
    perror("malloc fail!");
    exit(1);
  }
  newnode->data = x;
  newnode->left = newnode->right = NULL;

  return newnode;
}

void test01()
{
  BTNode* node1 = buyNode(1);
  BTNode* node2 = buyNode(2);
  BTNode* node3 = buyNode(3);
  BTNode* node4 = buyNode(4);
  //BTNode* node5 = buyNode(5);
  //BTNode* node6 = buyNode(6);

  node1->left = node2;
  node1->right = node3;
  node2->right = node4;
  //node2->right = node5;
  //node3->left = node6;


  //PreOrder(node1);
  //printf("\n");
  //InOrder(node1);
  //printf("\n");
  //PostOrder(node1);

  //int size = 0;
  //BinaryTreeSize(node1, &size);
  //printf("size : %d\n", size);
  size = 0;
  //BinaryTreeSize(node1, &size);
  //printf("size : %d\n", size);

  //printf("size:%d\n", BinaryTreeSize(node1));
  //printf("size:%d\n", BinaryTreeSize(node1));

  //printf("size:%d\n", BinaryTreeSize(node1));
  //printf("leaf size: %d\n", BinaryTreeLeafSize(node1));
  //printf("第K层size : %d\n", BinaryTreeLevelKSize(node1, 2));
  //printf("depth/height:%d\n", BinaryTreeDepth(node1));

  //BTNode* find =BinaryTreeFind(node1, 33);
  //printf("%s\n", find == NULL ? "未找到!" : "找到了!");

  //LevelOrder(node1);

  bool ret = BinaryTreeComplete(node1);
  printf("%s\n", ret == false ? "不是完全二叉树" : "是完全二叉树");

  BinaryTreeDestory(&node1);
}

int main()
{
  test01();
  return 0;
}

BinaryTree.c


函数方法的实现,重点重点!!!


二叉树的遍历


⼆叉树的操作离不开树的遍历,我们先来看看⼆叉树的遍历有哪些⽅式


按照规则,⼆叉树的遍历有:前序/中序/后序的递归结构遍历:


  1. 前序遍历(PreorderTraversal亦称先序遍历):访问根结点的操作发⽣在遍历其左右⼦树之前

访问顺序为:根结点、左⼦树、右⼦树

 2.中序遍历(InorderTraversal):访问根结点的操作发⽣在遍历其左右⼦树之中(间)

访问顺序为:左⼦树、根结点、右⼦树

 3.后序遍历(PostorderTraversal):访问根结点的操作发⽣在遍历其左右⼦树之后

访问顺序为:左⼦树、右⼦树、根结点


前序遍历


  • 核心思想就是递归:先递推,再回归


以前序遍历为例,传入根节点,依据前序遍历根左右的规则,我们只需先打印根节点,然后再将左结点当做一课新树的根节点进行相同操作,右子树同理


递归另一要素即终止条件,我们什么时候开始回归?


很容易想到,当我们在某一叶子结点的函数栈帧时,其左右结点都为空,在以此叶子结点为根节点开始前序遍历时,它的下一次递推创建的函数栈帧传过去的根节点就是空指针,不需要继续遍历,即当root==NULL时,直接返回即可。


void PreOrder(BTNode* root)
{
  if (root == NULL)
  {
    return;
  }
  printf("%d ", root->data);
  PreOrder(root->left);
  PreOrder(root->right);
}

图解




中序遍历
//中序遍历--左根右
void InOrder(BTNode* root)
{
  if (root == NULL)
  {
    return;
  }
  InOrder(root->left);
  printf("%d ", root->data);
  InOrder(root->right);
}
后序遍历
void PostOrder(BTNode* root)
{
  if (root == NULL)
  {
    //printf("NULL ");
    return;
  }
  PostOrder(root->left);
  PostOrder(root->right);
  printf("%d ", root->data);
}

二叉树的构建


  • 二叉树的构建也是递归实现的
  • 以一道题目为例


描述


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


输入描述:


输入包括1行字符串,长度不超过100。


输出描述:


可能有多组测试数据,对于每组数据, 输出将输入字符串建立二叉树后中序遍历的序列,每个字符后面都有一个空格。 每个输出结果占一行。


  • 思路:分治思想,递归构建二叉树,构建二叉树先malloc出根节点然后再构建左子树,右子树即可。因为字符串需要构建之后指针向后移动找到下一个字符,所以需要一个count指针标记字符位置,构建之后指针向后移动,指针指向’#‘或者’\0’代表到了空节点了,完成返回NULL即可


                       

#include<stdio.h>
typedef struct TreeNode
{
    char val;
    struct TreeNode* left;
    struct TreeNode* right;
}TreeNode;

TreeNode* maketree(char*arr,int*count)
{
    if(arr[*count]=='#'||arr[*count]=='\0')
    {
      (*count)++;
        return NULL;
    }
    TreeNode* newnode = (TreeNode*)malloc(sizeof(TreeNode));
    newnode->val = arr[(*count)++];
    newnode->left = maketree(arr,count);
    newnode->right = maketree(arr,count);
    return newnode;
}

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

int main()
{
    char arr[101];
    scanf("%s",arr);
    int count = 0;
    TreeNode* tree = maketree(arr,&count);
    Inorder(tree);
    return 0;
}

  • 重点还是递归,先构建根结点,再对左右子树构建,每次需要时申请一个结点空间即可,否则返回空指针


二叉树结点个数


  • 要求二叉树结点个数,即当下结点+左子树+右子树
  • 当结点某一子树为空时,返回0即可,递推结束
int BinaryTreeSize(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

二叉树的叶子结点个数

既然是叶子结点,那么度为0

  • 要求叶子结点,就是求左右子树叶子结点之和,依次递推下去
  • 满足叶子结点条件或是某一子树为空结束递推
// ⼆叉树叶⼦结点个数
int BinaryTreeLeafSize(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  if (root->left == NULL && root->right == NULL)
  {
    return 1;
  }
  return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

二叉树第k层结点个数

  • 要求第k层节点个数,即左右子树作为单独的树,它们的k-1层结点之和


     而左子树也有自己的左右子树,右子树同理,即这四棵子树作为单独的树,它们的第k-2层结点之和


     以此类推可以写出递推方式


  • 结束条件


     当k==1时说明已经递推到第k层了

     以及在递推过程中遇到空节点,返回0即可


// ⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
  if (root == NULL)
  {
    return 0;
  }

  if (k == 1)
  {
    return 1;
  }
  return BinaryTreeLevelKSize(root->left, k - 1)
    + BinaryTreeLevelKSize(root->right, k - 1);
}

二叉树深度/高度

  • 要求高度,即子树高度的最大值+1,子树高度的最大值又是其子树高度最大值+1,以此类推
  • 当递推到空节点时结束——>返回0(空节点不算高度)
  • 回归时每次返回左右子树高度取最大值+1
//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  int leftDep = BinaryTreeDepth(root->left);
  int rightDep = BinaryTreeDepth(root->right);

  return leftDep > rightDep ? leftDep + 1 : rightDep + 1;
}

BinaryTree.c(完整版)

#define _CRT_SECURE_NO_WARNINGS 1
#include "Binary.h"
#include "Queue.h"
//前序遍历---根左右
void PreOrder(BTNode* root)
{
  if (root == NULL)
  {
    return;
  }
  printf("%d ", root->data);
  PreOrder(root->left);
  PreOrder(root->right);
}

//中序遍历--左根右
void InOrder(BTNode* root)
{
  if (root == NULL)
  {
    return;
  }
  InOrder(root->left);
  printf("%d ", root->data);
  InOrder(root->right);
}

//后序遍历 ---左右根
void PostOrder(BTNode* root)
{
  if (root == NULL)
  {
    //printf("NULL ");
    return;
  }
  PostOrder(root->left);
  PostOrder(root->right);
  printf("%d ", root->data);
}
// ⼆叉树结点个数
//int size = 0;
//int BinaryTreeSize(BTNode* root)
//{
//  if (root == NULL)
//  {
//    return 0;
//  }
//  ++size;
//  BinaryTreeSize(root->left);
//  BinaryTreeSize(root->right);
//  return size;
//}

//错误的写法
//void BinaryTreeSize(BTNode* root,int* psize)
//{
//  if (root == NULL)
//  {
//    return 0;
//  }
//  ++(*psize);
//  BinaryTreeSize(root->left,psize);
//  BinaryTreeSize(root->right,psize);
//}

// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}
// ⼆叉树叶⼦结点个数
int BinaryTreeLeafSize(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  if (root->left == NULL && root->right == NULL)
  {
    return 1;
  }
  return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

// ⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
  if (root == NULL)
  {
    return 0;
  }

  if (k == 1)
  {
    return 1;
  }
  return BinaryTreeLevelKSize(root->left, k - 1)
    + BinaryTreeLevelKSize(root->right, k - 1);
}
//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  int leftDep = BinaryTreeDepth(root->left);
  int rightDep = BinaryTreeDepth(root->right);

  return leftDep > rightDep ? leftDep + 1 : rightDep + 1;
}
// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
  if (root == NULL)
  {
    return NULL;
  }
  if (root->data == x)
  {
    return root;
  }

  BTNode* leftFind = BinaryTreeFind(root->left, x);
  if (leftFind)
  {
    return leftFind;
  }
  BTNode* rightFind = BinaryTreeFind(root->right, x);
  if (rightFind)
  {
    return rightFind;
  }
  return NULL;
}

// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root)
{
  if (*root == NULL)
  {
    return;
  }
  BinaryTreeDestory(&((*root)->left));
  BinaryTreeDestory(&((*root)->right));

  free(*root);
  *root = NULL;
}


【初阶数据结构篇】实现链式结构二叉树(二叉链)下篇: https://developer.aliyun.com/article/1590149?spm=a2c6h.13148508.setting.15.605e4f0ewgW9sZ

目录
相关文章
|
6月前
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
187 10
 算法系列之数据结构-二叉树
|
8月前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
182 12
|
8月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
163 10
|
8月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
259 3
|
9月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
10月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
256 4
|
10月前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
683 8
|
11月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
131 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
11月前
|
存储 算法 搜索推荐
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
这篇文章主要介绍了顺序存储二叉树和线索化二叉树的概念、特点、实现方式以及应用场景。
204 0
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
|
11月前
|
Java
【用Java学习数据结构系列】震惊,二叉树原来是要这么学习的(二)
【用Java学习数据结构系列】震惊,二叉树原来是要这么学习的(二)
100 1