1. 程式人生 > 其它 >一起刷演算法 # 二分查詢 # No.1

一起刷演算法 # 二分查詢 # No.1

題目

153. 尋找旋轉排序陣列中的最小值 - 力扣(LeetCode) (leetcode-cn.com)

用時

39分鐘

結果

優化過程

第一次提交未通過,發現問題是因為對於 3 1 2這種,最小值剛好是mid的情況,無法正確尋找,因為它會跳過mid值。

這個時候我還沒意識到,這是因為這道題不是標準二分法的原因,而我使用了一般二分的一般形式。

class Solution {
    public int findMin(int[] nums) {
        int low = 0, hi = nums.length - 1;
        if(nums[low] < nums[hi])return nums[low];
        if(nums.length == 2)return nums[0] > nums[1]? nums[1]:nums[0];
        int mid = -1;
        while(low <= hi){
            mid = low + (hi - low) / 2;
            System.out.println(mid);
            if(nums[mid] > nums[0]){
                low = mid + 1;
            }
            else{
                hi = mid - 1;
            }
        }
        return nums[mid];
    }
}

第二次提交,這個時候我加入了一個判斷條件

if(nums[mid] < nums[low] && nums[mid] < nums[hi])return nums[mid];

來處理這種mid是最小值的情況,但還是會有新的問題,這個時候我嘗試繼續加限制條件,但感覺不太對,這演算法看起來就太複雜了。於是開始思考為什麼不行。

class Solution {
    public int findMin(int[] nums) {
        int low = 0, hi = nums.length - 1;
        if(nums[low] < nums[hi])return nums[low];
        if(nums.length == 2)return nums[0] > nums[1]? nums[1]:nums[0];
        int mid = -1;
        int temp = -1;
        while(low <= hi){
            mid = low + (hi - low) / 2;
            //System.out.print(mid);
            //System.out.print(low);
            //System.out.println(hi);
            if(nums[mid] < nums[low] && nums[mid] < nums[hi])return nums[mid];
            if(nums[low] < nums[hi])return nums[low];
            if(nums[mid] > nums[low]){

                low = mid + 1;
            }
            else{
                hi = mid - 1;
            }
            temp = mid;
        }
        return nums[mid];
    }
}

 

我發現,所有這些問題都是因為mid這個值在遍歷時被漏掉了!

簡單好理解的二分法! 要找這種序列的最小值,只需要思考,最小值到底在哪裡?最小值一定在無序部分中。 這些序列大概可以分為兩種情況: 1.前半部分有序:5678/912 2.後半部分有序:7812/345 我們發現,最小值一定在無序部分,那麼我們只需要不斷尋找無序部分就行了
程式碼如下,這裡有一些問題: 1.如何尋找無序部分? 通過if(nums[mid]>=nums[low])這一句比較來尋找,即中間值和每次二分出來的部分的第一個值比較,這一點比較好理解. 還需要加上這一句if(nums[low]<=nums[hi])returnnums[low];表示剩下的序列全都是有序了,沒法再找無序部分,這個時候直接返回第一個值即是最小值。 2.為什麼hi=mid而不是mid-1 在一般二分中,我們可以寫mid-1和mid+1是因為我們有一句if(nums[mid]==target)break;**即每次我們都check到了mid位置的值,所以我們可以通過位置移動來排除這個值,但這個演算法裡是沒有這句break語句check到mid值的,所以不能-1 於是最終版為:
class Solution {
    public int findMin(int[] nums) {
        int low = 0, hi = nums.length - 1;
        if(nums[low] <= nums[hi])return nums[low];
        int mid = -1;
        while(low <= hi){
            mid = low + (hi - low) / 2;
            if(nums[low] <= nums[hi])return nums[low];
            if(nums[mid] >= nums[low]){
                low = mid + 1;
            }
            else{
                hi = mid;  //!!!!!!!!!!!這裡不能減去1
            }
        }
        return nums[mid];
    }
}