一. 二分搜索(Binary Search)模板及其理解數組
1.通用模板,解決start, end, mid, <(<=), >(>=)等問題ide
http://www.lintcode.com/en/problem/binary-search/spa
class Solution { public: /** * @param nums: The integer array. * @param target: Target number to find. * @return: The first position of target. Position starts from 0. */ int binarySearch(vector<int> &array, int target) { // write your code here if(array.size() == 0){ return -1; } int start = 0, end = array.size() - 1; while(start + 1 < end){ int mid = start + (end - start) / 2; if(array[mid] == target){ end = mid; }else if(array[mid] < target){ start = mid; } else{ end = mid; } } if(array[start] == target){ return start; } if(array[end] == target){ return end; } return -1; } };
注意事項: code
1). start + 1 < end (最後會剩下start, end兩項);blog
2). mid = start + (end - start) / 2; (避免出現start + end越界狀況);排序
3). A[mid] 依次比較 ==, <, >;element
4). A[start], A[end]與target比較。 (對應first / last position問題,對應的比較順序不用)leetcode
2. 對二分搜索的理解get
1) 字面意義的二分搜索,對有序數組取中間值,比較並折半刪除it
2)first / last position問題,利用模板能夠很好地解決
3)只有能判斷解在某部分集合裏,就能夠將規模縮小(不必定是簡單的在中間位置左側或右側),若每次規模近似縮小一半,仍然是二分的思路;
即將一個O(n)問題經過O(1)操做轉化成一個O(k/2)的問題。
4)當複雜度要求O(logn)時,每每能夠考慮二分搜索
二 常見問題
2.1 基礎二分問題變種
1)Search a 2D matrix i
https://leetcode.com/problems/search-a-2d-matrix/
思路:將二維矩陣看作線性序列,則爲典型二分搜索問題,因此只須要線性序列與二維數組小標的對應
即: mid / n , mid % n
class Solution { public: bool searchMatrix(vector<vector<int>>& matrix, int target) { int m = matrix.size(), n = matrix[0].size(); int start = 0, end = m*n - 1; while(start + 1 < end){ int mid = start + (end - start) / 2; if(matrix[mid/n][mid%n] == target){ return 1; } else if(matrix[mid/n][mid%n] < target){ start = mid; } else{ end = mid; } } if (matrix[start/n][start%n] == target) { return 1; } if (matrix[end/n][end%n] == target) { return 1; } return 0; } };
2)Search a 2D matrix ii
https://leetcode.com/problems/search-a-2d-matrix-ii/
思路:根據矩陣的性質,從左下角向右上尋找。
若是target大於當前元素,當前列上方元素可刪除,因此向右走一步;
若是target小於當前元素,當前行右方元素可刪除,因此向上走一步。
class Solution { public: bool searchMatrix(vector<vector<int>>& matrix, int target) { int m = matrix.size(); int n = matrix[0].size(); int i = m - 1, j = 0; while(i >= 0 && j < n){ if(matrix[i][j] == target){ return 1; } else if(matrix[i][j] < target){ j++; } else{ i--; } } return 0; } };
2.2 first/last position 問題
1)Search for a range
https://leetcode.com/problems/search-for-a-range/
思路:使用兩遍模板,分別找到first 和 last position,則找到對應區間
class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { if(nums.size() == 0){ return vector<int>{0,0}; } int start = 0, end = nums.size() - 1; vector<int> range; // search for left bound while(start + 1 < end){ int mid = start + (end - start) / 2; if(nums[mid] == target){ end = mid; } else if(nums[mid] < target){ start = mid; } else{ end = mid; } } if(nums[start] == target){ range.push_back(start); } else if(nums[end] == target){ range.push_back(end); } else{ range.push_back(-1); } start = 0; end = nums.size() - 1; // search for right bound while(start + 1 < end){ int mid = start + (end - start) / 2; if(nums[mid] == target){ start = mid; } else if(nums[mid] < target){ start = mid; } else{ end = mid; } } if(nums[end] == target){ range.push_back(end); } else if(nums[start] == target){ range.push_back(start); } else{ range.push_back(-1); } return range; } };
2) Search insert position
https://leetcode.com/problems/search-insert-position/
思路:在數組中找到第一個大於等於target的位置(find first position)
class Solution { public: int searchInsert(vector<int>& nums, int target) { if(nums.size() == 0){ return 0; } int start = 0,end = nums.size() - 1; while(start + 1 < end){ int mid = start + (end - start) / 2; if(nums[mid] == target){ end = mid; } else if(nums[mid] < target){ start = mid; } else{ end = mid; } } if(nums[start] >= target){ return start; } else if(nums[end] >= target){ return end; } else{ return end + 1; } } };
3) Find bad version
https://leetcode.com/problems/first-bad-version/
思路:找到第一個bad version(first position)
// Forward declaration of isBadVersion API. bool isBadVersion(int version); class Solution { public: int firstBadVersion(int n) { if(n == 0){ return 0; } int start = 1, end = n; while(start + 1 < end){ int mid = start + (end - start) / 2; if(isBadVersion(mid)){ end = mid; } else { start = mid; } } if(isBadVersion(start)){ return start; } if(isBadVersion(end)){ return end; } } };
4) Sqrt(x)
https://leetcode.com/problems/sqrtx/
思路:找到最後一個平方小於等於x的數
class Solution { public: int mySqrt(int x) { long long start = 0, end = x; while(start + 1 < end){ long long mid = start + (end - start) / 2; if(mid*mid == x){ return mid; } else if(mid * mid < x){ start = mid; } else{ end = mid; } } if(end * end <= x){ return end; } return start; } };
2.3 較複雜二分搜索問題(包含旋轉排序數組內搜索)
這幾個問題仍然採用二分思想,但二分後刪除部分集合的調節並不明顯,每每須要經過畫圖幫助理解。
同時此類問題不少也能夠轉換成增長條件的first position / last position問題,從而使用二分模板。
1) Find peak element
https://leetcode.com/problems/find-peak-element/
思路:由於題目假設了num[-1] = num[n] = - 因此對於nums[mid]來說,其與左右元素之間無非只有三種狀況:
(1) nums[mid-1] < nums[mid] && nums[mid] > nums[mid+1] ,此時mid即爲peak element;
(2) nums[mid-1] < nums[mid] && nums[mid] < nums[mid+1],此時刪除數組左半部分仍然可保證有peak element, mid = start;
(3)nums[mid-1] > nums[mid] && nums[mid] > nums[mid+1], 此時刪除數組左半部分仍然可保證有peak element,mid = end;
(4)在波谷位置,隨意左右都可。
class Solution { public: int findPeakElement(vector<int>& nums) { int start = 0, end = nums.size() - 1; while(start + 1 < end ){ int mid = start + (end - start) / 2; if(nums[mid - 1] < nums[mid] && nums[mid] > nums[mid+1]){ return mid; } else if(nums[mid - 1] < nums[mid] && nums[mid] < nums[mid + 1]){ start = mid; } else{ end = mid; } } if(nums[start] > nums[end]){ return start; } return end; } };
2) Find Minimum in Rotated Sorted Array
https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/
思路: 根據旋轉排序數組性質,問題轉化爲find first element which is bigger than nums[nums.size() - 1];
class Solution { public: int findMin(vector<int>& nums) { int start = 0, end = nums.size() - 1; int target = nums[nums.size() - 1]; while(start + 1 < end){ int mid = start + (end - start) / 2; if(nums[mid] > target){ start = mid; } else{ end = mid; } } if(nums[start] < target){ return nums[start]; } return nums[end]; } };
3) Search in Rotated Sorted Array
https://leetcode.com/problems/search-in-rotated-sorted-array/
思路:分狀況進行「二分」的刪除區間操做
首先區分target與nums[nums.size() - 1]的關係
其次在不一樣區間內,根據target與nums[mid]的關係進行相關刪除區間操做。
class Solution { public: int search(vector<int>& nums, int target) { if(nums.size() == 0){ return -1; } int start = 0, end = nums.size() - 1; int temp = nums[end]; while(start + 1 < end){ int mid = start + (end - start) / 2; if(nums[mid] == target){ return mid; } if(temp == target){ return nums.size() - 1; } if(temp < target){ if(nums[mid] < target && nums[mid] > temp){ start = mid; } else{ end = mid; } } else{ if(nums[mid] > target && nums[mid] < temp){ end = mid; } else{ start = mid; } } } if(nums[start] == target){ return start; } if(nums[end] == target){ return end; } return -1; } };
4) Median of Two Sorted Arrays
https://leetcode.com/problems/median-of-two-sorted-arrays/
思路:取兩個數組中長度較小的那個,利用歸併排序的思想,咱們從a和b中一共拿k個數看下假設 length(a) < length(b)
從a中取pa = min(k / 2, length(a))個元素,從b中取pb = k – pa個元素;
若是a[pa - 1] < b [pb - 1], 說明a取少了,第k個在a後半部分和b前半部分,反之同理。
class Solution { public: int findk(vector<int>& nums1, vector<int>& nums2, int k){ if(nums1.size() > nums2.size()){ return findk(nums2, nums1, k); } if(nums1.size() == 0){ return nums2[k-1]; } if(k == 1){ return min(nums1[0], nums2[0]); } int len = nums1.size(); int p1 = min(k/2, len); int p2 = k - p1; vector<int> nums11(nums1.begin() + p1, nums1.end()); vector<int> nums21(nums2.begin(), nums2.begin() + p2); vector<int> nums12(nums1.begin(), nums1.begin() + p1); vector<int> nums22(nums2.begin() + p2, nums2.end()); return (nums1[p1 - 1] < nums2[p2 - 1]) ? findk( nums11, nums21 , k - p1) : findk( nums12, nums22, k - p2); } double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { int m = nums1.size(), n = nums2.size(); if( (m + n) % 2 == 1){ return findk(nums1, nums2, (m + n + 1) / 2); } else{ return (findk(nums1, nums2, (m + n) / 2) + findk(nums1, nums2, (m + n) / 2 + 1) ) / 2.0; } } };