數(shù)據(jù)結(jié)構(gòu)也不難:二分查找模版與例題

原文鏈接: 點(diǎn)這里
更多內(nèi)容就在我的個(gè)人博客 BlackBlog.tech 歡迎關(guān)注!
謝謝大家!

本文源自LeetCode二分查找章節(jié),通過本章節(jié)的練習(xí)對(duì)二分查找的基礎(chǔ)知識(shí)進(jìn)行掌握,同時(shí)能夠利用二分查找解決部分問題。二分查找是一個(gè)效率非常高的算法,它充分利用了元素間的次序關(guān)系,采用分治策略,可在最壞的情況下用O(log n)完成搜索任務(wù)。掌握二分查找對(duì)于提升代碼效率很有幫助。
LeetCode二分查找地址:https://leetcode-cn.com/explore/learn/card/binary-search/208/background/832/
本篇文章將介紹3個(gè)二分模版,同時(shí)完成十個(gè)例題。在閱讀過程中,如果對(duì)三個(gè)模板的區(qū)別難以理解,請(qǐng)務(wù)必完全閱讀完,這是一個(gè)需要逐步思考的過程。
給個(gè)目錄:
LeetCode704 二分查找
LeetCode69 x 的平方根
LeetCode374 猜數(shù)字大小
LeetCode33 搜索旋轉(zhuǎn)排序數(shù)組
LeetCode278 第一個(gè)錯(cuò)誤的版本
LeetCode75 尋找峰值
LeetCode159 尋找旋轉(zhuǎn)排序數(shù)組中的最小值
LeetCode34 在排序數(shù)組中查找元素的第一個(gè)和最后一個(gè)位置
LeetCode658 找到 K 個(gè)最接近的元素

二分查找基礎(chǔ)知識(shí)

什么是二分查找
二分查找是計(jì)算機(jī)科學(xué)中最基本、最有用的算法之一。 它描述了在有序集合中搜索特定值的過程。

二分查找中使用的術(shù)語:

目標(biāo) Target —— 你要查找的值
索引 Index —— 你要查找的當(dāng)前位置
左、右指示符 Left,Right —— 我們用來維持查找空間的指標(biāo)
中間指示符 Mid —— 我們用來應(yīng)用條件來確定我們應(yīng)該向左查找還是向右查找的索引

二分查找原理

二分查找是一種在每次比較之后將查找空間一分為二的算法。每次需要查找集合中的索引或元素時(shí),都應(yīng)該考慮二分查找。如果集合是無序的,我們可以總是在應(yīng)用二分查找之前先對(duì)其進(jìn)行排序。

二分查找一般由三個(gè)主要部分組成:

預(yù)處理 —— 如果集合未排序,則進(jìn)行排序。
二分查找 —— 使用循環(huán)或遞歸在每次比較后將查找空間劃分為兩半。
后處理 —— 在剩余空間中確定可行的候選者。

二分查找是一個(gè)效率非常高的算法,它充分利用了元素間的次序關(guān)系,采用分治策略,可在最壞的情況下用O(log n)完成搜索任務(wù)。掌握二分查找對(duì)于提升代碼效率很有幫助。

復(fù)雜度
二分查找的時(shí)間復(fù)雜度:O(log n),因?yàn)槎植檎沂峭ㄟ^對(duì)查找空間中間的值應(yīng)用一個(gè)條件來操作的,并因此將查找空間折半,在更糟糕的情況下,我們將不得不進(jìn)行 O(log n) 次比較,其中 n 是集合中元素的數(shù)目。
二分查找的空間復(fù)雜度:O(1),不需要任何其他額外空間。

LeetCode704 二分查找

我們先來一道最為基礎(chǔ)的二分查找題目,理解一下二分查找。
本題為LeetCode704 二分查找,屬于二分查找中最為基礎(chǔ)的練習(xí)。二分查找,顧名思義,就是將數(shù)組一分為二,在左右兩邊查找,確定元素區(qū)間之后再次一分為二,直至確定元素。

