Re(从零开始的DS生活 轻松从0基础写出Huffman树与红黑树)

枕上诗书闲处好,门前风景雨来佳。这篇文章主要讲述Re:从零开始的DS生活 轻松从0基础写出Huffman树与红黑树相关的知识,希望能为你提供帮助。


引言:从零开始的DS生活 从0写出Huffman树与红黑树,作为Re:0专题的第二篇,本文详细介绍了树的概念和术语,并配合两种树的遍历算法来进行理解。文内通过Huffman树和红黑树巩固对树形结构的理解,并附有800行的详细代码供读者理解与学习,适合点赞+收藏。有什么错误希望大家直接指出~


导读(有基础的同学可以通过以下直接跳转到感兴趣的地方)

树的基本概念和术语学习一个数据结构离不开他的基本定义,那什么是树的定义呢?
树的基本概念
树是n(n≥0)个节点的有限集合T,他或者为空(n=0),或者满足①有且仅有一个特定的称为根的结点,②其余节点分为m(m> 0)个互不相交的自己T1,T2,...Tm,其中每个子集又是一棵树,称其为根的子树。
这个定义就是说:树有一个称为”根(Root)”的特殊结点  ,其余结点分为若干个互不相交的树,称为原来结点的子树。

树的基本术语:
1.子孙结点和祖先结点:根的子树中任意节点称为该结点的子孙,结点的祖先是从根到该结点所经分支上的所有节点。
2.父结点,子结点,兄弟节点,堂兄弟节点:有子树的结点是其子树的根结点的父结点(双亲);若A是B的父结点,B就是A的子结点;3.同双亲的孩子互称为兄弟;双亲是兄弟关系的节点称为堂兄弟(双亲在同层)。
4.结点的度:结点的子树个数  。
5.树的度:树中所有结点中最大的度  (树的叉)。
6.叶结点度大于0的结点称为分支节点,度为0称为叶子节点又称终端节点。
7.路径和路径长度:树中两个结点之间的路径是由这两个结点之间所经过的结点序列构成的,而路径长度是路径上所经过的边的个数。
8.结点的层次,结点的深度,结点的高度,树的高度:结点的层次从树根开始定义,根结点为第1层,他的子结点尾第2层,以此类推。结点的深度是从根节点开始自顶向下逐层累加的。结点的高度是从叶结点开始自底向上逐层累加的。树的高度(深度)是树中结点的最大层数。
9.有序树和无序树:树中结点的各子树从左到右是有次序的,不能互换,称该树为有序树,否则称为无序树。
树的性质:
1.树中的结点数等于所有结点的度数加1
2.度为m的树中第i层至多有m^i-1个节点(i≥1)
3.高度为h的m叉树至多有(m^h - 1)/(m - 1)个结点
4.具有n各结点的m叉树的最小高度为 [logm(n(m - 1) + 1)]
5.一个N个结点的树只有N-1条边
6.除了根结点之外,每个结点之有且只有一个父结点
二叉树及其性质;普通树与二叉树的转换;
二叉树的定义:①度为2的树(树中所有结点中最大的度),②子树有左右顺序之分       
// Definition for a binary tree node.
public class TreeNode
int val;
TreeNode left;
TreeNode right;
TreeNode(int x)val = x;

二叉树的性质:
性质1:二叉树第i层上的结点数目最多为2^(i - 1)  (i≥1)。
性质2:深度为k的二叉树至多有2^(k) - 1个结点(k≥1)。
性质3:包含n个结点的二叉树的高度至少为log2  (n+1)。
性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0 = n2 + 1。
满二叉树:
1.一棵深度为k且有2^k - 1个结点的二叉树
2.可以对满二叉树的结点进行连续编号,约定编号从根开始,自上而下,自左而右
完全二叉树:
深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1到n的结点一一对应时,称为完全二叉树
完全二叉树特点:
1.叶子结点只可能出现在层次最大的两层上
2.对任一结点,若其右分支下子孙的最大层次为1,其左下分支的子孙的最大层次必为|或者1 + 1.
3.深度为k的完全二叉树第k层最少1个结点,最多2k - 1 - 1个结点:整棵树最少2k - 1个结点,最多2k - 2个结点
4.具有n个结点的完全二叉树的深度为[log2n] + 1
二叉树的转换;
树转换为二叉树过程
1.树中所有相同双亲结点的兄弟结点之间加一条连线。
2.对树中不是双亲结点的第一个孩子的结点,只保留新添加的该结点与左兄弟结点之间的连线,删去该结点与双亲结点之间的连线。
3.整理所有保留的连线,根据连线摆放成二又树的结构,转换完成。

二叉树转换为树过程:
1.若某结点是其双亲结点的左孩子,则把该结点的右孩子,右孩子的右孩子都与该结点的双亲结点用线连起来:
2.删除原二叉树中所有双亲结点与右孩子结点的连线:
3.根据连线摆放成树的结构,转换完成。

