数据结构与算法:树结构的基础部分

前言

本文主要讲解二叉树,顺序存储二叉树,线索化二叉树


数据结构与算法文章列表

数据结构与算法文章列表: 点击此处跳转查看


目录

在这里插入图片描述


(一)二叉树

(1)为什么使用树

1)分析数组优缺点
优点:按照索引查询元素速度快、能存储大量数据、按照索引遍历数组方便
缺点:根据内容查找元素速度慢、数组的大小一经确定不能改变、数组只能存储一种类型的数据、增加、删除元素效率慢、未封装任何方法,所有操作都需要用户自己定义。
数组示意图:
在这里插入图片描述

2) 分析链表的优缺点
优点:添加、删除比较方便
缺点:查找某个值时,需要从头节点开始遍历,效率比较低
操作示意图:
在这里插入图片描述

3) 树存储方式的分析
能提高数据存储,读取的效率。
案例: [7, 3, 10, 1, 5, 9, 12]
在这里插入图片描述


(2)树示意图

在这里插入图片描述
树的常用术语(结合示意图理解):

  1. 节点
  2. 根节点
  3. 父节点
  4. 子节点
  5. 叶子节点 (没有子节点的节点)
  6. 节点的权(节点值)
  7. 路径(从 root 节点找到该节点的路线)
  8. 子树
  9. 树的高度(最大层数)
  10. 森林 :多颗子树构成森林

(3)二叉树的概念

  1. 树中节点的度不大于2的有序树称为二叉树。
  2. 二叉树的子节点分为左节点和右节点
    示意图:
    在这里插入图片描述
  3. 如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则我们称为满二叉树。
    在这里插入图片描述
  4. 深度为k,有n个结点的二叉树当且仅当其每一个结点都与深度为k的满二叉树中编号从1到n的结点一一对应时,我们称为完全二叉树。
    在这里插入图片描述

(4)二叉树遍历的说明

  1. 前序遍历: 先输出父节点,再遍历左子树和右子树
  2. 中序遍历: 先遍历左子树,再输出父节点,再遍历右子树
  3. 后序遍历: 先遍历左子树,再遍历右子树,最后输出父节点
  4. 总结: 看输出父节点的顺序,就确定是前序,中序还是后序

(5)二叉树遍历应用实例(前序,中序,后序)

应用实例的说明和思路
在这里插入图片描述
二叉树遍历代码(点击下面链接查看源代码)
二叉树遍历应用实例(前序,中序,后序)


(6)二叉树-查找指定节点(前序,中序,后序)

要求:

  1. 请编写前序查找,中序查找和后序查找的方法。
  2. 并分别使用三种查找方式,查找 heroNO = 5 的节点
  3. 并分析各种查找方式,分别比较了多少次
  4. 思路分析图解
    在这里插入图片描述
    代码(点击下面链接查看源代码):
    二叉树-查找指定节点(前序,中序,后序)

(7)二叉树-删除节点

要求:

  1. 如果删除的节点是叶子节点,则删除该节点
  2. 如果删除的节点是非叶子节点,则删除该子树.
  3. 测试,删除掉 5 号叶子节点 和 3 号子树.
  4. 完成删除思路分析
    在这里插入图片描述
    代码(点击下面链接查看源代码):
    二叉树-删除节点

(二)顺序存储二叉树

(1)顺序存储二叉树的概念

基本说明:
从数据存储来看,数组存储方式和树的存储方式可以相互转换,即数组可以转换成树,树也可以转换成数组,看右面的示意图。
在这里插入图片描述
要求:

  1. 右图的二叉树的结点,要求以数组的方式来存放 arr : [1, 2, 3, 4, 5, 6, 6]
  2. 要求在遍历数组 arr 时,仍然可以以前序遍历,中序遍历和后序遍历的方式完成结点的遍历

顺序存储二叉树的特点:

  1. 顺序二叉树通常只考虑完全二叉树
  2. 第 n 个元素的左子节点为 2 * n + 1
  3. 第 n 个元素的右子节点为 2 * n + 2
  4. 第 n 个元素的父节点为 (n-1) / 2
  5. n : 表示二叉树中的第几个元素(按 0 开始编号如图所示)