題目

給定一個(gè) n 個(gè)元素有序的(升序)整型數(shù)組 nums 和一個(gè)目標(biāo)值 target ,寫一個(gè)函數(shù)搜索 nums 中的 target,如果目標(biāo)值存在返回下標(biāo),否則返回 -1。

示例 1:

輸入: nums = [-1,0,3,5,9,12], target = 9
輸出: 4
解釋: 9 出現(xiàn)在 nums 中并且下標(biāo)為 4

示例 2:

輸入: nums = [-1,0,3,5,9,12], target = 2
輸出: -1
解釋: 2 不存在 nums 中因此返回 -1

提示:

你可以假設(shè) nums 中的所有元素是不重復(fù)的。
n 將在 [1, 10000]之間。
nums 的每個(gè)元素都將在 [-9999, 9999]之間。

C++代碼

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size()-1;
        while(left<=right){
            int mid = (left+right)/2;  //計(jì)算計(jì)算中間值
            if(nums[mid]==target) return mid; //如果找到了target,返回當(dāng)前的索引
            if(nums[mid]<target) left = mid+1; //如果nums[mid]<target,證明正確的值在當(dāng)前位置的右邊
            if(nums[mid]>target) right = mid-1; //如果nums[mid]>target,證明正確的值在當(dāng)前位置的左邊
        }
        return -1;
    }
};

體會(huì)

本題是二分查找最為基礎(chǔ)的題目,對(duì)于理解二分查找非常重要。

二分查找 模版#1

模板 #1 是二分查找的最基礎(chǔ)和最基本的形式。這是一個(gè)標(biāo)準(zhǔn)的二分查找模板,是非常基礎(chǔ)簡單的二分查找。模板 #1 用于查找可以通過訪問數(shù)組中的單個(gè)索引來確定的元素或條件。模版#1 不需要后處理,因?yàn)槊恳徊街校愣荚跈z查是否找到了元素。如果到達(dá)末尾,則知道未找到該元素。

模版#1 對(duì)應(yīng)的例題為:
LeetCode69 x 的平方根
LeetCode374 猜數(shù)字大小
LeetCode33 搜索旋轉(zhuǎn)排序數(shù)組

模版#1 C++代碼

int binarySearch( vector<int> & nums, int target )
{
    if ( nums.size() == 0 )
        return(-1);
    int left = 0, right = nums.size() - 1;
    while ( left <= right )
    {
        /* Prevent (left + right) overflow */
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) return mid;
        else if ( nums[mid] < target ) left = mid + 1;
        else right = mid - 1;
    }
    /* End Condition: left > right */
    return(-1);
}

語法關(guān)鍵

初始條件:left = 0, right = length-1
終止:left > right
向左查找:right = mid-1
向右查找:left = mid+1

LeetCode69 x 的平方根

題目

實(shí)現(xiàn) int sqrt(int x) 函數(shù)。

計(jì)算并返回 x 的平方根,其中 x 是非負(fù)整數(shù)。

由于返回類型是整數(shù),結(jié)果只保留整數(shù)的部分,小數(shù)部分將被舍去。

示例 1:

輸入: 4
輸出: 2

示例 2:

輸入: 8
輸出: 2
說明: 8 的平方根是 2.82842..., 
     由于返回類型是整數(shù),小數(shù)部分將被舍去。

C++代碼

class Solution {
public:
    int mySqrt(int x){
        int left = 0;
        int right = x;
        if (x <= 1) return x;
        int ans= 0;
        while(left<=right){
            int mid = (right+left) /2; //計(jì)算中間值
            if(x/mid >= mid ) {
                left = mid+1; //如果mid*mid<=x 證明mid小了 left = mid+1
                ans = mid; //當(dāng)前的mid作為ans
            }
            else right = mid-1; //否則right = mid-1
        }
        return ans;
    }
};

體會(huì)

上述代碼采用二分查找的思路,與模版完全一致,注意每次計(jì)算的過程中要將mid存儲(chǔ)在ans中,因?yàn)槲覀儫o法通過mid*mid==x去判斷mid是否是正確值,所以我們將mid存儲(chǔ)在ans中,最后返回。

