数据结构和算法(堆排序和哈夫曼树、哈夫曼编码、解码)

堆排序:

在这里插入图片描述
一般使用大顶堆升序排列
使用小顶堆降序排列

在这里插入图片描述
下图为代码测试的树(数组格式)
在这里插入图片描述

堆降序代码实现:

import java.util.Arrays;

public class HeapSort {
	public static void main(String[] args) {
		int[] arr = { 4, 6, 8, 5, 9 };
		heapSort(arr);
		System.out.println(Arrays.toString(arr));
	}


	public static void heapSort(int[] arr) {
		int temp;
		// 调整树的结构,从arr.length/2-1一直到头节点
		for (int i = arr.length / 2 - 1; i >= 0; i--) {
			adjust(arr, arr.length, i);
		}

		// 开始交换
		// 交换总长度 - 1 次
		for (int j = arr.length - 1; j > 0; j--) {
			temp = arr[0];
			arr[0] = arr[j];
			arr[j] = temp;
			//由于顶部数据被交换,所以从顶部开始,整棵树都需要重新调整
			//由于有调整过一次的基础,树的结构没有太大改变,所以只要将顶部的数据下移,再调整一次就足够了
			adjust(arr, j, 0);
		}
	}

	/**
	 * 功能:完成将 i 对应的叶子结点的树调整成大顶堆 调整的时候都是从底层开始的
	 * 
	 * @param arr
	 * @param length
	 */
	public static void adjust(int[] arr, int length, int i) {
		// 线取出当前元素的值,保存再临时变量
		int temp = arr[i];
		// 开始调整
		// k的值为某个结点的右子节点
		for (int k = i * 2 + 1; k < length; k = 2 * k + 1) {
			if (k + 1 < length && arr[k] < arr[k + 1]) {
				k++;
			}

			if (arr[k] > temp) {// 如果子节点大于父节点
				arr[i] = arr[k];// 把较大的值赋给当前结点
				i = k;// !!! i指向 k,继续循环比较,
			} else {
				break;
			}
			// 当for循环后,i为父节点的树的最大值放在顶点了
			arr[i] = temp;// 将temp放在最后的为止
		}
	}
}

哈夫曼树:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

构成哈夫曼树的步骤:

在这里插入图片描述

构建哈夫曼树代码(使用ArrayList集合):

import java.util.ArrayList;
import java.util.Collections;

public class HuffmanTree {
	public static void main(String[] args) {
		int[] arr = { 13, 7, 8, 3, 29, 6, 1 };
		Node node = createHuffmanTree(arr);

	}
	


	public static Node createHuffmanTree(int[] arr) {
		// 第一步为了操作方便
		// 1.遍历arr数组
		// 2.将arr中每个元素够构成一个Node对象
		// 3.使用ArrayList管理Node
		ArrayList<Node> list = new ArrayList<Node>();
		for (int value : arr) {
			list.add(new Node(value));
		}
		Collections.sort(list);
		while (list.size() != 1) {
			// 取出最小值(不一定小就为左子树,没有要求)
			Node leftNode = list.get(0);

			// 取出次小值
			Node rightNode = list.get(1);

			// 构建新的Node结点
			Node parentNode = new Node(leftNode.value + rightNode.value);

			// 构建父子结点的关系
			parentNode.leftNode = leftNode;
			parentNode.rightNode = rightNode;

			// 删除list中的最小和慈孝
			list.remove(leftNode);
			list.remove(rightNode);

			// 加入新创建的结点
			list.add(parentNode);
			Collections.sort(list);
		}
//		System.out.println(list);
		return list.get(0);
		
	}
}

//为了让Node对象持续排序Collections集合排序
//让Node实现Comparable接口
class Node implements Comparable<Node> {
	public int value;
	public Node leftNode;
	public Node rightNode;

	public Node(int value) {
		this.value = value;
	}

	@Override
	public String toString() {
		return "Node [value=" + value + "]";
	}

	@Override
	public int compareTo(Node o) {
		// 表示从小到大排序
		return this.value - o.value;
	}

}

哈夫曼编码:

在这里插入图片描述

如何编码:

在这里插入图片描述

