java贪心算法哈夫曼_贪心算法之哈夫曼编码

哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法。其压缩率通常在20%~90%之间。哈夫曼编码算法用字符在文件中出现的频率表来建立一个用0,1串表示各字符的最优表示方式。

给出现频率高的字符较短的编码,出现频率较低的字符以较长的编码,可以大大缩短总码长。

1614d9a037811d70d1b4cc3fbbfb25b0.png

定长码:

3*(45+13+12+16+9+5) = 300 千位

变长码:

1*45+3*13+3*12+3*16+4*9+4*5 = 224 千位

1、前缀码

对每一个字符规定一个0,1串作为其代码,并要求任一字符的代码都不是其它字符代码的前缀。这种编码称为前缀码。

编码的前缀性质可以使译码方法非常简单。

表示最优前缀码的二叉树总是一棵完全二叉树,即树中任一结点都有2个儿子结点。

f(c)表示字符c出现的概率,dt(c)表示c的码长

平均码长定义为:

1f00feac98280f9a80e9bde557092377.png

使平均码长达到最小的前缀码编码方案称为给定编码字符集C的最优前缀码。

2、构造哈夫曼编码

哈夫曼提出构造最优前缀码的贪心算法,由此产生的编码方案称为哈夫曼编码。

哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树T。

算法以|C|个叶结点开始,执行|C|-1次的“合并”运算后产生最终所要求的树T。

以f为键值的优先队列Q用在贪心选择时有效地确定算法当前要合并的2棵具有最小频率的树。一旦2棵具有最小频率的树合并后,产生一棵新的树,其频率为合并的2棵树的频率之和,并将新树插入优先队列Q。经过n-1次的合并后,优先队列中只剩下一棵树,即所要求的树T。

算法huffmanTree用最小堆实现优先队列Q。初始化优先队列需要O(n)计算时间,由于最小堆的removeMin和put运算均需O(logn)时间,n-1次的合并总共需要O(nlogn)计算时间。因此,关于n个字符的哈夫曼算法的计算时间为O(nlogn) 。

3、哈夫曼算法的正确性

要证明哈夫曼算法的正确性,只要证明最优前缀码问题具有贪心选择性质和最优子结构性质。

(1)贪心选择性质

(2)最优子结构性质

具体代码实现:

1: import java.util.LinkedHashMap;

2: import java.util.ArrayList;

3: import java.util.Set;

4: import java.util.Iterator;

5:

6: class HuffmanNode {

7: char label;

8: int weight;

9: int parent;

10: int lChild;

11: int rChild;

12: int frequency;//频率主要是用来衡量字符在给定编码字符串中出现的次数

13:

14: public HuffmanNode(char label, int weight, int parent, int lChild,

15: int rChild) {

16: this.label = label;

17: this.weight = weight;

18: this.lChild = lChild;

19: this.rChild = rChild;

20: }

21: }

22:

23: class HuffmanTree {

24: private LinkedHashMap charTable; //主要用hashmap来存放字符及其出现的频率

25: private Set charset;

26: private ArrayList huffmanTree;//huffman节点集合

27: private ArrayList huffmanCode;//huffman编码集合

28:

29: public HuffmanTree(LinkedHashMap map) {

30: charTable = map;

31: charset = map.keySet();

32: creatHuffmanTree();//首先创建huffman树

33: creatHuffmanCode();

34: }

35:

36: private void initTree() {

37: huffmanTree = new ArrayList();

38: Iterator charIter = charset.iterator();

39: int i = 1;

40: huffmanTree.add(0,

41: new HuffmanNode((char) 0, Integer.MAX_VALUE, 0, 0, 0));

42: while (charIter.hasNext()) {

43: Character ch = charIter.next();

44: huffmanTree.add(i, new HuffmanNode(ch, charTable.get(ch), 0, 0, 0));

45: i++;

46: }

47: for (int j = charset.size() + 1; j < 2 * charset.size(); j++) {

48: huffmanTree.add(j, new HuffmanNode((char) 0, 0, 0, 0, 0));

49: }

50: }

51:

52: // 创建huffman树

53: private void creatHuffmanTree() {

54: initTree();

55: int min_child1;

56: int min_child2;

57: for (int i = charset.size() + 1; i < 2 * charset.size(); i++) {

58: min_child1 = 0;

59: min_child2 = 0;

60: for (int j = 1; j < i; j++) {

61: if (huffmanTree.get(j).parent == 0) {

62: if (huffmanTree.get(j).weight < huffmanTree.get(min_child1).weight

63: || huffmanTree.get(j).weight < huffmanTree

64: .get(min_child2).weight) {

65: if (huffmanTree.get(min_child1).weight < huffmanTree

66: .get(min_child2).weight) {

67: min_child2 = j;

68: } else {

69: min_child1 = j;

70: }

71: }

72: }

73: }

74: huffmanTree.get(min_child1).parent = i;

75: huffmanTree.get(min_child2).parent = i;

76:

77: if (min_child1 < min_child2) {

78: huffmanTree.get(i).lChild = min_child1;

79: huffmanTree.get(i).rChild = min_child2;

80: } else {

81: huffmanTree.get(i).rChild = min_child1;

82: huffmanTree.get(i).lChild = min_child2;

83: }

84:

85: huffmanTree.get(i).weight = huffmanTree.get(i).weight

86: + huffmanTree.get(i).weight;

87: }

88: }

89:

90: private void creatHuffmanCode() {

91: huffmanCode = new ArrayList(charset.size() + 1);

92: huffmanCode.add(0, null);

93: char[] tempChars = new char[charset.size() + 1];

94: for (int i = 1; i < charset.size() + 1; i++) {

95: int startIndex = charset.size();

96: int parent = huffmanTree.get(i).parent;

97: int ch = i;

98: while (parent != 0) {

99: if (huffmanTree.get(parent).lChild == ch) {

100: tempChars[startIndex] = '0';

101: } else {

102: tempChars[startIndex] = '1';

103: }

104: startIndex--;

105: ch = parent;

106: parent = huffmanTree.get(parent).parent;

107: }

108: System.out.println(String.valueOf(tempChars, startIndex + 1,

109: charset.size() - startIndex));

110: huffmanCode.add(i, String.valueOf(tempChars, startIndex + 1,

111: charset.size() - startIndex));

112: }

113: }// end method

114:

115: // huffman编码

116: public String enCodeString(String inString) {

117: StringBuffer temp = new StringBuffer();

118: for (int i = 0; i < inString.length(); i++) {

119: int ch = inString.charAt(i);

120: int j = 1;

121: for (; huffmanTree.get(j).label != ch && j < charset.size() + 1; j++) {

122: }

123: if (j <= charset.size()) {

124: temp.append(huffmanCode.get(j));

125: } else {

126: temp.append(ch);

127: }

128: }

129: return temp.toString();

130: }

131:

132: // huffman解码

133: public String deCodeString(String inString) {

134: StringBuffer temp = new StringBuffer();

135: int root = charset.size() * 2 - 1;

136: for (int i = 0; i < inString.length(); i++) {

137: char ch = inString.charAt(i);

138: if (ch == '0') {

139: root = huffmanTree.get(root).lChild;

140: } else if (ch == '1') {

141: root = huffmanTree.get(root).rChild;

142: } else {

143: temp.append(ch);

144: }

145: if (root <= charset.size()) {

146: temp.append(huffmanTree.get(root).label);

147: root = charset.size() * 2 - 1;

148: }

149: }

150: return temp.toString();

151: }

152:

153: }

154:

155: public class HuffmanTreeTest {

156: public static void main(String[] args) {

157: LinkedHashMap hasmap = new LinkedHashMap();

158: hasmap.put('a', 4);

159: hasmap.put('b', 5);

160: hasmap.put('c', 8);

161: hasmap.put('d', 10);

162:

163: HuffmanTree huffman = new HuffmanTree(hasmap);

164: String temp = huffman.enCodeString("abcd");

165: System.out.println(temp);

166: System.out.println(huffman.deCodeString(temp));

167:

168: }

169:

170: }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值