該題還可以使用牛頓迭代法的思路進(jìn)行求解。
附代碼:

class Solution {
public:
    int mySqrt(int x) {
        if (x == 0) return 0;
        double res = 1, pre = 0;
        while (abs(res - pre) > 1e-6) {
            pre = res;
            res = (res + x / res) / 2;
        }
        return int(res);
    }
};

LeetCode374 猜數(shù)字大小

題目

我們正在玩一個(gè)猜數(shù)字游戲。 游戲規(guī)則如下:
我從 1 到 n 選擇一個(gè)數(shù)字。 你需要猜我選擇了哪個(gè)數(shù)字。
每次你猜錯(cuò)了,我會(huì)告訴你這個(gè)數(shù)字是大了還是小了。
你調(diào)用一個(gè)預(yù)先定義好的接口 guess(int num),它會(huì)返回 3 個(gè)可能的結(jié)果(-110):

-1 : 我的數(shù)字比較小
 1 : 我的數(shù)字比較大
 0 : 恭喜!你猜對(duì)了!

示例 :

輸入: n = 10, pick = 6
輸出: 6

C++代碼

int guess(int num);
class Solution {
public:
    int guessNumber(int n) {
        long left = 0;
        long right = n;
        while(left<=right){
            long mid = (left+right)/2;
            if(guess(mid)==0) return mid;
            if(guess(mid)==1) left = mid+1;
            if(guess(mid)==-1) right = mid-1;
        }
        return left;
    }
};

體會(huì)

這個(gè)題完全就是大一的課后題,基礎(chǔ)二分查找問題,與模版#1思路完全一致,比較大小的部分已經(jīng)在guess中封裝了,注意判斷好guess最后的返回值。

LeetCode33 搜索旋轉(zhuǎn)排序數(shù)組

題目

假設(shè)按照升序排序的數(shù)組在預(yù)先未知的某個(gè)點(diǎn)上進(jìn)行了旋轉(zhuǎn)。
( 例如,數(shù)組 [0,1,2,4,5,6,7] 可能變?yōu)?[4,5,6,7,0,1,2] )。
搜索一個(gè)給定的目標(biāo)值,如果數(shù)組中存在這個(gè)目標(biāo)值,則返回它的索引,否則返回 -1
你可以假設(shè)數(shù)組中不存在重復(fù)的元素。
你的算法時(shí)間復(fù)雜度必須是 O(log n) 級(jí)別。

示例 1:

輸入: nums = [4,5,6,7,0,1,2], target = 0
輸出: 4

示例 2:

輸入: nums = [4,5,6,7,0,1,2], target = 3
輸出: -1

C++代碼

class Solution {
public:
    int search(vector<int> nums, int target) {
        //對(duì)于特殊情況的判斷
        if(nums.size()==0) return -1;
        if(nums.size()==1 && nums[0]==target) return 0;
        if(nums.size()==1 && nums[0]!=target) return -1;

        int index= 0 ; //記錄軸點(diǎn)
        int size = nums.size();//記錄下初始數(shù)組的長度
        for(int i=0;i<nums.size();i++){
            if(nums[i]<nums[i-1]){
                index = i; //找到軸點(diǎn)后結(jié)束循環(huán)
                break;
            }
            nums.push_back(nums[i]); //在找到軸點(diǎn)之前,將軸點(diǎn)前數(shù)字依次放在數(shù)組尾部
        }
        int left = index; //left = 軸點(diǎn)
        int right = nums.size()-1; //right = 數(shù)組當(dāng)前長度-1 
        int ans = -1;//ans表示最終的位置
        while(left<=right){
            int mid = (left+right)/2; //計(jì)算中點(diǎn)
            if(nums[mid]==target){
                ans = mid; //如果nums[mid] == target 
                break;
            }
            if(nums[mid]<target) left = mid+1; //如果nums[mid]<target 修改left
            if(nums[mid]>target) right = mid-1; //如果 nums[mid]>target 修改 right
            if(nums[right] == target ) ans = right; //如果 nums[right] == target 將 ans修改為right 否則ans為-1
            else ans = -1;
        }
        return ans % size;
    }
};

