public class Percolation { public Percolation(int N) // create N-by-N grid, with all sites blocked public void open(int i, int j) // open site (row i, column j) if it is not already public boolean isOpen(int i, int j) // is site (row i, column j) open? public boolean isFull(int i, int j) // is site (row i, column j) full? public boolean percolates() // does the system percolate?
}
private final int size;
private boolean[] op; // mark whether it is open or blocked
private final int vtop;
private final int vbot;
private WeightedQuickUnionUF wquf;
private WeightedQuickUnionUF backwash; public Percolation(int N) { if (N < 1) { throw new IllegalArgumentException("N must be >= 1"); } size = N; wquf = new WeightedQuickUnionUF(size * size + 2); backwash = new WeightedQuickUnionUF(size * size + 1); // op[vtop] is virtual top and op[vbot] is virtual bottom op = new boolean[size * size + 2]; vtop = 0; vbot = size * size + 1;
}
/**
* Open the (i, j) site in N-by-N grid.
*/ public void open(int i, int j) { if (isOpen(i, j)) return; op[xyTo1D(i, j)] = true; if (isTopRow(i)) { op[vtop] = true; // make sites in top row union to virtual top wquf.union(vtop, xyTo1D(i, j)); backwash.union(vtop, xyTo1D(i, j)); } if (isBotRow(i)) { op[vbot] = true; // make sites in bottom row union to virtual bottom wquf.union(vbot, xyTo1D(i, j)); } // up if (checkValid(i - 1, j) && isOpen(i - 1, j) && !wquf.connected(xyTo1D(i - 1, j), xyTo1D(i, j))) { wquf.union(xyTo1D(i - 1, j), xyTo1D(i, j)); backwash.union(xyTo1D(i - 1, j), xyTo1D(i, j)); } // down if (checkValid(i + 1, j) && isOpen(i + 1, j) && !wquf.connected(xyTo1D(i + 1, j), xyTo1D(i, j))) { wquf.union(xyTo1D(i + 1, j), xyTo1D(i, j)); backwash.union(xyTo1D(i + 1, j), xyTo1D(i, j)); } // left if (checkValid(i, j - 1) && isOpen(i, j - 1) && !wquf.connected(xyTo1D(i, j - 1), xyTo1D(i, j))) { wquf.union(xyTo1D(i, j - 1), xyTo1D(i, j)); backwash.union(xyTo1D(i, j - 1), xyTo1D(i, j)); } // right if (checkValid(i, j + 1) && isOpen(i, j + 1) && !wquf.connected(xyTo1D(i, j + 1), xyTo1D(i, j))) { wquf.union(xyTo1D(i, j + 1), xyTo1D(i, j)); backwash.union(xyTo1D(i, j + 1), xyTo1D(i, j)); } }
/**
* return true if the (i, j) site in N-by-N grid is open.
*/ public boolean isOpen(int i, int j) { if (!checkValid(i, j)) { throw new IndexOutOfBoundsException("index i out of range"); } return op[xyTo1D(i, j)] == true; } /**
* return true if the (i, j) site in N-by-N grid is full.
* A full site is an open site that can be connected to an
* open site in the top row via a chain of neighboring
* (left, right, up, down) open sites.
*/ public boolean isFull(int i, int j) { if (!isOpen(i, j)) { return false; } return backwash.connected(vtop, xyTo1D(i, j)); } /**
* return true if this N-by-N system is percolated
*/ public boolean percolates() { return wquf.connected(vtop, vbot); }
public class PercolationStats { public PercolationStats(int N, int T) // perform T independent computational experiments on an N-by-N grid public double mean() // sample mean of percolation threshold public double stddev() // sample standard deviation of percolation threshold public double confidenceLo() // returns lower bound of the 95% confidence interval public double confidenceHi() // returns upper bound of the 95% confidence interval public static void main(String[] args) // test client, described below
}
func binarySearch(key int, arr []int) int { low := 0 high := len(arr) - 1 for low <= high { mid := (low + high) / 2 if key < arr[mid] { high = mid - 1 } else if key > arr[mid] { low = mid + 1 } else { return mid } } return -1 }
// N^2
func twoSum(arr []int) int { count := 0 for i := 0; i < len(arr); i++ { for j := i + 1; j < len(arr); j++ { if arr[i] + arr[j] == 0 { count++ } } } return count; }
这样的算法简单粗暴,但低效,如果数组足够大,这个算法所花的时间是我们无法忍受的,但至少我们可以以它作为出发点优化算法效率。回过头来想一想,该问题想问的其实是能不能“找到”两个数,它们的和为0,既然提到了“找”,那它就是一个查找问题,那何必要用两层循环去穷举呢?完全可以用二叉搜索算法去查找-arr[i],这样算法的效率就提高到了,可以解决实际问题了,代码如下所示:
// NlogN
func twoSumFast(arr []int) int { sort.Ints(arr) count := 0 for i := 0; i < len(arr); i++ { if binarySearch(-arr[i], arr) > i { count++ } } return count }
// N
func twoSumFaster(arr []int) int { sort.Ints(arr) l := 0 r := len(arr) - 1 count := 0 for l < r { if arr[l] + arr[r] == 0 { count++ l++ r-- } else if arr[l] + arr[r] > 0 { r-- } else { l++ } } return count }
// N^3
func threeSum(arr []int) int { count := 0 for i := 0; i < len(arr); i++ { for j := i + 1; j < len(arr); j++ { for k := j + 1; k < len(arr); k++ { if arr[i] + arr[j] + arr[k] == 0 { count++ } } } } return count } // N^2logN
func threeSumFast(arr []int) int { count := 0 for i := 0; i < len(arr); i++ { for j := i + 1; j < len(arr); j++ { if binarySearch( -(arr[i] + arr[j]), arr ) > j { count++ } } } return count; } // N^2
func threeSumFaster(arr []int) int { count := 0 for i := 0; i < len(arr); i++ { key := arr[i] l := i + 1 r := len(arr) - 1 for l < r { if key + arr[l] + arr[r] == 0 { count++ l++ r-- } else if key + arr[l] + arr[r] > 0 { r-- } else { l++ } } } return count }
func localMin(arr []int) int { low := 0 high := len(arr) - 1 return localMinRecur(arr, low, high) } // 2lgNfunc localMinRecur(arr []int, low, high int) int { mid := (low + high) / 2 if mid == len(arr) - 1 || mid == 0 { return -1 } if arr[mid] < arr[mid - 1] && arr[mid] < arr[mid + 1] { return mid } // search in the half with the smaller neighbor var mLeft, mRight int if arr[mid - 1] < arr[mid] { mLeft = localMinRecur(arr, low, mid - 1) } if arr[mid + 1] < arr[mid] { mRight = localMinRecur(arr, mid + 1, high) } if mLeft != -1 { return mLeft } return mRight }