簡單模板代碼:算法
public static int binarySearch(int[] array, int target) { if (array == null || array.length == 0) { return -1; } int start = 0; int end = array.length - 1; int middle; // 相鄰 start = end - 1 或者 start = end while (start + 1 < end) { // (1) middle = start + (end - start) / 2; // (2) if (array[middle] == target) { return middle; } else if (array[middle] < target) { //target在右邊 start = middle; } else { end = middle; } } if (array[start] == target) { // (3) return start; } else if (array[end] == target) { // (4) return end; } else { return -1; } }
(1) (start+end)/2 在start+end的值較大的時候有可能會超出範圍, 所以這裏使用start+(end-start)/2數組
(2) 爲何使用start+1<end? 爲了防止死循環:測試
循環終止條件爲:start+1>=end, 即相鄰或者相交的狀況spa
(3) (4) : 在相鄰或者相交的狀況下都終止, 再判斷能夠簡單防止死循環code
問題描述: http://www.lintcode.com/en/problem/search-for-a-range/blog
即給定一個有序的數組和target value, 尋找target value的起始和結束位置, 不存在就返回(-1,-1);get
思路: 二分搜索的擴展.it
(1) 分二次查找io
(2) 結束條件都是相鄰或者相交模板
(3) 找左邊界就是start->end
(4) 找右邊界就是end->start
public class BinarySearch { public static void main(String[] args) { int[] array = new int[]{1, 2, 3, 4, 4 ,4, 4, 5}; System.out.println(Arrays.toString(binarySearch(array, 4))); } public static int[] binarySearch(int[] array, int target) { if (array == null || array.length == 0) { return new int[]{-1, -1}; } int start = 0; int end = array.length - 1; // left是左邊的, right是右邊的 int middle, left, right; // 相鄰 start = end - 1 或者 start = end while (start + 1 < end) { middle = start + (end - start) / 2; if (array[middle] == target) { end = middle; } else if (array[middle] < target) { //target在右邊 start = middle; } else { end = middle; } } if (array[start] == target) { left = start; } else if (array[end] == target) { left = end; } else { return new int[]{-1, -1}; } start = 0; end = array.length - 1; middle = 0; while (start + 1 < end) { middle = start + (end - start) / 2; if (array[middle] == target) { start = middle; } else if (array[middle] < target) { //target在右邊 start = middle; } else { end = middle; } } if (array[end] == target) { right = end; } else if (array[start] == target) { right = start; } else { return new int[]{-1, -1}; } return new int[]{left, right}; } }
問題描述:
http://www.lintcode.com/en/problem/search-insert-position/
(1) 給一個有序的數組
(2) 尋找插入位置
二分查找的精髓: 每次去掉一半, 最後剩下的狀況就是start=end或者start+1=end, 而不管哪一種均可以當作start+1=end考慮;
public static int searchInsert(int[] A, int target) { if (A == null || A.length == 0) { return 0; } int start = 0, end = A.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (A[mid] == target) { return mid; } else if (A[mid] < target) { start = mid; } else { end = mid; } } if (A[start] >= target) { return start; } else if (A[end] >= target) { return end; } else { return end + 1; } }
問題描述:
http://www.lintcode.com/en/problem/search-in-rotated-sorted-array/
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7
might become 4 5 6 7 0 1 2
).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
循環數組的二分查詢,畫圖2個象限,簡單的分類討論感受用代碼實現也很麻煩.
這裏說一下思路:
(1) 首先判斷mid在第一部分和第二部分
(2) 將問題細化, 這裏只考慮第一部分:
若是A[start] < A[end], 則是通常狀況
不然 start, mid ,end將數組分爲了3個區間, A[mid]<=target<=A[end]則說明target在mid和end中間, 不然是start,mid中.
(3) 第二部分相似
(4) 問題解決
public static int search(int[] A, int target) { if (A == null || A.length == 0) { return -1; } int start = 0, end = A.length - 1; int mid; while (start + 1 < end) { mid = start + (end - start) / 2; if (A[mid] == target) return mid; // 分爲2大類: 在哪邊? if (A[mid] > A[start]) { if (A[end] >= A[start]) { //.. if (A[mid] > target) { end = mid; } else { start = mid; } } else { if (target >= A[start] && target <= A[mid]) { end = mid; } else { start = mid; } } } else { if (A[start] <= A[end]) { if (A[mid] > target) { end = mid; } else { start = mid; } } else { if (target >= A[mid] && target <=A[end]){ start = mid; }else { end = mid; } } } } if (A[start] == target) { return start; } else if (A[end] == target) { return end; } else { return -1; } }
問題描述, 很是簡單, 一個矩陣m*n 能夠用數組表示:
(1) 沒一行都有序
(2) 第n+1行比第n行的都大
例如:
[ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ]
很是簡單, 當作是一個數組就能夠..
public boolean searchMatrix(int[][] matrix, int target) { if (matrix == null || matrix.length == 0) { return false; } if (matrix[0] == null || matrix[0].length == 0) { return false; } int row = matrix.length, column = matrix[0].length; int start = 0, end = row * column - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; int number = matrix[mid / column][mid % column]; if (number == target) { return true; } else if (number < target) { start = mid; } else { end = mid; } } if (matrix[start / column][start % column] == target) { return true; } else if (matrix[end / column][end % column] == target) { return true; } return false; }
下面考慮更爲複雜的狀況:
(1) 每一行都是有序的
(2) 每一列都是有序的要怎麼作?
這裏說明一下思路:
從左下方開始判斷, 若是target < matirx[i][j], 說明第i列的數都沒用, i++
若是target > matirx[i][j], 說明第j列的數都沒有用, j++.
合併有序數組很是簡單, 可是假設這樣的狀況: A中有足夠的位置去合併, 不能建立新的數組要怎麼辦 ?
A = [1, 2, 3, empty, empty]
, B = [4, 5]
After merge, A will be filled as [1, 2, 3, 4, 5]
思路很是簡單, 看empty在哪邊? 這裏從後面開始擴展就能夠~
代碼以下: 這裏也是建立了新數組C, 而後把A拷貝進去模擬A中有足夠的位置:
public static void main(String[] args) { merge( new int[]{1, 3, 4}, new int[]{2, 3, 5, 8, 9} ); } public static void merge(int[] A, int B[]) { int[] C = new int[A.length + B.length]; System.arraycopy(A, 0, C, 0, A.length); int a = A.length - 1; int b = B.length - 1; int i = a + b + 1; while (i >= 0 && a >= 0 && b >= 0) { if (A[a] > B[b]) { C[i] = A[a]; a--; } else { C[i] = B[b]; b--; } i--; } if (b < 0) { //a 剩下 System.arraycopy(A, 0, C, 0, a + 1); } else if (a < 0) { System.arraycopy(B, 0, C, 0, b + 1); } System.out.println(Arrays.toString(C)); }
(1) 2個有序數組
(2) 尋找2個數組加起來第k個
(3) 要求是算法複雜度是log(m+n)
思路是什麼:要log的時間複雜度,每次必須拋棄一半:
只要每次比較 A[k/2]和B[k/2]就能夠, 若是A[k/2]<B[k/2]就能夠拋棄A中的k/2個元素...
代碼以下(沒有通過充分測試):
public static void main(String[] args) { int[] A = {1, 2, 3, 4, 5, 6}; int[] B = {2, 3, 4, 5}; System.out.println(findMedianSortedArrays(A, B)); } public static double findMedianSortedArrays(int A[], int B[]) { int len = A.length + B.length; if (len % 2 == 1) { return findKth(A, 0, B, 0, len / 2 + 1); } return ( findKth(A, 0, B, 0, len / 2) + findKth(A, 0, B, 0, len / 2 + 1) ) / 2.0; } /** * @param A 數組A * @param A_start 數組A起始位置 * @param B 數組B * @param B_start 數組B起始位置 * @param k 尋找第K個數 * @return */ public static int findKth(int[] A, int A_start, int[] B, int B_start, int k) { if (A_start > A.length - 1) { return B[B_start + k - 1]; } if (B_start > B.length - 1) { return A[A_start + k - 1]; } // if (k == 1) { return Math.min(A[A_start], B[B_start]); } // 要考慮k/2超出邊界的狀況 int a_new_start = A_start + k / 2 - 1; int b_new_start = B_start + k / 2 - 1; int a_length, b_length; if (a_new_start > A.length - 1) { a_new_start = A.length - 1; } a_length = a_new_start - A_start + 1; if (b_new_start > B.length - 1) { b_new_start = B.length - 1; } b_length = b_new_start - B_start + 1; if (A[a_new_start] < B[b_new_start]) { //A這邊小一點, 全面的所有被拋棄 return findKth(A, a_new_start + 1, B, B_start, k - a_length); } else { return findKth(A, A_start, B, b_new_start + 1, k - b_length); } }