體會(huì)

依舊是一個(gè)模版題,難度較低。我們之前說過,二分查找的三個(gè)步驟,第一步是預(yù)處理 即如果集合未排序,則進(jìn)行排序。這個(gè)題將數(shù)組對(duì)某一軸點(diǎn)進(jìn)行了旋轉(zhuǎn),需要進(jìn)行預(yù)處理。我們尋找到軸點(diǎn),將數(shù)組修改為升序后,預(yù)處理完成,后續(xù)進(jìn)行二分即可。該題最后的處理有些類似模板2。

二分查找 模版#2

模板 #2 是二分查找的高級(jí)模板。它用于查找需要訪問數(shù)組中當(dāng)前索引及其直接右鄰居索引的元素或條件。
查找條件需要訪問元素的直接右鄰居。
使用元素的右鄰居來確定是否滿足條件,并決定是向左還是向右。
保證查找空間在每一步中至少有 2 個(gè)元素。
需要進(jìn)行后處理。 當(dāng)你剩下 1 個(gè)元素時(shí),循環(huán) / 遞歸結(jié)束。 需要評(píng)估剩余元素是否符合條件。

模版#2 對(duì)應(yīng)的例題為:
LeetCode278 第一個(gè)錯(cuò)誤的版本
LeetCode75 尋找峰值
LeetCode159 尋找旋轉(zhuǎn)排序數(shù)組中的最小值

模版#2 C++代碼

int binarySearch(vector<int>& nums, int target){
   if(nums.size() == 0)
       return -1;

   int left = 0, right = nums.size();
   while(left < right){
       // Prevent (left + right) overflow
       int mid = left + (right - left) / 2;
       if(nums[mid] == target){ return mid; }
       else if(nums[mid] < target) { left = mid + 1; }
       else { right = mid; }
   }

   // Post-processing:
   // End Condition: left == right
   if(left != nums.size() && nums[left] == target) return left;
   return -1;
}

語法關(guān)鍵

初始條件:left = 0, right = length
終止:left == right
向左查找:right = mid
向右查找:left = mid+1

LeetCode278 第一個(gè)錯(cuò)誤的版本

題目

你是產(chǎn)品經(jīng)理,目前正在帶領(lǐng)一個(gè)團(tuán)隊(duì)開發(fā)新的產(chǎn)品。不幸的是,你的產(chǎn)品的最新版本沒有通過質(zhì)量檢測。由于每個(gè)版本都是基于之前的版本開發(fā)的,所以錯(cuò)誤的版本之后的所有版本都是錯(cuò)的。

假設(shè)你有 n 個(gè)版本 [1, 2, ..., n],你想找出導(dǎo)致之后所有版本出錯(cuò)的第一個(gè)錯(cuò)誤的版本。

你可以通過調(diào)用 bool isBadVersion(version) 接口來判斷版本號(hào) version 是否在單元測試中出錯(cuò)。實(shí)現(xiàn)一個(gè)函數(shù)來查找第一個(gè)錯(cuò)誤的版本。你應(yīng)該盡量減少對(duì)調(diào)用 API 的次數(shù)。

示例:

給定 n = 5,并且 version = 4 是第一個(gè)錯(cuò)誤的版本。

調(diào)用 isBadVersion(3) -> false
調(diào)用 isBadVersion(5) -> true
調(diào)用 isBadVersion(4) -> true

所以,4 是第一個(gè)錯(cuò)誤的版本。 

C++代碼

