数据结构 —— 二叉树的创建、遍历、插入排序以及HuffMan压缩

数据结构 —— 二叉树的创建、遍历、插入排序以及HuffMan压缩

 1. 二叉树定义
   
 2. 二叉树遍历
   
 3. 二叉树插入排序
 
 4. HuffMan压缩
  1. 二叉树的定义
    树中的任意节点的度不大于2的有序树,它是一种最简单且最重要的树。
    在这里插入图片描述

    图 1 一棵简单二叉树(以下二叉树的遍历以图1为例进行)

  2. 二叉树的遍历
           二叉树遍历指通过一定的方式来遍历访问二叉树中的元素。常用的二叉树遍历分为先序遍历、中序遍历以及后序遍历以及层次遍历等。所谓先序、中序以及后序都是根据根节点的访问先后顺序来命名的。

       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 中序遍历 —— 先左子树,再根节点,最后右子树; 或先右子树,再根节点,最后左子树
在这里插入图片描述

图3表示中序遍历示意图。其中蓝线表示遍历路径,红色数字表示访问顺序。且中序遍历的结果为:4 8 2 5 1 9 6 10 3 7(先左子树,再根节点,后右子树)
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遍历 —— 先左子树,再右子树,最后根节点; 或先右子树,再左子树,最后根节点
在这里插入图片描述

图4表示后序遍历示意图。其中蓝线表示遍历路径,红色数字表示访问顺序。且中序遍历的结果为:8 4 5 2 9 10 6 7 3 1(先左子树,再右子树,后根节点)
public void printTree(Node root) {
	if(root == null) {
		return;
	}	
	//后序遍历
	String val = root.value;
	printTree(root.right);
	printTree(root.left);
	System.out.println(val);
}

个人感悟: 三种遍历方式(先序遍历、中序遍历、后序遍历)都是依据根节点的访问先后顺序来进行的。如先序表示先访问根节点,中序表示先访问一个子树,再访问根节点,最后访问另一个子树。诸如等等。另外,除了这三种方式以外,还有如层次遍历等其他遍历方式。

  1. 二叉树的插入排序
           所谓的二叉树插入排序即为:在将数据存储入二叉树结构的过程中按照一种特定的规则(如左小右大)填充二叉树,最后通过一种遍历方式即可获得排序(如升序或降序)后的数据列表。
       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按左小右大的原则尽量填充整个二叉树
在这里插入图片描述

图6为由{6 4 5 1 2 8 9 }生成的二叉树。若我们要得到该序列的升序或降序排列结果,我们只需对该二叉树进行中序遍历即可。 若采用先左子树、再根节点,后右子树的中序遍历方式,将得到{1 2 4 5 6 8 9}的升序排序结果。 若采用先右子树,再根节点,后左子树的中序遍历方式,将得到{9 8 6 5 4 2 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);
			}
		}
	}
}

在数据分布合理的情况下,这种方式还可以生成满二叉树,极大节省了内存空间。

  1. 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树
在这里插入图片描述

图7表示生成HuffMan树过程。 ①:获取频数序列中两个最小值{4,4},生成第一步中的二叉树。 ②:将第一步中生成的根节点的值和剩下的频数序列组合,生成新的频数序列,并重复第一步;③:重复 ②,直到只剩下最后一个节点。
//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
在这里插入图片描述

图8表示HuffMan编码过程。 生成的HuffMan编码为:{ a:00 ,b:01, f:10,c:11 }
//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压缩的部分认识,欢迎大家一起讨论。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值