(2)顺序存储二叉树遍历

需求: 给你一个数组 {1,2,3,4,5,6,7},要求以二叉树前序遍历的方式进行遍历。 前序遍历的结果应当为1,2,4,5,3,6,7

public class ArrBinaryTreeDemo {
    
    
     public static void main(String[] args) {
    
    
          int[] arr = {
    
     1, 2, 3, 4, 5, 6, 7 };
          // 创建一个 ArrBinaryTree
          ArrBinaryTree arrBinaryTree = new  ArrBinaryTree(arr);
          arrBinaryTree.preOrder(); // 1,2,4,5,3,6,7
     }
}

// 编写一个ArrayBinaryTree, 实现顺序存储二叉树遍历
class ArrBinaryTree {
    
    
     private int[] arr;// 存储数据结点的数组
     public ArrBinaryTree(int[] arr) {
    
    
          this.arr = arr;
     }

     // 重载preOrder
     public void preOrder() {
    
    
          this.preOrder(0);
     }

     // 编写一个方法,完成顺序存储二叉树的前序遍历
     /**
      *
      * @param index 数组的下标
      */
     public void preOrder(int index) {
    
    
          // 如果数组为空,或者 arr.length = 0
          if (arr == null || arr.length == 0) {
    
    
              System.out.println("数组为空,不能按照二叉树的前序遍历");
          }
          // 输出当前这个元素
          System.out.println(arr[index]);
          // 向左递归遍历
          if ((index * 2 + 1) < arr.length) {
    
    
              preOrder(2 * index + 1);
          }
          // 向右递归遍历
          if ((index * 2 + 2) < arr.length) {
    
    
              preOrder(2 * index + 2);
          }
     }
}

结果:

1
2
4
5
3
6
7

(3)顺序存储二叉树应用实例

八大排序算法中的堆排序,就会使用到顺序存储二叉树


(三)线索化二叉树

(1)问题

将数列 {1, 3, 6, 8, 10, 14 } 构建成一颗二叉树. n+1=7
在这里插入图片描述
问题分析:

  1. 当我们对上面的二叉树进行中序遍历时,数列为 {8, 3, 10, 1, 6, 14 }
  2. 但是 6, 8, 10, 14 这几个节点的左右指针,并没有完全的利用上.
  3. 如果我们希望充分的利用 各个节点的左右指针, 让各个节点可以指向自己的前后节点,怎么办?
  4. 解决方案-线索二叉树

(2)线索二叉树基本介绍

  1. n 个结点的二叉链表中含有 n+1 【公式 2n-(n-1)=n+1】 个空指针域。利用二叉链表中的空指针域,存放指向该结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索")
  2. 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种
  3. 一个结点的前一个结点,称为前驱结点
  4. 一个结点的后一个结点,称为后继结点

(3)线索二叉树应用案例

应用案例说明:将下面的二叉树,进行中序线索二叉树。中序遍历的数列为 {8, 3, 10, 1, 14, 6}
在这里插入图片描述
思路分析: 中序遍历的结果:{8, 3, 10, 1, 14, 6}
在这里插入图片描述
说明: 当线索化二叉树后,Node 节点的 属性 left 和 right ,有如下情况:

  1. left 指向的是左子树,也可能是指向的前驱节点. 比如 ① 节点 left 指向的左子树, 而 ⑩ 节点的 left 指向的就是前驱节点.
  2. right 指向的是右子树,也可能是指向后继节点,比如 ① 节点 right 指向的是右子树,而⑩ 节点的 right 指向的是后继节点

代码(点击下面链接查看源代码):
线索二叉树应用案例


(4)遍历线索化二叉树

  1. 说明:对前面的中序线索化的二叉树, 进行遍历
  2. 分析:因为线索化后,各个结点指向有变化,因此原来的遍历方式不能使用,这时需要使用新的方式遍历线索化二叉树,各个节点可以通过线型方式遍历,因此无需使用递归方式,这样也提高了遍历的效率。 遍历的次序应当和中序遍历保持一致。
  3. 代码(点击下面链接查看源代码):
    遍历线索化二叉树

猜你喜欢

转载自blog.csdn.net/a13027629517/article/details/115263388