class Solution {
public:
    int firstBadVersion(int n) {
        long left = 0;
        long right = n;
        while(left<right){
            long mid = (left+right)/2; //計(jì)算中點(diǎn)
            if(isBadVersion(mid)==false) left = mid+1; //如果mid是正常產(chǎn)品,證明第一個(gè)錯(cuò)誤產(chǎn)品在右側(cè)
            if(isBadVersion(mid)==true) right = mid; //如果mid是錯(cuò)誤產(chǎn)品,證明第一個(gè)錯(cuò)誤產(chǎn)品在是自己或者在左側(cè)
        }
        return left;
    }
};

體會(huì)

題目非常簡單,直接套用模板#2即可。此題與模板#1的最大區(qū)別就在于,right不能修改為mid-1,而必須修改為mid。因?yàn)楫?dāng)如果mid是錯(cuò)誤產(chǎn)品,無法判斷第一個(gè)錯(cuò)誤版本在mid之前,還是就是當(dāng)前mid。這是模板#2與模板#1的最大不同。

LeetCode75 尋找峰值

題目

峰值元素是指其值大于左右相鄰值的元素。
給定一個(gè)輸入數(shù)組 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。
數(shù)組可能包含多個(gè)峰值,在這種情況下,返回任何一個(gè)峰值所在位置即可。
你可以假設(shè) nums[-1] = nums[n] = -∞

示例 1:

輸入: nums = [1,2,3,1]
輸出: 2
解釋: 3 是峰值元素,你的函數(shù)應(yīng)該返回其索引 2。

示例 2:

輸入: nums = [1,2,1,3,5,6,4]
輸出: 1 或 5 
解釋: 你的函數(shù)可以返回索引 1,其峰值元素為 2;
     或者返回索引 5, 其峰值元素為 6。

說明:
你的解法應(yīng)該是 O(logN) 時(shí)間復(fù)雜度的。

C++代碼

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        if(nums.size()==1) return 0;
        long left = 0;
        long right = nums.size()-1;
        while(left<right){
            long mid = (left+right)/2; //計(jì)算中間值
            if(nums[mid]>nums[mid+1]) right = mid; //如果當(dāng)前值大于后一個(gè)值 證明當(dāng)前點(diǎn)可能是峰值點(diǎn)
            if(nums[mid]<nums[mid+1]) left = mid+1; //如果當(dāng)前值小于后一個(gè)值 證明在這之后沒有峰值點(diǎn)
        }
        return left;
    }
};

體會(huì)

直接套用模板2,此題與上一道題非常類似,如果當(dāng)前值大于后一個(gè)值 證明當(dāng)前點(diǎn)可能是峰值點(diǎn) right修改為mid,如果當(dāng)前值小于后一個(gè)值 證明在這之后沒有峰值點(diǎn)left修改為mid+1,最后返回left。
PS:如果此題要求求解最值,則需要使用三分法。

LeetCode159 尋找旋轉(zhuǎn)排序數(shù)組中的最小值

題目

假設(shè)按照升序排序的數(shù)組在預(yù)先未知的某個(gè)點(diǎn)上進(jìn)行了旋轉(zhuǎn)。
( 例如,數(shù)組 [0,1,2,4,5,6,7] 可能變?yōu)?[4,5,6,7,0,1,2] )。
請(qǐng)找出其中最小的元素。
你可以假設(shè)數(shù)組中不存在重復(fù)元素。
示例 1:

輸入: [3,4,5,1,2]
輸出: 1

示例 2:

輸入: [4,5,6,7,0,1,2]
輸出: 0

C++代碼

class Solution {
public:
    int findMin(vector<int> nums) {
        if(nums.size()==1) return nums[0];
        int left = 0;
        int right = nums.size()-1;
        while(left<right && nums[left]>nums[right] ){
            int mid = (left+right)/2;
            if(nums[left]<=nums[mid]) left = mid+1; //如果nums[left]<=nums[mid],即mid前的序列是遞增的,則軸值一定不在mid之前,修改left=mid+1
            else if(nums[left]>nums[mid]) right = mid;//如果nums[left]>nums[mid],mid前的序列不是遞增的,則可能存在軸值,切mid有可能是軸值,修改right=mid。
        }
        return nums[left];
    }
};

