Minimum Spanning Tree
1. Greedy Algorithms
Aside from Dynamic Programming, Greedy Algorithm is another kind of optimization maneuver, in which every time we make a locally optimal choice and finally get the global optimum. According to CLRS, a problem exhibits Optimal Substructure if an optimal solution to the problem contains optimal solutions to the sub-problems. A good case in point may be Huffman Coding, which generates optimal prefix code in light of the frequencies of different symbols. Here is my solution to an SJTU ACM Online Judge problem concerning such coding strategy:
1 import java.util.*; 2 3 class Huffman { 4 private class Node { 5 public int freq, sum; 6 7 public Node(int freq,int sum) { 8 this.freq = freq; 9 this.sum = sum; 10 } 11 } 12 private int size, len; 13 private Node[] arr; 14 15 public Huffman() { 16 size = 50000; 17 arr = new Node[size]; 18 } 19 public void doubleSpace() { 20 Node[] tmp = arr; 21 arr = new Node[size<<1]; 22 for (int i=0;i<size;i++) { 23 arr[i] = tmp[i]; 24 } 25 size <<= 1; 26 } 27 public void getLeaf(int f) { 28 if (len==size) { 29 doubleSpace(); 30 } 31 arr[len++] = new Node(f,0); 32 } 33 public int freqSum() { 34 // Return the weighted sum of prefix code length 35 PriorityQueue<Node> q = new PriorityQueue<Node>(len, 36 new Comparator<Node>() { 37 public int compare(Node a,Node b) { 38 if (a.freq<b.freq) { 39 return -1; 40 } else if (a.freq>b.freq) { 41 return 1; 42 } else { 43 return 0; 44 } 45 } 46 } 47 ); 48 for (int i=0;i<len;i++) { 49 q.add(arr[i]); 50 } 51 while (q.size()>1) { 52 Node a = q.poll(), b = q.poll(); 53 q.add(new Node(a.freq+b.freq,a.freq+b.freq+a.sum+b.sum)); 54 } 55 return q.peek().sum; 56 } 57 } 58 59 public class Main { 60 public static void main(String[] args) { 61 Huffman tree = new Huffman(); 62 Scanner in = new Scanner(System.in); 63 int n=in.nextInt(),tmp; 64 for (int i=0;i<n;i++) { 65 tmp = in.nextInt(); 66 tree.getLeaf(tmp); 67 } 68 System.out.println(tree.freqSum()); 69 in.close(); 70 } 71 }
2. Prim's Algorithm
To find a Minimal Spanning Tree of a given weighted graph, Prim's Algorithm is a greedy strategy in which we always choose an optimal vertex and connect it to the spanning tree. The following code is my solution to the USACO training problem "agrinet":
1 import java.io.*; 2 import java.util.*; 3 4 public class agrinet { 5 public static final int INF = 100001; 6 public static Scanner input; 7 public static PrintWriter output; 8 public static int num; 9 public static int [][] cost; 10 11 public static int genMST() { 12 // Prim's Algorithm based on Adjacency Matrix: 13 int val=0, pos=0; 14 boolean vis[] = new boolean [num]; 15 int dist[] = new int [num]; 16 vis[0] = true; 17 for (int i=1;i<num;i++) { 18 dist[i] = INF; 19 } 20 for (int i=1;i<num;i++) { 21 for (int j=0;j<num;j++) { 22 // update all the remaining vertices 23 if (!vis[j] && dist[j]>cost[pos][j]) 24 dist[j] = cost[pos][j]; 25 } 26 dist[pos] = INF; 27 for (int j=0;j<num;j++) { 28 // search for an optimal vertex 29 if (!vis[j] && dist[j]<dist[pos]) { 30 pos = j; 31 } 32 } 33 vis[pos] = true; 34 val += dist[pos]; 35 } 36 return val; 37 } 38 public static void main(String[] args) throws IOException { 39 input = new Scanner(new FileReader("agrinet.in")); 40 num = input.nextInt(); 41 cost = new int[num][num]; 42 for (int i=0;i<num;i++) { 43 for (int j=0;j<num;j++) { 44 cost[i][j] = input.nextInt(); 45 } 46 } 47 input.close(); 48 output = new PrintWriter(new FileWriter("agrinet.out")); 49 output.println(genMST()); 50 output.close(); 51 } 52 }
3. Kruskal's Algorithm
Kruskal's Algorithm is another greedy method to build a Minimal Spanning Tree, in which every time we add an optimal safe edge to an edge set. Here I provide my solution an SJTU ACM Online Judge problem as an example:
1 import java.util.*; 2 3 class DisjointSet { 4 private int[] root; // root of each item 5 private int size; // number of items 6 7 public DisjointSet(int size) { 8 if (size<=0) { 9 throw new RuntimeException("Illegal Initial Size"); 10 } else { 11 this.size = size; 12 root = new int[size]; 13 for (int i=0;i<size;i++) { 14 root[i] = -1; 15 } 16 } 17 } 18 public void union(int i,int j) { 19 // Merge the set of i and the set of j 20 if (i<0||i>=size||j<0||j>=size) { 21 throw new RuntimeException("IndexOutOfBounds on union"); 22 } else { 23 i = find(i); 24 j = find(j); 25 if (i!=j) { 26 // BEWARE that i and j may be identical 27 if (root[i]<root[j]) { 28 root[i] += root[j]; 29 root[j] = i; 30 } else { 31 root[j] += root[i]; 32 root[i] = j; 33 } 34 } 35 } 36 } 37 public int find(int idx) { 38 // Determine and return root[idx] 39 if (idx<0||idx>=size) { 40 throw new RuntimeException("IndexOutOfBounds on find"); 41 } else { 42 int prev = root[idx]; 43 if (prev<0) { 44 return idx; 45 } else { 46 root[idx] = find(prev); 47 return root[idx]; 48 } 49 } 50 } 51 } 52 53 class AdjList { 54 private class Vert { 55 public Edge next; 56 } 57 private class Edge { 58 public int end, weight; 59 public Edge next; 60 61 public Edge(int end,int weight,Edge next) { 62 this.end = end; 63 this.weight = weight; 64 this.next = next; 65 } 66 } 67 private class HeapItem { 68 public int start, end, weight; 69 70 public HeapItem(int start,int end,int weight) { 71 this.start = start; 72 this.end = end; 73 this.weight = weight; 74 } 75 } 76 77 public static final int INF = 100001; 78 private int num; 79 private Vert [] vert; 80 81 public AdjList(int num) { 82 this.num = num; 83 vert = new Vert[num]; 84 for (int i=0;i<num;i++) { 85 vert[i] = new Vert(); 86 } 87 } 88 public void insert(int i,int j,int w) { 89 if (i<j) { 90 vert[i].next = new Edge(j,w,vert[i].next); 91 } else { 92 vert[j].next = new Edge(i,w,vert[j].next); 93 } 94 } 95 public int genMST() { 96 // Kruskal's Algorithm based on Adjacency List: 97 DisjointSet set = new DisjointSet(num); 98 PriorityQueue<HeapItem> q = new PriorityQueue<HeapItem>(INF, 99 new Comparator<HeapItem>() { 100 public int compare(HeapItem a,HeapItem b) { 101 if (a.weight<b.weight) { 102 return -1; 103 } else if (a.weight>b.weight) { 104 return 1; 105 } else { 106 return 0; 107 } 108 } 109 } 110 ); 111 for (int i=0;i<num;i++) { 112 Edge itr = vert[i].next; 113 while (itr!=null) { 114 q.add(new HeapItem(i,itr.end,itr.weight)); 115 itr = itr.next; 116 } 117 } 118 int cnt=0, val=0; 119 while (cnt<num-1) { 120 HeapItem e = q.poll(); 121 if (set.find(e.start)!=set.find(e.end)) { 122 set.union(e.start,e.end); 123 val += e.weight; 124 cnt++; 125 } 126 } 127 return val; 128 } 129 } 130 131 public class Main { 132 public static void main(String[] args) { 133 Scanner in = new Scanner(System.in); 134 int v = in.nextInt(); 135 int e = in.nextInt(); 136 AdjList g = new AdjList(v); 137 for (int i=0;i<e;i++) { 138 int j = in.nextInt()-1; 139 int k = in.nextInt()-1; 140 int w = in.nextInt(); 141 g.insert(j,k,w); 142 } 143 in.close(); 144 System.out.println(g.genMST()); 145 } 146 }
References:
1. Cormen, T. H. et al. Introduction to Algorithms [M] . 北京:机械工业出版社, 2006-09