数据结构 —— 二叉树的创建、遍历、插入排序以及HuffMan压缩
1. 二叉树定义
2. 二叉树遍历
3. 二叉树插入排序
4. HuffMan压缩
-
二叉树的定义
树中的任意节点的度不大于2的有序树,它是一种最简单且最重要的树。
图 1 一棵简单二叉树(以下二叉树的遍历以图1为例进行) -
二叉树的遍历
二叉树遍历指通过一定的方式来遍历访问二叉树中的元素。常用的二叉树遍历分为先序遍历、中序遍历以及后序遍历以及层次遍历等。所谓先序、中序以及后序都是根据根节点的访问先后顺序来命名的。2.1先序遍历 —— 先根节点,再左子树,最后右子树
图2表示图一的先序遍历示意图。其中蓝线表示遍历路径,红色数字表示访问顺序。且先序遍历的结果为:1 2 4 8 5 3 6 9 10 7
public void printTree(Node root) {
if(root == null) {
return;
}
//先序遍历
String val = root.value;
printTree(root.left);
printTree(root.right);
System.out.println(val);
}
2.2 中序遍历 —— 先左子树,再根节点,最后右子树; 或先右子树,再根节点,最后左子树
public void printTree(Node root) {
if(root == null) {
return;
}
//中序遍历
String val = root.value;
System.out.println(val);
printTree(root.left);
printTree(root.right);
}
2.3遍历 —— 先左子树,再右子树,最后根节点; 或先右子树,再左子树,最后根节点
public void printTree(Node root) {
if(root == null) {
return;
}
//后序遍历
String val = root.value;
printTree(root.right);
printTree(root.left);
System.out.println(val);
}
个人感悟: 三种遍历方式(先序遍历、中序遍历、后序遍历)都是依据根节点的访问先后顺序来进行的。如先序表示先访问根节点,中序表示先访问一个子树,再访问根节点,最后访问另一个子树。诸如等等。另外,除了这三种方式以外,还有如层次遍历等其他遍历方式。
- 二叉树的插入排序
所谓的二叉树插入排序即为:在将数据存储入二叉树结构的过程中按照一种特定的规则(如左小右大)填充二叉树,最后通过一种遍历方式即可获得排序(如升序或降序)后的数据列表。
3.1 按左小右大的原则生成左右线性树
图5为由{4 3 5 2 1 6 7 }生成的左右线性二叉树。若我们要得到该序列的升序或降序排列结果,我们只需对该二叉树进行中序遍历即可。 若采用先左子树、再根节点,后右子树的中序遍历方式,将得到{1 2 3 4 5 6 7}的升序排序结果。 若采用先右子树,再根节点,后左子树的中序遍历方式,将得到{7 6 5 4 3 2 1}的降序遍历结果。
//向二叉树中插入元素(左右线性树)
public void addTree(int value) {
Node CurrentNode = root;
if(CurrentNode == null) {
root = new Node(value);
return;
}
while(true) {
if(value < CurrentNode.value) {
if(CurrentNode.left == null) {
CurrentNode.left = new Node(value);
return;
}else {
CurrentNode = CurrentNode.left;
}
}else {
if(CurrentNode.right == null) {
CurrentNode.right = new Node(value);
return;
}else {
CurrentNode = CurrentNode.right;
}
}
}
}
值得注意的是:这种通过生成左右线性树的方式进行数据排序非常消耗内存空间。这种方式不值得推荐。
3.1按左小右大的原则尽量填充整个二叉树
/向二叉树中插入元素(填满整个二叉树)
public void addTree( Node rootTmp, int value) {
if(rootTmp == null) {
rootTmp = new Node(value);
if(root == null) {
root = rootTmp;
}
}else {
//遵循左小右大的填充准则
if(value < rootTmp.value) {
if(rootTmp.left == null) {
rootTmp.left = new Node(value);
return;
}else {
addTree(rootTmp.left, value);
}
}else {
if(rootTmp.right == null) {
rootTmp.right = new Node(value);
return;
}else {
addTree(rootTmp.right, value);
}
}
}
}
在数据分布合理的情况下,这种方式还可以生成满二叉树,极大节省了内存空间。
- HuffMan压缩
4.1 HuffMan压缩定义
HuffMan压缩是一种依据HuffMan编码原则的无损压缩方式,其基本思想是用较少的bit来表示出现频率高的字节,而用较多的bit表示出现频率低的字节。
4.1 HuffMan压缩的原理。
统计文件中的每一种字节出现的频数,并生成对应的HuffMan树‘; 根据生成的HuffMan树和“左0右1”准则对字节进行HuffMan编码;根据HuffMan编码对文件进行编码,从而实现HuffMan压缩。下面详解HuffMan压缩算法流程:
现有一文件的内容为:“aaaabbbbcccccccffffff”。
第一步:统计文件中各字节的频数:
a : 4
b : 4
c : 7
f : 6
第二步:根据频数生成HuffMan树
//HuffMan树生成过程
public void HuffManTree(ArrayList<Node> data){
//将节点数组存入HuffMan树中
ArrayList<Node> dataTmp = sortAscend(data);
Node min1Node = dataTmp.get(0); //freq频率最小的节点
Node min2Node = dataTmp.get(1); //freq频率第二小的节点
root = new Node();
root.left = min1Node;
root.right = min2Node;
root.freq = min1Node.freq + min2Node.freq;
min1Node.parent = root;
min2Node.parent = root;
//删除最小两个元素
dataTmp.remove(0);
dataTmp.remove(0);
if(dataTmp.size() == 0) {
return;
}else {
dataTmp.add(root);
HuffManTree(dataTmp);
}
}
//插入排序
public ArrayList<Node> sortAscend(ArrayList<Node> data){
//插入排序
ArrayList<Node> nodeAscend = new ArrayList();
for(int i = 0; i < data.size(); i++) {
if(nodeAscend.size() == 0) { //列表中无元素
nodeAscend.add(data.get(i));
}else {
int flag = nodeAscend.size(); //判断当前元素为最小值标志
for(int j = nodeAscend.size() - 1; j >= 0; j--) {
if(data.get(i).freq > nodeAscend.get(j).freq) {
nodeAscend.add(j + 1, data.get(i));
break;
}
}
//如果当前元素为最小值则将其添加到首位
if(nodeAscend.size() == flag) {
nodeAscend.add(0, data.get(i));
}
}
}
return nodeAscend;
}
第三步:根据HuffMan树进行HuffMan编码。按照左0右1
//HuffMan编码
public void HuffManTreeCode() {
leafNode = new ArrayList();
getLeafNode(root);
//遍历列表获取叶节点所对应的编码
for(int i = 0; i < leafNode.size(); i++) {
code = "";
getCode(leafNode.get(i));
System.out.println(code + " " + leafNode.get(i).freq +" "+ leafNode.get(i).value);
}
}
public void getCode(Node node) {
if(node != root && node != null) { //如果node不是根节点
Node parentNode = node.parent; //获取节点的父节点
//根据左0右1进行编码
if(parentNode.left == node) {
code = "0" + code;
}else if(parentNode.right == node) {
code = "1" + code;
}
getCode(parentNode);
}else {
return;
}
}
第四步:根据HuffMan码表对文件进行编码,从而得到HuffMan压缩结果。
第五步:HuffMan解压即为:按照编码文件和码表对文件进行读取。
以上为个人对数据结构以及HuffMan压缩的部分认识,欢迎大家一起讨论。