【数据结构】二叉树

简介: 【数据结构】二叉树

树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个有层次关系的集合

将其称之为数,由于其看似一颗倒挂树,根朝上,叶朝下。

  • 树的特点:

1.有个特殊的结点,称为根节点,根结点没有前驱结点

2.除根节点外,其余结点被分为M(M>0)个不相关的集合T1,T2,T3…Tm,其中每一个集合Ti(1<=i<=m)又是一个与树类似的子树,每棵子树的根结点有且只有一个前驱,可以有0个或多个后结点

3.树是递归定义的

任何一棵树都有俩部分组成:根与子树

树的相关概念

结点的度:一个结点含有的子树的个数称为该结点的度。

叶结点或终端结点:度为零的结点。

非终端结点或分支结点:度不为零的结点

双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点

孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点

兄弟结点:具有相同父结点的结点互称为兄弟结点(亲兄弟)

树的度:一个树中最大的结点的度称为树的度

结点的层次:从根开始定义起,根为第一层,根的子结点为第二层,以此类推

树的高度或深度:树中结点的最大层次

堂兄弟结点:双亲在同一层的结点互为堂兄弟

结点的祖先:从根到该结点所经分支上的所有结点

子孙:以某结点为根的子树中任一结点都称为该结点的子孙

森林:由m(m>0)棵互不相关的树的集合称为森林

树的注意事项

  • 子树是不相交的
  • 除了根结点外,每个结点有且仅有一个父节点
  • 一棵N个结点的树有N-1条边

左孩子右兄弟表示法

typedef int DataType;
struct TreeNode
{
  struct TreeNode* FirstChild;//第一个孩子结点
  struct TreeNode* NextBroter;//指向其下一个兄弟结点
  DataType data;//结点中的数据域
};

树实际中的应用

  • Linux树状目录结构

  • windows树状目录结构

二叉树

二叉树的概念

一棵二叉树是结点的一个有限集合,该集合:

  • 要么为空
  • 要么由一个根结点加上俩棵别称为左子树和右子树的二叉树组成

二叉树的注意事项

  • 二叉树不存在度大于2的结点
  • 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

特殊二叉树

满二叉树

一个二叉树,如果每一层的结点数都达到最大值,则这个二叉树就是满二叉树。即如果一个二叉树的层数为k,且结点总数是2k-1,则为满二叉树

完全二叉树

完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的,对于深度为k的,由n个结点的二叉树,当且仅当其每一个结点都与深度的k满二叉树中编号从1至n的结点——对应时称为完全二叉树

二叉树的性质

  • 对于任何一棵完全二叉树,如果度为0其叶结点个数为n0,度为2的分支结点个数为n2,则n0=n2+1。(度为0的永远比度为2的多一个)
  • 高度为h的完全二叉树,结点数量范围[2h-1,2h-1]
  • 结点为N的完全二叉树,结点数量的范围[log(N+1),log(N+1)]

二叉树的存储形式

顺序存储

顺序结构存储就是使用数组存储,一般数组只能用来表示完全二叉树(不是完全二叉树会浪费很多空间),二叉树顺序存储在物理上是一个数组,在逻辑上是一棵二叉树。

  • 表示二叉树的值在数组位置中的下标关系:
parent = (child - 1) / 2;
leftchild = parent * 2 + 1;
rightchild = parent * 2 + 2;

链式存储

二叉树的链式存储结构是指用链表表示一棵二叉树,使用链来表示元素之间的逻辑关系。

通常方法是链表中每个结点由三个域组成,数据域与左右指针域。

typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
   struct BinTreeNode* _pLeft; // 指向当前节点左孩子
   struct BinTreeNode* _pRight; // 指向当前节点右孩子
   BTDataType _data; // 当前节点值域
}

链式结构又分为二叉链与三叉链

typedef int BTDataType;
// 三叉链
struct BinaryTreeNode
{
 struct BinTreeNode* _pParent; // 指向当前节点的双亲
 struct BinTreeNode* _pLeft; // 指向当前节点左孩子
 struct BinTreeNode* _pRight; // 指向当前节点右孩子
 BTDataType _data; // 当前节点值域
};

二叉树链式结构

前面的知识已经对二叉树有了些许了解,总的来讲,普通二叉树没有意义,但是学习二叉树可以对后期了解更多与二叉树有关的知识进入交互学习。

比如搜索二叉树也是二叉树的一种:其左子二叉树小于右子二叉树,搜索二叉树也叫排序二叉树。

类似二分查找,但二分查找的劣势:

1.数组不便于增删查找

2.需要升序或者降序

实际上经常被用来搜索的数据结构有:平衡二叉搜索树、哈希表、B树

简单初始化

进入了解二叉树之前需要简单对二叉树进行初始化,以便对二叉树有清晰的认知。

