二分查詢—包括查詢第一個目標元素和最後一個目標元素

小米n416發表於2020-11-01

學了沒過多久又忘了,在這裡寫一下筆記。此處主要學習參考了labuladong寫的二分查詢筆記,我加上了我自己的理解。

二分查詢需要明確:

  • 查詢條件:找到中間的元素,還是找到多個目標元素第一個(最左邊)的元素,還是多個目標元素的最後一個
  • 查詢區間:左閉右閉while(left<=right),左閉右開while(left<right)
    需要判斷什麼時候區間中沒有元素,來作為判別條件。在while(left<=right)時,[left+1,left]區間中沒有元素,退出迴圈。
    while(left<right)時,[left,left)中沒有元素,退出迴圈。
    但其實左閉右開可以轉換成左閉右閉。
  • 什麼時候返回查詢到的目標元素的下標:
    • 找到中間元素:找到立即返回
    • 找邊界目標元素:找到目標,不斷縮小區間返回。
      • 找左邊界:if(nums[mid] == target) right=mid;
      • 找右邊界:if(nums[mid] == target) left=mid+1;
  • 邊界元素需要在迴圈外面打補丁:
    • 找左邊界:if(left==len) return 0;目標元素比陣列所有元素都大,返回-1
    • 找右邊界:if(left==0) return -1;目標元素比陣列所有元素都小,返回-1
  • 邊界目標元素的返回:
    • 左邊界:當區間縮小到只有兩個元素,且這兩個元素相同的時候,while條件會漏掉,返回打補丁return nums[left] == target ? left : -1;
      在這裡插入圖片描述
    • 右邊界:因為找到目標元素,左邊界向右縮排,left=mid+1。當while結束時,nums[left]一定不等於target,即相當於left來到了目標元素的後面一個位置,但是nums[left-1]有可能目標元素的右邊界,返回打補丁return nums[left-1] == target ? (left-1) : -1;

一般查詢元素

int search(vector<int>& nums, int target) {
        int len=nums.size();
        if(len == 0) return -1;
        int left=0,right=nums[len-1];//閉區間
        while(left<=right){//閉區間中不含元素的判定[left+1,left]
            int mid=left+(right-left)/2;
            if(nums[mid] == target){
                return mid;//找到元素立即返回,找左右邊界元素不是找到立即返回,而是繼續縮小區間
            }
            else if(nums[mid] > target){
                right=mid-1;//閉區間,從mid分割開
            }
            else if(nums[mid] < target){
                left=mid+1;
            }
        }
        return -1;//沒有找到元素
    }

下面的兩個程式碼好像我寫的有問題,部分可以成功返回下標,部分返回-1

找第一個目標元素

int left_bound(vector<int>& nums, int target){
	int len=nums.size();
	if (len == 0) return -1;
	int left=0,right=len;
	while(left < right){//因為右邊界取不到索引 
		int mid=left+(right-left)/2;
		if(nums[mid]==target){
			right=mid;//縮小邊界,不斷向左邊界靠近
		}
		else if(nums[mid]>target){
			right=mid;
		}
		else if(nums[mid]<target){
			left=mid+1;
		}
		//目標元素比陣列中的所有元素都大 
		if(left==len) return -1;
		//反之區間只有兩個元素時,且剛好是兩個相同的元素 
		return nums[left]==target ? left : -1;
	}
}

int main(){
	vector<int> nums;
	nums.push_back(0);nums.push_back(1);nums.push_back(1);nums.push_back(2);nums.push_back(3);	
	int ans=left_bound(nums,4);
	cout<<ans<<endl;
	return 0;
} 

找最後一個目標元素

int right_bound(vector<int>& nums,int target){
	int len=nums.size();
	if l(en == 0) return -1;
	int left=0,right=len;
	while(left < right){//因為右邊界取不到索引 
		int mid=left+(right-left)/2;
		if(nums[mid]==target){
			//不斷向右邊界靠近
			left=mid+1; 
		}
		else if(nums[mid]>target){
			right=mid;
		}
		else if(nums[mid]<target){
			left=mid+1;
		}
		//沒找到右邊界
		if(left==0) return -1;
		//因為找到目標,左邊界向有縮排,left=mid+1。當while結束時,nums[left]一定不等於target,而nums[left-1]有可能是 
		return nums[left -1] == target ? (left-1) : -1;
	}

int main(){
vector<int> nums;
	nums.push_back(0);nums.push_back(1);nums.push_back(1);nums.push_back(2);nums.push_back(3);	
	int ans=right_bound(nums,1);
	cout<<ans<<endl;
}

相關文章