1. 构建哈夫曼树
public class Huffman {
static class Node{
int val;
Node left, right;
Node(int val){
this.val = val;
}
}
// 看作是合并森林中的树
private static Node buildHuffman(int[] w) {
Queue<Node> heap = new PriorityQueue<>((a, b)->(a.val-b.val));
for(int i = 0; i < w.length; ++i) {
heap.offer(new Node(w[i]));
}
while(heap.size() > 1) {
// 弹出两棵树,合并成一颗再压入堆中
Node tree1 = heap.poll();
Node tree2 = heap.poll();
Node newTree = new Node(tree1.val+tree2.val);
newTree.left = tree1;
newTree.right = tree2;
heap.offer(newTree);
}
return heap.peek();
}
}
2. 最小生成树
kruskal 算法
- 将图中所有边按权重从小到大排序,假设放在集合 G 当中,集合 S 放即将构成最小生成树所选的边,刚开始时,集合 S 为空集
- 逐渐选取权重最小的边,若此边与已经已经选中的边没有构成环,则放进 S 集合中
- 重复第二步,直至 S 集合中的边的数量为 G 中(顶点数-1)
/**
* graph: 列数为 3 的二维数组,每一行分别代表边的两个顶点和边的权值
* m: 顶点个数
*/
// 返回最小生成树的权值和
private static int kruskal(int[][] graph, int m) {
int n = graph.length;
// 将边按权值从小到大排序
Arrays.sort(graph, (a, b)->(a[2]-b[2]));
int edge = 0, result = 0;
for(int i = 0; i < n && edge < m-1; ++i) {
// 判断当前边的两个端点是否属于同一棵树
if(find(graph[i][0]) != find(graph[i][1])) {
union(graph[i][0], graph[i][1]);
result += graph[i][2];
edge++;
}
}
// 如果加入边的数量小于 m-1,则无向图不存在最小生成树
if(edge < m-1) result = -1;
return result;
}
其中,find 和 union 为并查集操作。
3. 并查集
同时使用路径压缩、按秩合并优化的并查集,其每个操作的时间复杂度近似为 O(1)。
根结点没有双亲,可以用来存储启发式信息(使用负数)。
class UnionFind {
int[] parent;
public UnionFind(int n) {
parent = new int[n];
for(int i = 0; i < n; ++i) {
parent[i] = -1;
}
}
public void union(int a, int b) {
int ra, rb;
if((ra = find(a)) == (rb = find(b))) return;
if(parent[ra] < parent[rb]) parent[rb] = ra;
else parent[ra] = rb;
// 只有当两棵树深度相等时,才更新深度信息。
if(parent[ra] == parent[rb]) --parent[rb];
}
public int find(int a) {
int root = a;
// 获得根结点 root
while(parent[root] >= 0){
root = parent[root];
}
int t;
// 将路径上的双亲都改为根结点
while(parent[a] >= 0){
t = parent[a];
parent[a] = root;
a = t;
}
return root;
}
}
4. 前缀树
基于二维数组实现的前缀树:
public class Trie {
final int maxn = 1000;
int[][] tree = new int[maxn][26];
int[] count = new int[maxn];
boolean[] isEnd = new boolean[maxn];
int last;
public void insert(String word) {
int p = 0;
for(int i = 0; i < word.length(); ++i) {
int c = word.charAt(i)-'a';
if(tree[p][c] == 0) tree[p][c] = ++last;
p = tree[p][c];
count[p]++;
}
isEnd[p] = true;
}
public int countPrefix(String prefix) {
int p = 0;
for(int i = 0; i < prefix.length(); ++i) {
int c = prefix.charAt(i)-'a';
if(tree[p][c] == 0) return 0;
p = tree[p][c];
}
return count[p];
}
}