體會(huì)

依舊是一道模版題。在確定mid之后,需要比對(duì)left與mid之間的關(guān)系,如果nums[left]<=nums[mid],即mid前的序列是遞增的,則軸值一定不在mid之前,修改left=mid+1;如果nums[left]>nums[mid],mid前的序列不是遞增的,則可能存在軸值,切mid有可能是軸值,修改right=mid。注意確定好邊界即可。

二分查找 模版#3

模板 #3 是二分查找的另一種獨(dú)特形式。它用于搜索需要訪問當(dāng)前索引及其在數(shù)組中的直接左右鄰居索引的元素或條件。
搜索條件需要訪問元素的直接左右鄰居。
使用元素的鄰居來確定它是向右還是向左。
保證查找空間在每個(gè)步驟中至少有 3 個(gè)元素。
需要進(jìn)行后處理。 當(dāng)剩下 2 個(gè)元素時(shí),循環(huán) / 遞歸結(jié)束。 需要評(píng)估其余元素是否符合條件。

模版#3 對(duì)應(yīng)的例題為:
LeetCode34 在排序數(shù)組中查找元素的第一個(gè)和最后一個(gè)位置
LeetCode658 找到 K 個(gè)最接近的元素

模版#3 C++代碼

int binarySearch(vector<int>& nums, int target){
    if (nums.size() == 0)
        return -1;

    int left = 0, right = nums.size() - 1;
    while (left + 1 < right){
        // Prevent (left + right) overflow
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid;
        } else {
            right = mid;
        }
    }
    // Post-processing:
    // End Condition: left + 1 == right
    if(nums[left] == target) return left;
    if(nums[right] == target) return right;
    return -1;
}

語法關(guān)鍵

初始條件:left = 0, right = length-1
終止:left + 1 == right
向左查找:right = mid
向右查找:left = mid

LeetCode34 在排序數(shù)組中查找元素的第一個(gè)和最后一個(gè)位置

題目

給定一個(gè)按照升序排列的整數(shù)數(shù)組 nums,和一個(gè)目標(biāo)值 target。找出給定目標(biāo)值在數(shù)組中的開始位置和結(jié)束位置。

你的算法時(shí)間復(fù)雜度必須是 O(log n) 級(jí)別。

如果數(shù)組中不存在目標(biāo)值,返回 [-1, -1]

示例 1:

輸入: nums = [5,7,7,8,8,10], target = 8
輸出: [3,4]

示例 2:

輸入: nums = [5,7,7,8,8,10], target = 6
輸出: [-1,-1]

C++代碼

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.size()==0) return {-1,-1};
        int left = 0;
        int right = nums.size()-1;
        while(left<right){
            int mid = (left+right)/2;
            if(nums[mid]==target) { //如果找到target 只需要向前向后搜索這個(gè)序列 找到起點(diǎn)和終點(diǎn)即可
                int I=mid;
                int j=mid;
                while(nums[i+1]==target && i<nums.size()-1) i=i+1; 
                while(nums[j-1]==target && j>0) j = j-1;
                return{j,i};
            }
            if(nums[mid]<target) left = mid+1; //如果nums[mid]<target 說明target在mid的右側(cè)
            else if(nums[mid]>target) right = mid-1; //如果nums[mid]>target 說明target在mid的左側(cè)
        }
        return{-1,-1};
    }
};

體會(huì)

做這個(gè)題的時(shí)候很想將模版#3套用進(jìn)去,但是沒有成功。這個(gè)題使用的是比較經(jīng)典的二分思路。本次需要尋找對(duì)應(yīng)序列的起點(diǎn)與終點(diǎn),所以當(dāng)我們找到一個(gè)target的值時(shí),要向前向后進(jìn)行搜索確定序列的起點(diǎn)終點(diǎn)。

LeetCode658 找到 K 個(gè)最接近的元素

題目