哈夫曼编码代码实现(编码和解码):

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class HuffmanCode {

	public static void main(String[] args) {
		String string = "i like like like java do you like a java";
		byte[] zip = getHuffmanBytes(string);
		byte[] decode = decode(huffmanCodes,zip);
		String string2 = new String(decode);
		
		System.out.println(string2);

	}

	public static byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < huffmanBytes.length; i++) {
			byte b = huffmanBytes[i];
			// 需要一个flag来判断是否是最后一个,可能要补高位
			boolean flag = (i == huffmanBytes.length - 1);
			stringBuilder.append(byteToBitString(!flag, b));
		}

		// 将哈夫曼标的key和value颠倒过来,便于后续步骤
		Map<String, Byte> map = new HashMap<String, Byte>();
		for (Entry<Byte, String> entry : huffmanCodes.entrySet()) {
			map.put(entry.getValue(), entry.getKey());
		}

		// 此时stringBuilder的值为二进制(字符串表示)
		// 创建集合类,存放byte
		List<Byte> list = new ArrayList<>();
		// i用来扫描stringBuilder
		for (int i = 0; i < stringBuilder.length();) {
			int count = 1;// 计数器
			boolean flag = true;// 用来控制退出循环扫描
			Byte b = null; // 用来存放获取到的byte值

			while (flag) {
				String key = stringBuilder.substring(i, i + count);
				// 试着获取
				b = map.get(key);

				// 说明没有匹配到
				if (b == null) {
					// 增加长度,继续尝试
					count++;
				} else {
					// 匹配成功,flag设置为false
					flag = false;
				}
			}
			list.add(b);
			// 更新i的值,从下一个位置开始找
			i += count;
		}
		// 到这里为止,list中存放着所有byte
		byte[] b = new byte[list.size()];
		for (int i = 0; i < list.size(); i++) {
			b[i] = list.get(i);
		}
		return b;

	}

	/**
	 * 将一个byte转成一个二进制的字符串
	 * 
	 * @param flag 标志是否需要补高位如果是true,表示需要补高位,如果时false表示不补
	 * @param b    传入的byte
	 * @return 是b的二进制的字符串
	 */
	public static String byteToBitString(Boolean flag, byte b) {
		// 使用变量保存b
		int temp = b;// 将b转成int
		// 如果是正数我们还存在
		// 这里返回的时补码
		if (flag) {
			temp |= 256;
		}
		String string = Integer.toBinaryString(temp);
		if (flag) {
			return string.substring(string.length() - 8);
		} else {
			return string;
		}
	}

	

	public static byte[] getHuffmanBytes(String string) {
		byte[] bytes = string.getBytes();
		List<Node2> nodes = getNodes(bytes);
		Node2 root = createHuffmantree(nodes);

		getCodes(root, "", stringBuilder);
		byte[] zip = zip(bytes, huffmanCodes);
		return zip;
	}

	/**
	 * 
	 * @param bytes       原始的字符串对应的byte[]
	 * @param hufmanCodes 生成的哈夫曼编码
	 * @return 处理后的byte[]
	 */
	public static byte[] zip(byte[] bytes, Map<Byte, String> hufmanCodes) {
		StringBuilder stringBuilder = new StringBuilder();

		for (int i = 0; i < bytes.length; i++) {
			stringBuilder.append(hufmanCodes.get(bytes[i]));
		}
		// 将01字符串转为字节数组
		int len;
		if (stringBuilder.length() % 8 == 0) {
			len = stringBuilder.length() / 8;
		} else {
			len = stringBuilder.length() / 8 + 1;
		}
		byte[] by = new byte[len];
		int index = 0;
		String string;
		for (int i = 0; i < stringBuilder.length(); i += 8) {

			if (i + 8 < stringBuilder.length()) {
				string = stringBuilder.substring(i, i + 8);

			} else {
				string = stringBuilder.substring(i);
			}
			by[index++] = (byte) Integer.parseInt(string, 2);
		}
		return by;
	}

	static Map<Byte, String> huffmanCodes = new HashMap<Byte, String>();
	static StringBuilder stringBuilder = new StringBuilder();

	/**
	 * 功能:将传入的node结点时所有叶子结点时的赫夫曼树
	 * 
	 * @param node          传入结点
	 * @param code          路径:左子节点是0 ,右子节点 1
	 * @param stringBuilder 用于拼接路径
	 */
	public static void getCodes(Node2 node, String code, StringBuilder stringBuilder) {
		// 为每一个结点都创建一个StringBuilder
		StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);

		stringBuilder2.append(code);
		if (node != null) {// 如果node等于null不处理
			if (node.dataByte == null) {// 非叶子结点
				getCodes(node.leftNode, "0", stringBuilder2);

				getCodes(node.rightNode, "1", stringBuilder2);

			} else {
				huffmanCodes.put(node.dataByte, stringBuilder2.toString());
			}
		}
	}

	public static List<Node2> getNodes(byte[] bytes) {
		ArrayList<Node2> list = new ArrayList<Node2>();

		HashMap<Byte, Integer> map = new HashMap<Byte, Integer>();
		for (int i = 0; i < bytes.length; i++) {
			if (!map.containsKey(bytes[i])) {
				map.put(bytes[i], 1);
			} else {
				map.put(bytes[i], map.get(bytes[i]) + 1);
			}
		}
		for (Entry<Byte, Integer> setMap : map.entrySet()) {
			list.add(new Node2(setMap.getKey(), setMap.getValue()));
		}
		return list;

	}

	public static Node2 createHuffmantree(List<Node2> list) {
		while (list.size() != 1) {
			Collections.sort(list);
			Node2 leftNode = list.get(0);
			Node2 rightNode = list.get(1);
			Node2 parentNode = new Node2(leftNode.weight + rightNode.weight);
			parentNode.leftNode = leftNode;
			parentNode.rightNode = rightNode;
			list.remove(leftNode);
			list.remove(rightNode);
			list.add(parentNode);

		}
		return list.get(0);
	}
}

class Node2 implements Comparable<Node2> {
	Byte dataByte;
	int weight;
	Node2 rightNode;
	Node2 leftNode;

	public Node2(Byte dataByte, int weight) {
		this.dataByte = dataByte;
		this.weight = weight;
	}

	public Node2(int weight) {
		this.weight = weight;
	}

	@Override
	public int compareTo(Node2 o) {
		// TODO Auto-generated method stub
		// 从小到大排序
		return this.weight - o.weight;
	}

	@Override
	public String toString() {
		return "Node [dataByte=" + dataByte + ", weight=" + weight + "]";
	}

}

发布了68 篇原创文章 · 获赞 12 · 访问量 5196

猜你喜欢

转载自blog.csdn.net/qq_40963076/article/details/105296267