二分法查找數的範圍

數的範圍

題目描述

給定一個按照升序排列的長度爲n的整數數組,以及 q 個查詢。

對於每個查詢,返回一個元素k的起始位置和終止位置(位置從0開始計數)。

如果數組中不存在該元素,則返回“-1 -1”。

輸入格式

第一行包含整數n和q,表示數組長度和詢問個數。

第二行包含n個整數(均在1~10000範圍內),表示完整數組。

接下來q行,每行包含一個整數k,表示一個詢問元素。

輸出格式

共q行,每行包含兩個整數,表示所求元素的起始位置和終止位置。

如果數組中不存在該元素,則返回“-1 -1”。

數據範圍

1n1000001≤n≤100000
1q100001≤q≤10000
1k100001≤k≤10000

輸入樣例:

6 3
1 2 2 3 3 4
3
4
5

輸出樣例:

3 4
5 5
-1 -1

題目分析

本題是查找數所在的位置,而與一般的不同,對所查找的隊列中存在重複的情況,因此返回的則是一個範圍,而不是單獨一個下標位置。

因此針對本題可以採用二分法,判斷邊界

  1. 確定分界點check
    • 若判斷左邊時,即q[mid] >= x時,mid = (l + r) / 2
    • 若判斷右邊時,即q[mid] <= x時,mid = (l + r +1) /2。因爲當判斷區間僅有兩個元素時,會進入死循環
  2. 修改l、r左右邊界
    1. q[mid] >= x,則r = mid;否則l = mid + 1;
    2. q[mid] <= x,則l = mid;否則r = mid - 1;
  3. 判斷無解

代碼實現

#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int n, m;//定義數組個數,以及查詢次數
int q[N];

int main(){
    scanf("%d %d", &n, &m);
    
    for(int i = 0; i < n; i++)  scanf("%d", &q[i]);
    while(m--)
    {
        // 輸入查詢整數
        int x;
        scanf("%d", &x);
        // 確定邊界,先找左邊界
        int l = 0, r = n - 1;
        while(l < r)
        {
            int mid = l + r >> 1;//取 mid
            // 數在左邊,調整右邊界
            if(q[mid] >= x)
                r = mid;
            // 數在右邊,調整左邊界
            else
                l = mid + 1;    
        }
        // 判斷無解
        if(q[l] != x)   printf("%d %d\n", -1, -1);
        else
        {
            // 打印左邊界
            printf("%d ", l);
            // 找右邊界
            int l = 0, r = n - 1;
            while(l < r)
            {
                int mid = l + r + 1 >> 1;
                if(q[mid] <= x) l = mid;
                else r = mid - 1;
            }
            // 打印右邊界
            printf("%d\n", r);
        }
    }
    return 0;
}

運行結果

輸入

8 4
1 2 2 3 3 4 6 8
3
4
5
6

輸出

3 4
5 5
-1 -1
6 6

總結

本題採用整數二分法。通過二分找邊界,注意越界問題與邊界的變動。

注意:有單調性的題目一定可以二分,可以二分的不一定有單調性

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章