給定一個(gè)排序好的數(shù)組,兩個(gè)整數(shù) k 和 x,從數(shù)組中找到最靠近 x(兩數(shù)之差最小)的 k 個(gè)數(shù)。返回的結(jié)果必須要是按升序排好的。如果有兩個(gè)數(shù)與 x 的差值一樣,優(yōu)先選擇數(shù)值較小的那個(gè)數(shù)。
示例 1:

輸入: [1,2,3,4,5], k=4, x=3
輸出: [1,2,3,4]

示例 2:

輸入: [1,2,3,4,5], k=4, x=-1
輸出: [1,2,3,4]

說明:
k 的值為正數(shù),且總是小于給定排序數(shù)組的長度。
數(shù)組不為空,且長度不超過 104
數(shù)組里的每個(gè)元素與 x 的絕對(duì)值不超過 104

C++代碼

class Solution {
public:
    vector<int> findClosestElements(vector<int>& arr, int k, int x) {
        int left =0;
        int right = arr.size()-k; //為了防止越界,將right定為arr.size()-k,做預(yù)處理。
        while(left<right){
            int mid = (left+right)/2;
            if(abs(arr[mid]-x)<=abs(arr[mid+k]-x)){ //比較arr[mid]與x的差值 與 arr[mid+k]與x的差值,如果abs(arr[mid]-x)<=abs(arr[mid+k]-x),證明左側(cè)離x更接近,否則右側(cè)與x更接近。
                right = mid;
            }
            else left = mid+1;
        }
        return vector<int>(arr.begin()+left, arr.begin() + left+k); //返回結(jié)果的時(shí)候同樣要做處理。
    }
};

體會(huì)

本題依舊沒有用到模版#3(尷尬!),采用類似模版#2的思路,但本題的特殊之處在于預(yù)處理和后處理。預(yù)處理為了防止越界,將將right定為arr.size()-k,返回結(jié)果時(shí)返回的事[left,left+k]這一段序列。
本題使用二分的思路,我們要找與x最接近的k個(gè)數(shù),每次計(jì)算出mid后,比較arr[mid]與x的差值 與 arr[mid+k]與x的差值,如果abs(arr[mid]-x)<=abs(arr[mid+k]-x),證明左側(cè)離x更接近,更新right;否則右側(cè)與x更接近,更新left。

總結(jié)

模版對(duì)比

(圖片源自LeetCode)
三個(gè)模版的主要差異在于左、中、右索引的分配,循環(huán)或遞歸終止條件,后處理。
二分查找是非常常用的算法,真實(shí)應(yīng)用中,我們并不需要死記硬背這三個(gè)模版,更重要的是通過這三個(gè)模版理解二分搜索,在實(shí)際應(yīng)用中,仔細(xì)確定查找邊界、遞歸范圍,一般便可以解決問題。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評(píng)論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,497評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評(píng)論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,727評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,193評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,411評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,945評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,777評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,978評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,216評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評(píng)論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,657評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,960評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容

  • <center>#1 Two Sum</center> link Description:Given an arr...
    鐺鐺鐺clark閱讀 2,188評(píng)論 0 3
  • 算法思想貪心思想雙指針排序快速選擇堆排序桶排序荷蘭國旗問題二分查找搜索BFSDFSBacktracking分治動(dòng)態(tài)...
    第六象限閱讀 3,172評(píng)論 0 0
  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問題, 分享了一些自己做題目的經(jīng)驗(yàn)。 張土汪:刷leetcod...
    土汪閱讀 12,762評(píng)論 0 33
  • 本文首發(fā)于我的個(gè)人博客:尾尾部落 二分查找法作為一種常見的查找方法,將原本是線性時(shí)間提升到了對(duì)數(shù)時(shí)間范圍,大大縮短...
    繁著閱讀 29,484評(píng)論 3 9
  • 昨天蔡依林一身紅色運(yùn)動(dòng)套裝外搭了一件做舊牛仔外衣出現(xiàn)在北京機(jī)場,實(shí)話講,蔡依林還真的抓住了2018年的一個(gè)潮流,運(yùn)...
    蹦豆啊閱讀 419評(píng)論 0 0