算法總結—二分搜索與旋轉排序數組

一. 二分搜索(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;
    }
};
Search a 2D matrix i

 

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;
    }
};
Search a 2D matrix ii

 

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;
    }
};
Search for a 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;
        }
    }
};
Search insert position

 

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;
        }
    }
};
find bad version

 

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;
    }
};
Sqrt(x)

 

 

 

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;
    }
};
find peak element

 

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];
    }
};
Find Minimum in rotated sorted array

 

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;
    } 
};
Search in rotated sorted array

 

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;
        }
        
    }
};
Median of Two sorted arrays
相關文章
相關標籤/搜索