▶ 书中第一章部分程序,加上自己补充的代码,包括若干种二分搜索,寻找图上连通分量数的两种算法
● 代码,二分搜索
1 package package01; 2 3 import java.util.Arrays; 4 import edu.princeton.cs.algs4.StdRandom; 5 6 public class class01 7 { 8 public int binarySearch(int [] a, int target) // 非递归实现 9 { 10 int lp = 0, rp = a.length - 1; 11 for(;lp <= rp;) // 含等号的条件,后面修改 lp 和 rp 是使用强的跳跃条件 12 { 13 int mp = lp + (rp - lp) / 2; 14 if (target < a[mp]) 15 rp = mp - 1; // 不要使用 mp 作为跳跃条件,会导致死循环 16 else if (target > a[mp]) 17 lp = mp + 1; 18 else 19 return mp; 20 } 21 return -1; 22 } 23 24 public int binarySearchRecursion(int [] a, int target)// 递归实现 25 { 26 return binarySearchRecursionKernel(a,target,0,a.length-1); 27 } 28 29 private int binarySearchRecursionKernel(int [] a, int target, int lp, int rp) 30 { 31 for(;lp<=rp;) 32 { 33 int mp = lp + (rp - lp) / 2; 34 if (target < a[mp]) 35 return binarySearchRecursionKernel(a,target,lp,mp - 1); 36 else if (target > a[mp]) 37 return binarySearchRecursionKernel(a,target,mp + 1, rp); 38 else 39 return mp; 40 } 41 return -1; 42 } 43 44 public static void main(String[] args) 45 { 46 int N = 10000; 47 int [] a = new int [N]; 48 for(int i=0;i<N;i++) 49 a[i] = i;//(int)(StdRandom.uniform()*10000); // 注释部分留作随机检验 50 //Arrays.sort(a); 51 52 int target = 7919;//(int)(StdRandom.uniform()*10000); 53 int output1 = binarySearch(a,target); 54 int output2 = binarySearchRecursion(a,target); 55 56 System.out.printf(" %d,%d ",output1,output2); 57 return; 58 } 59 }
● 重复元素二分搜索,包括查找第一次出现、最后一次出现,以及出现多少次
1 package package01; 2 3 import java.util.Arrays; 4 import edu.princeton.cs.algs4.StdRandom; 5 6 public class class01 7 { 8 public int binarySearchFirst(int [] a, int target) // 寻找第一个等于键值的目标 9 { 10 return binarySearchFirstKernel(a, target, 0, a.length-1); 11 } 12 13 private int binarySearchFirstKernel(int [] a, int target,int lp,int rp) // 添加起点和终点(两端点都包含),为了能与 Count 函数配合 14 { 15 for(;lp<=rp;) 16 { 17 int mp = lp + (rp - lp) / 2; 18 if (target < a[mp]) 19 rp = mp - 1; 20 else if (target > a[mp]) 21 lp = mp + 1; 22 else if(mp == 0 || target > a[mp-1]) // target == a[mp],找第一个 23 return mp; // a[mp] 就是第一个 24 else // a[mp] 前面还有等值的,不能强跳跃,否则可能跨空 25 rp = mp; 26 } 27 return -1; 28 } 29 30 public int binarySearchLast(int [] a, int target) // 寻找最后一个等于键值的目标 31 { 32 return binarySearchLastKernel(a,target, 0, a.length-1); 33 } 34 35 private int binarySearchLastKernel(int [] a, int target,int lp, int rp) // 添加起点和终点(两端点都包含) 36 { 37 for(;lp<=rp;) 38 { 39 int mp = lp + (rp - lp) / 2; 40 if (target < a[mp]) 41 rp = mp - 1; 42 else if (target > a[mp]) 43 lp = mp + 1; 44 else if(mp == a.length-1 || target < a[mp+1]) // target == a[mp],找最后一个 45 return mp; // a[mp] 就是第一个 46 else // a[mp] 后面还有等值的,不能强跳跃,否则可能跨空 47 lp = mp; 48 } 49 return -1; 50 } 51 52 public int binarySearchCount(int [] a, int target) // 寻找等于键值的元素个数 53 { 54 int lp = 0, rp = a.length-1; 55 for(;lp<=rp;) 56 { 57 int mp = lp + (rp - lp) / 2; 58 if (target < a[mp]) 59 rp = mp - 1; 60 else if (target > a[mp]) 61 lp = mp + 1; 62 else // 找到元素后,搜查首个和最后一个进行计数 63 return binarySearchLastKernel(a,target,lp,rp) - binarySearchFirstKernel(a,target,lp,rp) + 1; 64 } 65 return -1; 66 } 67 68 public static void main(String[] args) 69 { 70 int N = 10000; 71 int [] a = new int [N]; 72 for(int i=0;i<N;i++) 73 a[i] = i/100;//(int)(StdRandom.uniform()*10000); // 用于随机测试 74 //Arrays.sort(a); 75 76 int target = 29;//(int)(StdRandom.uniform()*10000); // 可以检验边界 0,99 等情况 77 int output1 = binarySearchFirst(a,target); 78 int output2 = binarySearchLast(a,target); 79 int output3 = binarySearchCount(a,target); 80 81 System.out.printf(" %d,%d,%d ",output1,output2,output3); 82 return; 83 } 84 }
● 数组随机化
1 package package01; 2 3 public class class01 4 { 5 public static void shuffle(Object[] a) 6 { 7 int n = a.length; 8 for (int i = 0; i < n; i++)// 每次在 a[0] ~ a[i] 中随机挑一个 a[r],交换 a[i] 与a[r] 9 { 10 int r = (int)(Math.random() * (i + 1)); 11 Object swap = a[r]; 12 a[r] = a[i]; 13 a[i] = swap; 14 } 15 } 16 17 public static void shuffle2(Object[] a) 18 { 19 int n = a.length; 20 for (int i = 0; i < n; i++)// 每次在 a[i] 右边随机挑一个 a[r],交换 a[i] 与a[r] 21 { 22 int r = i + (int)(Math.random() * (n - i)); 23 Object swap = a[r]; 24 a[r] = a[i]; 25 a[i] = swap; 26 } 27 } 28 29 public static void main(String[] args) 30 { 31 String[] a = { "0","1","2","3","4","5","6","7","8","9" }; 32 class01.shuffle(a); 33 for (int i = 0; i < a.length; i++) 34 System.out.print(a[i]); 35 36 System.out.print(" "); 37 38 String[] b = { "0","1","2","3","4","5","6","7","8","9" }; 39 class01.shuffle2(b); 40 for (int i = 0; i < a.length; i++) 41 System.out.print(b[i]); 42 43 return; 44 } 45 }
● 计算图连通分量的算法。输入文件第一行是节点数,后面每行是一个连接的两端节点编号,用 java class01 < inputFile.txt 来运行。
1 package package01; 2 3 import edu.princeton.cs.algs4.StdIn; 4 import edu.princeton.cs.algs4.StdOut; 5 6 public class class01 7 { 8 private int[] parent; // 节点祖先标记 9 private int[] rank; // 树深度,仅根节点有效 10 private int count; // 节点数 11 12 public class01(int n) 13 { 14 count = n; 15 parent = new int[n]; 16 rank = new int[n]; 17 for (int i = 0; i < n; i++) 18 { 19 parent[i] = i; 20 rank[i] = 1; // 源代码用的是 0 21 } 22 } 23 24 public int find(int p) // 寻找 p 的根标号 25 { 26 for (validate(p); p != parent[p]; p = parent[p]); 27 return p; 28 } 29 30 public int count() // 节点数 31 { 32 return count; 33 } 34 35 public boolean connected(int p, int q) // 判断 p 与 q 是否连通 36 { 37 return find(p) == find(q); 38 } 39 40 public void union(int p, int q) // 合并 p 和 q 41 { 42 int rootP = find(p); 43 int rootQ = find(q); 44 if (rootP == rootQ) 45 return; 46 47 if (rank[rootP] < rank[rootQ]) // 较小树连接到较大树的树根上,树高按最大值计算 48 parent[rootP] = rootQ; 49 else if (rank[rootP] > rank[rootQ]) 50 parent[rootQ] = rootP; 51 else // 两树等大,合并后树高增一 52 { 53 parent[rootQ] = rootP; 54 rank[rootP]++; 55 } 56 count--; // 合并后连接分量减少 57 } 58 59 public void union2(int p, int q) // 合并 p 和 q,第二种方法,更快 60 { 61 int rootP = find(p); 62 int rootQ = find(q); 63 if (rootP == rootQ) 64 return; 65 66 if (rank[rootP] < rank[rootQ]) 67 { 68 parent[rootP] = rootQ; 69 rank[rootQ] += rank[rootP]; // 树高按加和计算 70 } 71 else 72 { 73 parent[rootQ] = rootP; 74 rank[rootP] += rank[rootQ]; 75 } 76 count--; 77 } 78 79 private void validate(int p) // 判断输入的 p 是否合法 80 { 81 if (p < 0 || p >= parent.length) 82 throw new IllegalArgumentException(" p = " + p + "is illegal "); 83 } 84 85 public static void main(String[] args) 86 { 87 int n = StdIn.readInt(); 88 class01 uf = new class01(n); 89 for (; !StdIn.isEmpty();) 90 { 91 int p = StdIn.readInt(); 92 int q = StdIn.readInt(); 93 if (uf.connected(p, q)) 94 continue; 95 uf.union(p, q); 96 //StdOut.println(p + " " + q); 97 } 98 StdOut.println(uf.count() + " components"); 99 } 100 }