二分查詢-不套用模板

國科大小渣渣發表於2020-10-16

二分查詢-不套用模板

這裡參考自大佬 https://leetcode-cn.com/problems/search-insert-position/solution/te-bie-hao-yong-de-er-fen-cha-fa-fa-mo-ban-python-/

體會:

  • 使用減治思想,在迴圈體內排除元素。同樣如果寫成<=的形式表示從迴圈體內找到元素。
while(first<last)
  • 最後退出的時候一定是left和right相等的時候,所以無所謂返回誰。
return left;
  • 寫成下面這樣而不寫成(left+right)/2主要是避免兩者相加會溢位。
  • mid的求法屬於向下取整,即left+1=right的時候mid=left+0,如果下面更新的時候存在left=mid這樣的賦值會形成死迴圈(應該通過調整left和right的if-else的大於小於條件更新避免出現left=mid這種形式更新)。
mid=left+(right-left)/2;
left=mid;
  • 如何縮減?如下列註釋
//這裡mid下標對應的元素值小於target,故mid應該排除出去。
if(nums[mid]<target)//[mid+1,right]
	left=mid+1;
else//與上面對稱,是[left,mid]
	right=mid;

例項程式碼
形式可能千變萬化,根據需要進行調整。

  • 陣列可能升序可能降序,但是可以通過函式預處理
  • 寫【left,right】兩邊都閉區間的形式,保障都能取到
  • while寫成小於的形式,保障退出的時候left=right
  • leftright的值更新是互補的形式,避免寫left=mid造成死迴圈
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        //求非降序範圍[first, last]內第一個不小於value的值的位置
        int len=size(nums);
        int left=0;
        int right=len-1;
        int mid;
        while(left<right)//搜尋區間[first, last)不為空
        {
            mid=left+(right-left)/2;//防溢位
            if(nums[mid]<target)
                left=mid+1;
            else
                right=mid;
        }
        return left;//last也行,因為[first, last)為空的時候它們重合
    }
};

題型一:在陣列中查詢符合條件的元素的下標

704. 二分查詢

非模板做法:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0,right=nums.size()-1;
        int mid;
        while(left<right)
        {
            mid=left+(right-left)/2;
            if(nums[mid]<target)
            {
                left=mid+1;
            }
            else
            {
                right=mid;
            }
        }
        if(nums[left]==target)
            return left;
        else
            return -1;
    }
};

模板做法:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0,right=nums.size()-1;
        int mid;
        while(left<=right)
        {
            mid=left+(right-left)/2;
            if(nums[mid]>target)
            {
                right=mid-1;
            }
            else if(nums[mid]<target)
            {
                left=mid+1;
            }
            else
                return mid;
        }
        return -1;
    }
};

34. 在排序陣列中查詢元素的第一個和最後一個位置
總結

非模板做法:

在這裡插入程式碼片

模板做法:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> temp;
        int left=0,right=nums.size()-1;
        int mid;
        int start=0,end=0;
        //找左邊界
        while(left<=right)
        {
            mid=left+(right-left)/2;
            if(nums[mid]>target)
                right=mid-1;
            else if(nums[mid]<target)
                left=mid+1;
            else if(nums[mid]==target)
                right=mid-1;
        }
        if(left>=nums.size() || nums[left]!=target)
            left=-1;
        start=left;
        temp.push_back(left);
        //找右邊界
        left=0;
        right=nums.size()-1;
        while(left<=right)
        {
            mid=left+(right-left)/2;
            if(nums[mid]>target)
                right=mid-1;
            else if(nums[mid]<target)
                left=mid+1;
            else if(nums[mid]==target)
                left=mid+1;
        }
        if(right<0 || nums[right]!=target)
            right=-1;
        end=right;
        temp.push_back(right);

        return temp;
    }
};

1095. 山脈陣列中查詢目標值

總結:

  • 基本思路:二分法先獲取山頂元素的下標,分成升序和降序兩部分,利用二分法尋找目標元素的下標
  • 首先是山頂元素的獲取,沒有直接利用三者關係,而是利用兩者的關係從兩邊不斷地向中間靠攏縮減,減少了訪問次數。得到山頂元素可以先判斷一下是不是目標元素,減少運算量
/**
 * // This is the MountainArray's API interface.
 * // You should not implement it, or speculate about its implementation
 * class MountainArray {
 *   public:
 *     int get(int index);
 *     int length();
 * };
 */

class Solution {

public:
    int findMountainTop(MountainArray &mountainArr,int target,int left,int right) 
    {
        int mid;
        //直接使用兩個不等式約束,條件過於緊
        while(left<right)
        {
            mid=left+(right-left)/2;
            if(mountainArr.get(mid)<mountainArr.get(mid+1))
            {
                left=mid+1;//左側削減
            }
            else
            {
                right=mid;//右側削減
            }
        }
        return left;//lef==right
    }

    int findSortedArray(MountainArray &mountainArr,int target,int left,int right)
    {
        int mid;
        while(left<right)//去排除一定不存在元素的空間
        {
            mid=left+(right-left)/2;
            if(mountainArr.get(mid)<target)
            {
                //[mid+1,right]
                left=mid+1;
            }
            else
            {
                //[left,mid]
                right=mid;
            }
        }
        return left;
    }

    int findReverseArray(MountainArray &mountainArr,int target,int left,int right)
    {
        //注意:可以在大於小於上做文章避免出現left=mid這種向下取整導致的死迴圈問題
        int mid;
        while(left<right)//去排除一定不存在元素的空間
        {
            //向下取整。mid=left+0,所以應當避免下面出現left=mid的賦值導致死迴圈
            mid=left+(right-left)/2;
            if(mountainArr.get(mid)>target)
            {
                //[mid+1,right]
                left=mid+1;
            }
            else
            {
                //[left,mid]
                right=mid;
            }
        }
        return left;
    }

    int findInMountainArray(int target, MountainArray &mountainArr) {
        int index=-1;
        //獲取陣列長度
        int len=mountainArr.length();
        //尋找最大值下標
        int peakIndex=findMountainTop(mountainArr,target,0,len-1);
        //判斷最大值下標位置是不是目標值
        if(mountainArr.get(peakIndex)==target)
            return peakIndex;
        //升序陣列部分尋找target
        index=findSortedArray(mountainArr,target,0,peakIndex-1);
        if(mountainArr.get(index)==target)
            return index;
        //降序陣列部分尋找target
        index=findReverseArray(mountainArr,target,peakIndex+1,len-1);
        if(mountainArr.get(index)==target)
            return index;
        else
            return -1;
    }
};

相關文章