#include<stdio.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
  struct BinaryTreeNode* left;
  struct BinaryTreeNode* right;
  BTDataType data;
}BTNode;
BTNode* BuyBTNode(BTDataType x)
{
  BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
  if (newnode == NULL)
  {
    perror("malloc fail");
    return;
  }
  newnode->data = x;
  newnode->left = NULL;
  newnode->right = NULL;
}
BTNode* CreatBTNode()
{
  BTNode* node1 = BuyBTNode(1);
  BTNode* node2 = BuyBTNode(2);
  BTNode* node3 = BuyBTNode(3);
  BTNode* node4 = BuyBTNode(4);
  BTNode* node5 = BuyBTNode(5);
  BTNode* node6 = BuyBTNode(6);
  node1->left = node2;
  node1->right = node4;
  node2->left = node3;
  node4->left = node5;
  node4->right = node6;
  return node1;
}

二叉树的遍历

所谓二叉树遍历是按照某种特定的规则,依次对二叉树中的结点进行相应的操作,并且每个结点只操作一次访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

二叉树的遍历有:前序遍历、中序遍历、后续遍历

由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。

前序遍历

前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。

  • 根-左子树-右子树
void PreOrder(BTNode* node)
{
  if (node == NULL)
  {
    printf("NULL ");
    return;
  }
  printf("%d ", node->data);
  PreOrder(node->left);
  PreOrder(node->right);
}

中序遍历

中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。

  • 左子树-根-右子树
void InOrder(BTNode* node)
{
  if (node == NULL)
  {
    printf("NULL ");
    return;
  }
  InOrder(node->left);
  printf("%d ", node->data);
  InOrder(node->right);
}

后续遍历

后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

  • 左子树-右子树-根
void PostOrder(BTNode* node)
{
  if (node == NULL)
  {
    printf("NULL ");
    return;
  }
  PostOrder(node->left);
  PostOrder(node->right);
  printf("%d ", node->data);
}

三种遍历代码实现

#include<stdio.h>
#include<stdlib.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
  struct BinaryTreeNode* left;
  struct BinaryTreeNode* right;
  BTDataType data;
}BTNode;
BTNode* BuyBTNode(BTDataType x)
{
  BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
  if (newnode == NULL)
  {
    perror("malloc fail");
    return NULL;
  }
  newnode->data = x;
  newnode->left = NULL;
  newnode->right = NULL;
  return newnode;
}
BTNode* CreatBTNode()
{
  BTNode* node1 = BuyBTNode(1);
  BTNode* node2 = BuyBTNode(2);
  BTNode* node3 = BuyBTNode(3);
  BTNode* node4 = BuyBTNode(4);
  BTNode* node5 = BuyBTNode(5);
  BTNode* node6 = BuyBTNode(6);
  node1->left = node2;
  node1->right = node4;
  node2->left = node3;
  node4->left = node5;
  node4->right = node6;
  return node1;
}
void PreOrder(BTNode* node)
{
  if (node == NULL)
  {
    printf("NULL ");
    return;
  }
  printf("%d ", node->data);
  PreOrder(node->left);
  PreOrder(node->right);
}
void InOrder(BTNode* node)
{
  if (node == NULL)
  {
    printf("NULL ");
    return;
  }
  InOrder(node->left);
  printf("%d ", node->data);
  InOrder(node->right);
}
void PostOrder(BTNode* node)
{
  if (node == NULL)
  {
    printf("NULL ");
    return;
  }
  PostOrder(node->left);
  PostOrder(node->right);
  printf("%d ", node->data);
}
int main(void)
{
  BTNode* node = CreatBTNode();
  PreOrder(node);
  printf("\n");
  InOrder(node);
  printf("\n");
  PostOrder(node);
  printf("\n");
  return 0;
}

层序遍历

二叉树除了可以前序遍历、中序遍历、后序遍历,还可以层序遍历,层序遍历就是从二叉树的根结点出发,首先访问根结点,然后第二层从左至右开始访问,以此类推…自上而下,从左往右。

  • 原则:出上一层,入后一层
void levelOrder(BTNode* node)
{
  Queue q;
  QueueInit(&q);
  if (node)
  {
    QueuePush(&q, node);
  }
  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);
    }
  }
  Queuedestory(&q);
}

二叉树相关代码实现

二叉树的结点个数

//二叉树结点个数
int BinaryTreeSize(BTNode* node)
{
  if (node == NULL)
    return 0;
  return BinaryTreeSize(node->left) + BinaryTreeSize(node->right) + 1;
}
//二叉树结点个数
int BinaryTreeSize(BTNode* node)
{
  return (node == NULL) ? 0
    : BinaryTreeSize(node->left)
    + BinaryTreeSize(node->right)
    + 1;
}

二叉树的高度

