笔试会遇到的树相关的算法

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 算法

  1. 将图中所有边按权重从小到大排序,假设放在集合 G 当中,集合 S 放即将构成最小生成树所选的边,刚开始时,集合 S 为空集
  2. 逐渐选取权重最小的边,若此边与已经已经选中的边没有构成环,则放进 S 集合中
  3. 重复第二步,直至 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];
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值