树的前序,中序,后序,层次序遍历;
二叉树的遍历(traversing  binary  tree)按某神搜索路径寻访树中每个结点,使每个结点均被访向一次仅且一次。
1.先序遍历二叉树(根> 左> 右)
2.中序遍历二叉树(左> 根> 右)
3.后序遍历二叉树(左> 右> 根)
4.层序遍历二叉树
先序遍历,递归解法与非递归解法
/**
* 首先,定义树的存储结构 TreeNode,Definition for a binary tree node.
*/
public class TreeNode
int val;
TreeNode left;
TreeNode right;

TreeNode(int x)val = x;


/**
* 递归解法
* 思想: 先序遍历:根左右。先打印根节点,再递归左右子树,需要结合递归的函数调用栈理解
*/
public List< Integer> preorderTraversal(TreeNode root)
List< Integer> list = new ArrayList< > ();
// 输入根节点、list集合
helper(root, list);
return list;

// 利用 helper,加 list 条件
private void helper(TreeNode root, List< Integer> list)
if (root == null)
return;

list.add(root.val); // 保存根节点
if (root.left != null)
helper(root.left, list); // 遍历左子树

if (root.right != null)
helper(root.right, list); // 遍历右子树



/**
* 非递归解法-迭代法
* 思想:迭代法,需要用到一个辅助栈
*/
public List< Integer> preorderTraversalFunction(TreeNode root)
Deque< TreeNode> stack = new LinkedList< > (); // 存储节点
List< Integer> list = new ArrayList< > (); // 保存遍历节点的顺序
TreeNode curr = root; // 根节点
while (curr != null || stack.size() > 0)
while (curr != null)
stack.push(curr);
list.add(curr.val);
curr = curr.left;

curr = stack.pop().right; // 当没有右子树时,只删除最上面节点

return list;

迭代法思想图解:

树的存储结构,标准形式; 完全树(complete tree)的数组形式存储;1.双亲表示法:假设以一组连续空间存储数的结点,同时在每个结点中,附设一个指示器指示其双亲结点到链表中的位置。
data(数据域)

parent(指针域)

存储结点的数据信息存

储该结点的双亲所在数组中的下标

2.孩子表示法:把每个结点的孩子结点排列起来,以单链表作为存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空。然后n个头指针又组成-个线性表,采用顺序存储结构,存放进一个一维数组中。孩子链表的孩子结点如下表1,表头数据的表头结点如下表2.
child(数据域)

next(指针域)

存储某个结点在表头数组中的下标

存储指向某结点的下一 个孩子结点的指针

data(数据域)

【Re(从零开始的DS生活 轻松从0基础写出Huffman树与红黑树)】firstchild(指针域)

存储某个结点的数据信息存

储该结点的孩子链表的头指针

 
3.孩子兄弟表示法:任意一棵树, 它的结点的第一个孩子如果存在就是唯一的, 它的右兄弟存在也是唯一的。因此,设置两个指针, 分别指向该结点的第一个孩子和此结点的右兄弟。
data(数据域)

firstchild(指针域)

rightsib

存储结点的数据信息

存储该结点的第一个孩子的存储地址

存储该结点的右兄弟结点的存储地址

 
树的应用,Huffman树的定义与应用;Huffman树:构造一棵二叉树,该树的带权路径长度达到最小, 称为最优二叉树,也称为哈夫曼树(Huffman Tree)
基本概念:
1.树的路径长度:从根到每一结点的路径长度之和。
2.结点的带权路径长度:从该结点到树根之间的路径长度与结点上权的乘积。
3.树的带权路径长度:树中所有叶子结点的带权路径长度之和,通常记做WPL。
构造方式:
每次把权值最小的两棵二叉树合并;左节点权值比右节点小;
Huffman算法:
1.由给定的n个权值w0, w1, w2, .... wn-1,构造具有n棵二叉树的集合F= T0,T1, T2, ... Tn-1,
其中每一棵二叉树Ti只有一个带有权值wi的根结点,其左、右子树均为空。
2.在F中选取两棵根结点的权值最小的二叉树,做为左、右子树构造一棵新的二叉树。置新的二叉
树的根结点的权值为其左、右子树,上根结点的权值之和。
3.在F中删去这两棵二叉树,加入新得的树。
4.重复(2)(3), 直到F只含一棵树为止。这棵树就是赫夫曼树
import java.util.ArrayList;
import java.util.List;
/**
* @author 小明
* 哈夫曼树
*/

public class HuffmanTree
//节点
public static class Node< E>
E data; //数据
int weight; //权重
Node leftChild; //左子节点
Node rightChild; //右子节点

public Node(E data, int weight)
super();
this.data = https://www.songbingjia.com/android/data;
this.weight = weight;


public String toString()
return "Node[" + weight + ",data="https://www.songbingjia.com/android/+ data +"]";



public static void main(String[] args)
List< Node> nodes = new ArrayList< Node> ();

    推荐阅读