《算法》第五章部分程序 part 8
▶ 书中第五章部分程序,包括在加上自己补充的代码,适用于基因序列的 2-Bit 压缩算法,行程长压缩算法,Huffman 压缩算法,LZW 压缩算法
● 适用于基因序列的 2-Bit 压缩算法
package package01; import edu.princeton.cs.algs4.BinaryStdIn;
import edu.princeton.cs.algs4.BinaryStdOut
import edu.princeton.cs.algs4.Alphabet; public class class01
{
private class01() {} public static void compress() // 压缩,ATGC 每个字母只用 00、01、10、11 之一来表示
{
Alphabet DNA = Alphabet.DNA;
String s = BinaryStdIn.readString();
int n = s.length();
BinaryStdOut.write(n);
for (int i = 0; i < n; i++)
BinaryStdOut.write(DNA.toIndex(s.charAt(i)), 2); // 找到字母在字母表 DNA 中的索引(0 ~ 3),转化为两位二进制字符
BinaryStdOut.close(); // 关闭二进制流,补齐字符字节,防止出错
} public static void expand() // 扩展
{
Alphabet DNA = Alphabet.DNA;
int n = BinaryStdIn.readInt();
for (int i = 0; i < n; i++)
BinaryStdOut.write(DNA.toChar(BinaryStdIn.readChar(2)), 8); // 读入两位二进制字符,作为索引得到对应的字母,转为 Byte 写出
BinaryStdOut.close();
} public static void main(String[] args)
{
if (args[0].equals("-")) // '-' 表压缩,'+' 表扩展
compress();
else if (args[0].equals("+"))
expand();
else
throw new IllegalArgumentException("\n<main> Illegal command.\n");
}
}
● 行程长压缩算法,注意输入和输出都是二进制串
package package01; import edu.princeton.cs.algs4.BinaryStdIn;
import edu.princeton.cs.algs4.BinaryStdOut; public class class01
{
private static final int R = 256; // 最大行程长
private static final int LG_R = 8; // log(R),为了表示最大行程长需要的 Bit 数 private class01() {} public static void compress()
{
char run = 0; // 累计扫描相同的字符数,初始化为 0
for (boolean b = false; !BinaryStdIn.isEmpty();)// 默认第 1 个字符为 false
{
if (BinaryStdIn.readBoolean() != b) // 与上一个字符不同
{
BinaryStdOut.write(run, LG_R); // 以规定的 Bit 宽度写入累计行程长
run = 1; // 累计扫描数设定为 1
b = !b; // 翻转字符 b
}
else // 与上一个字符相同
{
if (run == R - 1) // 累计扫描述已经最大,需要切成两截分别记录
{
BinaryStdOut.write(run, LG_R); // 先写入长为 R-1 的行程
run = 0;
BinaryStdOut.write(run, LG_R); // 再插入一个长为 0 的行程
}
run++; // 计数器自加 1
}
}
BinaryStdOut.write(run, LG_R);
BinaryStdOut.close();
} public static void expand()
{
for (boolean b = false; !BinaryStdIn.isEmpty();)// 默认第 1 个字符为 false
{
int run = BinaryStdIn.readInt(LG_R); // 按规定的 Bit 宽度读入一个行程长
for (int i = 0; i < run; i++) // 向输出中写行程长个数的字符 b
BinaryStdOut.write(b);
b = !b; // 翻转字符 b
}
BinaryStdOut.close();
} public static void main(String[] args)
{
if (args[0].equals("-"))
compress();
else if (args[0].equals("+"))
expand();
else
throw new IllegalArgumentException("\n<main> Illegal command.\n");
}
}
● Huffman 压缩算法
package package01; import edu.princeton.cs.algs4.BinaryStdIn;
import edu.princeton.cs.algs4.BinaryStdOut;
import edu.princeton.cs.algs4.MinPQ; public class class01
{
private static final int R = 256; // 字符表基数 private class01() { } private static class Node implements Comparable<Node> // Trie 树节点
{
private final char ch; // 字符,频率和左右子树
private final int freq;
private final Node left, right; Node(char inputCh, int inputFreq, Node inputLeft, Node inputRight)
{
ch = inputCh;
freq = inputFreq;
left = inputLeft;
right = inputRight;
} private boolean isLeaf() // 是否为叶节点
{
return (left == null) && (right == null);
} public int compareTo(Node that) // 两节点相比较
{
return this.freq - that.freq;
}
} public static void compress()
{
char[] input = BinaryStdIn.readString().toCharArray();
int[] freq = new int[R]; // 统计字母频率
for (int i = 0; i < input.length; i++)
freq[input[i]]++;
Node root = buildTrie(freq); // 用频率表建立 Trie 树
String[] st = new String[R]; // 建立查找表,将字符映射到 01 串
buildCode(st, root, "");
writeTrie(root); // 首先写入 Trie 树用于解压
BinaryStdOut.write(input.length); // 写输入串的长度
for (int i = 0; i < input.length; i++) // 压缩过程,循环每次处理一个字符
{
String code = st[input[i]];
for (int j = 0; j < code.length(); j++)
BinaryStdOut.write(code.charAt(j) != '0'); // code[j] == '0' 则条件不成立,写个 0,反之写个 1
}
BinaryStdOut.close();
} public static void expand()
{
Node root = readTrie(); // 首先读取 Trie 树
int length = BinaryStdIn.readInt(); // 读取输入串的长度
for (int i = 0; i < length; i++) // 解压过程,循环每次处理一个字符
{
Node x = root;
for (; !x.isLeaf(); x = (BinaryStdIn.readBoolean() ? x.right : x.left)); // 顺着Trie 树往下直到叶节点
BinaryStdOut.write(x.ch, 8); // 输出叶节点对应的字符
}
BinaryStdOut.close();
} private static Node buildTrie(int[] freq) // 用频率表建立 Trie 树,返回根节点
{
MinPQ<Node> pq = new MinPQ<Node>(); // 初始化所有字母节点,入队
for (char i = 0; i < R; i++)
{
if (freq[i] > 0)
pq.insert(new Node(i, freq[i], null, null));
}
if (pq.size() == 1) // 只有一个字母的情况,原文不是 '\0' 则插入 '\0',不然插入 '\1'
pq.insert(new Node(freq['\0'] == 0 ? '\0' : '\1', 0, null, null)); for (; pq.size() > 1;) // 每次合并两个频率最小的节点并入队,直到队列中只剩一个元素
{
Node left = pq.delMin(), right = pq.delMin();
Node parent = new Node('\0', left.freq + right.freq, left, right);
pq.insert(parent);
}
return pq.delMin(); // 最后剩下的节点就是根节点,其 freq 即为总字符数
} private static void buildCode(String[] st, Node x, String s) // 遍历以 x 为根节点的树,每到达叶节点就对查找表 st 赋字符串值,当前字符串前缀为 s
{
if (x.isLeaf())
st[x.ch] = s; // x.ch 是叶节点对应的字母(如 a = 97),将查找表中该索引位置的值设为遍历进来时得到的 01 串
else
{
buildCode(st, x.left, s + '0'); // 往左遍历,串上加一个 '0'
buildCode(st, x.right, s + '1'); // 往左遍历,串上加一个 '1'
}
} private static void writeTrie(Node x) // 将 x 为根节点的 Trie 树写成字符串
{
if (x.isLeaf()) // x 是叶节点,写个 1,后跟字符的 8 Bit 二进制表示,然后递归回退
{
BinaryStdOut.write(true);
BinaryStdOut.write(x.ch, 8);
return;
}
BinaryStdOut.write(false); // x 不是叶节点,写个 0,然后分别遍历 x 的左右子节点
writeTrie(x.left);
writeTrie(x.right);
} private static Node readTrie() // 读取 Trie 树,注意频率没有用,全都设为 -1
{
if (BinaryStdIn.readBoolean()) // 当前位是 1 则说明接下来是叶节点,一次性读入 8 Bit,转成 char
return new Node(BinaryStdIn.readChar(), -1, null, null);
else // 当前位是 0 则说明还没有到叶节点,左右子节点分别向下递归
return new Node('\0', -1, readTrie(), readTrie());
} public static void main(String[] args)
{
if (args[0].equals("-"))
compress();
else if (args[0].equals("+"))
expand();
else
throw new IllegalArgumentException("\n<main> Illegal command.\n");
}
}
● LZW 压缩算法
package package01; import edu.princeton.cs.algs4.BinaryStdIn;
import edu.princeton.cs.algs4.BinaryStdOut;
import edu.princeton.cs.algs4.TST; public class class01
{
private static final int R = 256; // 字符表基数
private static final int W = 12; // 编码宽度(Bit)
private static final int L = 4096; // 编码表大小, L = 2^W private class01() {} public static void compress()
{
String input = BinaryStdIn.readString();
TST<Integer> st = new TST<Integer>(); // 编译表
for (int i = 0; i < R; i++) // 先初始化所有的单字符
st.put("" + (char)i, i);
for (int code = R + 1; input.length() > 0;) // st[R] 标记 EOF,st[R+1] ~ st[L-1] 记录新的编码
{
String s = st.longestPrefixOf(input); // 在输入中寻找目前掌握的最长前缀
BinaryStdOut.write(st.get(s), W); // 把当前最长前缀的编码写到输出中,指定宽度 W
int t = s.length();
if (t < input.length() && code < L) // 刚才找到的前缀短于原文剩余长度,且编译表还没填满
st.put(input.substring(0, t + 1), code++); // 连着刚才找到的前缀以及 1 个前瞻字符一起编码为编译表的新一项
input = input.substring(t); // 砍掉原文前 t 字符,用于下一次扫描
}
BinaryStdOut.write(R, W); // 最后写上字符表基数
BinaryStdOut.close();
} public static void expand()
{
String[] st = new String[L];
for (int i = 0; i < R; i++) // 初始化编译表的前 R 项
st[i] = "" + (char)i;
st[R] = ""; // st[R] 标记 EOF(这里没用) int codeword = BinaryStdIn.readInt(W); // 读入定宽字符,如果读入的字符就是字符集基数,说明正文部分是空的,不用解压
if (codeword == R)
return;
String val = st[codeword]; // 从编译表中找到原文,第一个定宽字符的原文肯定是单字符
for (int i = R + 1;;) // i 指向当前编译表中的首个空白项
{
BinaryStdOut.write(val); // 找到的原文写到输出流
codeword = BinaryStdIn.readInt(W); // 读取下一个定宽字符,同 for 之前的部分
if (codeword == R)
break;
String s = st[codeword];
if (i == codeword) // 特殊情况,欲查找的编码欲当前段字符串的编码相同
s = val + val.charAt(0); // 此时原文的最后一个字符与首字符相同,补上
if (i < L) // 编译表还没填满
st[i++] = val + s.charAt(0); // 连着之前的原文以及当前原文的第 1 个字符一起编码为编译表的新一项
val = s; // 当前原文保存到 val 中,用于下次编码
}
BinaryStdOut.close();
} public static void main(String[] args)
{
if (args[0].equals("-"))
compress();
else if (args[0].equals("+"))
expand();
else
throw new IllegalArgumentException("\n<main> Illegal command.\n");
}
}
《算法》第五章部分程序 part 8的更多相关文章
- 《算法》第五章部分程序 part 3
▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串高位优先排序(美国国旗排序) ● 美国国旗排序 package package01; import edu.princeton.cs.algs4 ...
- 《算法》第五章部分程序 part 7
▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串的二进制表示.十六进制表示.图形表示 ● 二进制表示 package package01; import edu.princeton.cs.al ...
- 《算法》第五章部分程序 part 6
▶ 书中第五章部分程序,包括在加上自己补充的代码,非确定性有穷自动机(NFA),grep 命令(利用 NFA 匹配) ● 非确定性有穷自动机(NFA) package package01; impor ...
- 《算法》第五章部分程序 part 5
▶ 书中第五章部分程序,包括在加上自己补充的代码,Knuth-Morris-Pratt 无回溯匹配,Boyer - Moore 无回溯匹配,Rabin - Karp 指纹匹配 ● Knuth-Morr ...
- 《算法》第五章部分程序 part 4
▶ 书中第五章部分程序,包括在加上自己补充的代码,Trie 树类,Trie 集合,三值搜索树(Ternary Search Trie) ● Trie 树类 package package01; imp ...
- 《算法》第五章部分程序 part 2
▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串高位优先排序(计数 + 插排),(原地排序),(三路快排,与前面的三路归并排序相同) ● 计数 + 插排 package package01; ...
- 《算法》第五章部分程序 part 1
▶ 书中第五章部分程序,包括在加上自己补充的代码,字母表类,字符串低位优先排序(桶排) ● 字母表类 package package01; import edu.princeton.cs.algs4. ...
- Gradle 1.12用户指南翻译——第四十五章. 应用程序插件
本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- 《算法》第一章部分程序 part 1
▶ 书中第一章部分程序,加上自己补充的代码,包括若干种二分搜索,寻找图上连通分量数的两种算法 ● 代码,二分搜索 package package01; import java.util.Arrays; ...
随机推荐
- java中关于File类的细说
File类是我们接触的比较多的类,当初学习是真是傻傻分不清啊.今天就总结一下file的一些比较不好区分的地方. 首先:当然就是构造方法 File f = new File("文件路径&quo ...
- 使用Html Agility Pack快速解析Html内容
Html Agility Pack 是一个开源的.NET 方案HTML解析器. 开源地址:https://github.com/zzzprojects/html-agility-pack 用法:vs上 ...
- Android 引用库项目,Debug 库项目
转自:http://www.cnblogs.com/xitang/p/3615768.html#commentform 使用引用项目,无法追到源代码,无法Debug库项目The JAR of this ...
- FB的破解与安装
1使用破解序列号安装 先找到host文件,一般可能是隐藏的windows/system32/drivers/etc在下面加入127.0.0.1 activate.adobe.com127.0.0.1 ...
- P2430严酷的训练
传送 这个题的题干很长,长到令人恶心 这个题的p乍一看好像没有卵用,但其实他很有用(废话).这里的“费用”不再是tw[i](wky做第i道题的时间),而是tw[p[i]](wky做第i道题所对应的知识 ...
- 服务容错保护断路器Hystrix之六:缓存功能的使用
高并发环境下如果能处理好缓存就可以有效的减小服务器的压力,Java中有许多非常好用的缓存工具,比如Redis.EHCache等,当然在Spring Cloud的Hystrix中也提供了请求缓存的功能, ...
- mysql监测工具
可视性是系统设计的最佳境界,MySQL 也不例外.一旦完成了 MySQL 环境的搭建.运行并调优,您千万不要认为已经万事大吉了. 数据库环境既会受到来自系统更改或流量负荷的影响,也会遇到例如流量高峰. ...
- mariadb semi plugin遇到的坑
安装完semi plugin运行一段时间后,重启mariadb, 突然发现canal无法解析数据了,一直在报错,然后登陆mariadb, show plugins竟然没有看到之前安装的semi plu ...
- 笔记函数 - Ring0 Sleep()
#define DELAY_ONE_MICROSECOND (-10) #define DELAY_ONE_MILLISEND (DELAY_ONE_MICROSECOND*1000) void Sl ...
- 协议无关组播-密集模式 PIM-DM
一.组播路由协议 (一) 路由器依靠转发项来转发组播数据包.转发项的生成则是组播路由协议所要完成的任务.组播路由协议有距离矢量组播路由协议(DVMRP).协议无关组播-密集模式(PIM-DM).协议无 ...