//二叉树的高度或者深度
int BinaryTreeHeight(BTNode* node)
{
  if (node == NULL)
    return 0;
  int leftHeight = BinaryTreeHeight(node->left) + 1;
  int rightHeight = BinaryTreeHeight(node->right) + 1;
  
  return (leftHeight > rightHeight) 
    ? leftHeight 
    : rightHeight;
}

二叉树的叶子结点个数

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

第k层结点个数

寻找第k层结点结合第一层与第k层距离为k,第二层与第k层距离为k-1…

//二叉树第k层结点个数
int BinaryTreelevel(BTNode* node, int k)
{
  if (node == NULL)
  {
    return 0;
  }
  if (k == 1)
  {
    return 1;
  }
  return BinaryTreelevel(node->left, k - 1) 
    + BinaryTreelevel(node->right, k - 1);
}

二叉树查找值为x的结点

//二叉树查找值为x的结点
BTNode* BinaryTreeFine(BTNode* node, BTDataType x)
{
  if (node == NULL)
  {
    return NULL;
  }
  if (node->data == x)
  {
    return node;
  }
  BTNode* left = BinaryTreeFine(node->left,x);
  BTNode* right = BinaryTreeFine(node->right, x);
  if (left != NULL)
  {
    return left;
  }
  if (right != NULL)
  {
    return right;
  }
  return NULL;
}

二叉树的销毁

void BinaryTreeDestory(BTNode* node)
{
  if(node==NULL)
  {
    return;
  }
  BInaryTreeDestory(node->left);
  BInaryTreeDestory(node->right);
  free(node);
  node->NULL;
}

DFS与BFS

  • DFS:深度优先遍历(二叉树有序遍历)——一般使用递归
  • BFS:广度优先遍历(二叉树层序遍历)——一般使用队列
相关文章
|
29天前
|
存储 C++
【数据结构】搜索二叉树
【数据结构】搜索二叉树
|
5天前
|
存储 机器学习/深度学习
【数据结构】二叉树全攻略,从实现到应用详解
本文介绍了树形结构及其重要类型——二叉树。树由若干节点组成,具有层次关系。二叉树每个节点最多有两个子树,分为左子树和右子树。文中详细描述了二叉树的不同类型,如完全二叉树、满二叉树、平衡二叉树及搜索二叉树,并阐述了二叉树的基本性质与存储方式。此外,还介绍了二叉树的实现方法,包括节点定义、遍历方式(前序、中序、后序、层序遍历),并提供了多个示例代码,帮助理解二叉树的基本操作。
34 13
【数据结构】二叉树全攻略,从实现到应用详解
|
3月前
|
存储 算法
【数据结构和算法】--- 二叉树(4)--二叉树链式结构的实现(2)
【数据结构和算法】--- 二叉树(4)--二叉树链式结构的实现(2)
26 0
|
2天前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
2天前
|
存储 机器学习/深度学习 C语言
数据结构基础详解(C语言): 树与二叉树的基本类型与存储结构详解
本文介绍了树和二叉树的基本概念及性质。树是由节点组成的层次结构,其中节点的度为其分支数量,树的度为树中最大节点度数。二叉树是一种特殊的树,其节点最多有两个子节点,具有多种性质,如叶子节点数与度为2的节点数之间的关系。此外,还介绍了二叉树的不同形态,包括满二叉树、完全二叉树、二叉排序树和平衡二叉树,并探讨了二叉树的顺序存储和链式存储结构。
|
2天前
|
存储 C语言
数据结构基础详解(C语言): 树与二叉树的应用_哈夫曼树与哈夫曼曼编码_并查集_二叉排序树_平衡二叉树
本文详细介绍了树与二叉树的应用,涵盖哈夫曼树与哈夫曼编码、并查集以及二叉排序树等内容。首先讲解了哈夫曼树的构造方法及其在数据压缩中的应用;接着介绍了并查集的基本概念、存储结构及优化方法;随后探讨了二叉排序树的定义、查找、插入和删除操作;最后阐述了平衡二叉树的概念及其在保证树平衡状态下的插入和删除操作。通过本文,读者可以全面了解树与二叉树在实际问题中的应用技巧和优化策略。
|
23天前
|
存储
【初阶数据结构篇】二叉树基础概念
有⼀个特殊的结点,称为根结点,根结点没有前驱结点。
|
28天前
|
存储 算法 Linux
【数据结构】树、二叉树与堆(长期维护)(1)
【数据结构】树、二叉树与堆(长期维护)(1)
|
28天前
|
算法
【数据结构】树、二叉树与堆(长期维护)(2)
【数据结构】树、二叉树与堆(长期维护)(2)
【数据结构】树、二叉树与堆(长期维护)(2)
|
29天前
|
算法 Java
数据结构二叉树
这篇文章讨论了数据结构中的二叉树,并提供了一个二叉树中序遍历的算法示例,包括给定二叉树的根节点返回中序遍历结果的Java代码实现。
数据结构二叉树