欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報(bào)邀請(qǐng)碼778899分享:算法魅力-二分查找實(shí)戰(zhàn)

柚子快報(bào)邀請(qǐng)碼778899分享:算法魅力-二分查找實(shí)戰(zhàn)

http://yzkb.51969.com/

目錄

前言

算法定義

樸素二分模版

二分查找?

?二分的邊界查找

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

?暴力算法

二分查找?

邊界查找分析

?山峰數(shù)組的峰頂

暴力枚舉

二分查找

搜索旋轉(zhuǎn)排序數(shù)組中的最小值(medium)

二分查找

結(jié)束語

前言

在前面我們學(xué)習(xí)了雙指針,以及其中誕生的分支滑動(dòng)窗口,接下來我們將探討其另外一個(gè)“兄弟”-二分查找。本質(zhì)上也是用左右兩個(gè)指針。

這個(gè)算法的前提是我們數(shù)據(jù)是有序排列的,這里的有序并不只是單純的有序,有時(shí)候根據(jù)數(shù)據(jù)的排列我們可以將數(shù)據(jù)劃分為兩個(gè)區(qū)間,可以簡稱為二段性,(兩段區(qū)間是有序的)且根據(jù)問題選擇合適的二分思路,二分算法有基礎(chǔ)的套用也有進(jìn)階的實(shí)現(xiàn)。

算法定義

二分查找算法(Binary Search Algorithm)是一種在有序數(shù)組中查找特定元素的搜索算法。其基本思想是通過不斷將搜索區(qū)間縮小一半來查找目標(biāo)值。以下是二分查找算法的步驟:

首先確定搜索區(qū)間的起始位置(left)和結(jié)束位置(right)。計(jì)算中間位置(mid),通常是(left + right) / 2,為了避免溢出也可以寫成left + (right - left) / 2。有時(shí)候也寫成?left + (right - left+1) / 2,兩者區(qū)別就是在偶數(shù)個(gè)數(shù)據(jù)時(shí),一個(gè)是取左邊,一個(gè)是取靠中間右邊??梢岳斫獬上蛳禄蛘呦蛏先≌1容^中間位置的元素與目標(biāo)值:

如果中間位置的元素等于目標(biāo)值,則搜索成功,返回中間位置的索引。如果中間位置的元素小于目標(biāo)值,則將搜索區(qū)間的起始位置設(shè)置為mid + 1,因?yàn)槟繕?biāo)值必定在右側(cè)區(qū)間。如果中間位置的元素大于目標(biāo)值,則將搜索區(qū)間的結(jié)束位置設(shè)置為mid - 1,因?yàn)槟繕?biāo)值必定在左側(cè)區(qū)間。重復(fù)步驟2和3,直到找到目標(biāo)值或者搜索區(qū)間為空(即left > right)。

如果整個(gè)數(shù)組中沒有找到目標(biāo)值,則返回一個(gè)特殊值(如-1)表示未找到。

樸素二分模版

#include

int binarySearch(const std::vector& nums, int target) {

int left = 0, right = nums.size() - 1;

while (left <= right) {

int mid = left + (right - left) / 2;

if (nums[mid] == target) {

return mid; // 找到目標(biāo)值,返回索引

} else if (nums[mid] < target) {

left = mid + 1; // 在右側(cè)區(qū)間繼續(xù)查找

} else {

right = mid - 1; // 在左側(cè)區(qū)間繼續(xù)查找

}

}

return -1; // 未找到目標(biāo)值

}

二分查找?

704. 二分查找 - 力扣(LeetCode)

本題可以通過暴力枚舉,通過將數(shù)組的數(shù)據(jù)與目標(biāo)值進(jìn)行比較,相等就返回下標(biāo),不存在就返回-1.

本題也可以直接就二分查找,就像題目標(biāo)題一樣。

class Solution {

public:

int search(vector& nums, int target) {

int left=0,right=nums.size()-1;

while(left<=right){

int mid=left+(right-left)/2;

if(nums[mid]

left=mid+1;

else if(nums[mid]>target)

right=mid-1;

else

return mid;

}

return -1;

}

};

?二分的邊界查找

有效利用數(shù)據(jù)的二段性

下面我們將通過一道題來引入進(jìn)階的二分。

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

34. 在排序數(shù)組中查找元素的第一個(gè)和最后一個(gè)位置 - 力扣(LeetCode)

?暴力算法

這道題我們同樣可以通過遍歷數(shù)據(jù)來求得左右位置,一個(gè)從左邊開始查找,一個(gè)從右邊開始查找,相等就保存并返回到數(shù)據(jù)中,代碼實(shí)現(xiàn)也很簡單。

class Solution {

public:

vector searchRange(vector& nums, int target) {

int left=-1,right=-1;

int n=nums.size();

for(int i=0;i

if(nums[i]==target){

left=i;

break;

}

}

for(int j=n-1;j>=0;j--){

if(nums[j]==target){

right=j;

break;

}

}

return{left,right};

}

};

但是這道題讓我們?cè)O(shè)置O(logn)的時(shí)間復(fù)雜度,同樣是查找,故我們可以采用二分查找的思路。

只不過這里要左右兩個(gè)值,理所當(dāng)然采用兩次二分查找,本質(zhì)上這道題就是進(jìn)行左右邊界查找。

二分查找?

class Solution {

public:

vector searchRange(vector& nums, int target) {

if(nums.size()==0)

return{-1,-1};

int left=0,right=nums.size()-1;

while(left

int mid=left+(right-left)/2;

if(nums[mid]

left=mid+1;

else

right=mid;

}

int begin=left;

if(nums[left]!=target)

return{-1,-1};

right=nums.size()-1;

while(left

int mid=left+(right-left+1)/2;

if(nums[mid]>target)

right=mid-1;

else

left=mid;

}

return{begin,right};

}

};

邊界查找分析

方便敘述,用?x 表示該元素, resLeft 表示左邊界, resRight 表示右邊界。

左邊界查找

尋找左邊界思路

左邊區(qū)間 [left, resLeft - 1] 都是小于 x 的;

右邊區(qū)間(包括左邊界) [resLeft, right] 都是大于等于 x 的;

因此,關(guān)于 mid 的落點(diǎn),我們可以分為下面兩種情況:

當(dāng)mid 落在 [left, resLeft - 1] 區(qū)間的時(shí)候,也就是 arr[mid] < target 。說明 [left, mid] 都是可以舍去的,此時(shí)更新 left 到 mid + 1 的位置, 繼續(xù)在 [mid + 1, right] 上尋找左邊界;

當(dāng) mid 落在 [resLeft, right] 的區(qū)間的時(shí)候,也就是 arr[mid] >= target 。

說明 [mid + 1, right] (因?yàn)?mid 可能是最終結(jié)果,不能舍去)是可以舍去的,此時(shí)更新 right 到 mid 的位置,繼續(xù)在 [left, mid] 上尋找左邊界;

注意:這面找中間元素需要向下取整。

因?yàn)楹罄m(xù)移動(dòng)左右指針的時(shí)候:

左指針: left = mid + 1 ,是會(huì)向后移動(dòng)的,因此區(qū)間是會(huì)縮小的;

右指針: right = mid ,可能會(huì)原地踏步(比如:如果向上取整的話,如果剩下 1,2 兩個(gè)元

素, left == 1 , right == 2 , mid == 2 。更新區(qū)間之后, left,right,mid 的

值沒有改變,就會(huì)陷入死循環(huán))。

因此一定要注意,當(dāng) right = mid 的時(shí)候,要向下取整。

尋找右邊界思路

用 resRight 表示右邊界;

注意到右邊界的特點(diǎn): 左邊區(qū)間 (包括右邊界) [left, resRight] 都是小于等于 x 的;

右邊區(qū)間 [resRight+ 1, right] 都是大于 x 的;

關(guān)于 mid 的落點(diǎn),可以分為下面兩種情況:

當(dāng)mid 落在 [left, resRight] 區(qū)間的時(shí)候,說明 [left, mid - 1] ( mid 不可以舍去,因?yàn)橛锌赡苁亲罱K結(jié)果) 都是可以舍去的,此時(shí)更新 left 到 mid

的位置;

當(dāng) mid 落在 [resRight+ 1, right] 的區(qū)間的時(shí)候,說明 [mid, right] 內(nèi)的元素是可以舍去的,此時(shí)更新 right 到 mid - 1 的位置;

注意:這里找中間元素需要向上取整。

因?yàn)楹罄m(xù)移動(dòng)左右指針的時(shí)候:

左指針: left = mid ,可能會(huì)原地踏步(比如:如果向下取整的話,如果剩下 1,2 兩個(gè)元

素, left == 1, right == 2,mid == 1 。更新區(qū)間之后, left,right,mid 的值沒有改變,就會(huì)陷入死循環(huán))。

右指針: right = mid - 1 ,是會(huì)向前移動(dòng)的,因此區(qū)間是會(huì)縮小的;

?綜上所述:

當(dāng)選擇兩段式的模板時(shí):

在求 mid 的時(shí)候,只有 right - 1 的情況下,才會(huì)向上取整(也就是 +1 取中間數(shù))

?山峰數(shù)組的峰頂

852. 山脈數(shù)組的峰頂索引 - 力扣(LeetCode)

暴力枚舉

峰頂?shù)奶攸c(diǎn):?兩側(cè)的元素都要?。

因此,我們可以遍歷數(shù)組內(nèi)的每?個(gè)元素,找到某?個(gè)元素?兩邊的元素?即可。

class Solution {

public:

int peakIndexInMountainArray(vector& arr) {

int n = arr.size();

// 遍歷數(shù)組內(nèi)每?個(gè)元素,直到找到峰頂

for (int i = 1; i < n - 1; i++)

// 峰頂滿?的條件

if (arr[i] > arr[i - 1] && arr[i] > arr[i + 1])

return i;

// 為了處理 oj 需要控制所有路徑都有返回值

return -1;

}

};

二分查找

通過發(fā)現(xiàn)題目發(fā)現(xiàn)數(shù)據(jù)存在二段性,峰值左邊數(shù)值依次遞增,峰值右邊依次遞減。

算法思路:

峰頂數(shù)據(jù)特點(diǎn): arr[i] > arr[i - 1] && arr[i] > arr[i + 1] ;

峰頂左邊的數(shù)據(jù)特點(diǎn): arr[i] > arr[i - 1] && arr[i] < arr[i + 1] ,也就是呈現(xiàn)上升趨勢;

峰頂右邊數(shù)據(jù)的特點(diǎn): arr[i] < arr[i - 1] && arr[i] > arr[i + 1] ,也就是呈現(xiàn)下降趨勢。

因此,根據(jù) mid 位置的信息,可以分為下?三種情況:

如果 mid 位置呈現(xiàn)上升趨勢,說明我們接下來要在 [mid + 1, right] 區(qū)間繼續(xù)搜索;

如果 mid 位置呈現(xiàn)下降趨勢,說明我們接下來要在 [left, mid - 1] 區(qū)間搜索;

如果 mid 位置就是?峰,直接返回結(jié)果。

因?yàn)榈谝粋€(gè)位置和最后一個(gè)位置不可能是峰值,所以left=1,right=arr.size()-2;

class Solution

{

public:

int peakIndexInMountainArray(vector& arr)

{

int left = 1, right = arr.size() - 2;

while(left < right)

{

int mid = left + (right - left + 1) / 2;

if(arr[mid] > arr[mid - 1]) left = mid;

else right = mid - 1;

}

return left;

}

};

.

搜索旋轉(zhuǎn)排序數(shù)組中的最小值(medium)

153. 尋找旋轉(zhuǎn)排序數(shù)組中的最小值 - 力扣(LeetCode)

?暴力解法就是遍歷數(shù)據(jù)直接找最小值。當(dāng)然也可以直接sort排序,直接返回?cái)?shù)組首元素(哈哈哈,這個(gè)方法抽象)

class Solution {

public:

int findMin(vector& nums) {

sort(nums.begin(),nums.end());

return nums[0];

}

};

二分查找

我們可以發(fā)現(xiàn)翻轉(zhuǎn)后的數(shù)組來兩段區(qū)間都是嚴(yán)格遞增的。

其中 C 點(diǎn)就是要求的點(diǎn)。

二分的本質(zhì):找到一個(gè)判斷標(biāo)準(zhǔn),使得查找區(qū)間能夠一分為二。

通過圖像我們可以發(fā)現(xiàn), [A,B] 區(qū)間內(nèi)的點(diǎn)都是嚴(yán)格大于 D 點(diǎn)的值的, C 點(diǎn)的值是嚴(yán)格小

于 D 點(diǎn)的值的。但是當(dāng) [C,D] 區(qū)間只有一個(gè)元素的時(shí)候, C 點(diǎn)的值是可能等于 D 點(diǎn)的值

的。 因此,初始化左右兩個(gè)指針 left , right : 然后根據(jù) mid 的落點(diǎn),我們可以這樣劃分下一次查詢的區(qū)間:

當(dāng) mid 在 [A,B] 區(qū)間的時(shí)候,也就是 mid 位置的值嚴(yán)格大于 D 點(diǎn)的值,下一次查詢區(qū)間在 [mid + 1,right] 上;

當(dāng) mid 在 [C,D] 區(qū)間的時(shí)候,也就是 mid 位置的值嚴(yán)格?于等于 D 點(diǎn)的值,下次

查詢區(qū)間在 [left,mid] 上。

當(dāng)區(qū)間長度變成 1 的時(shí)候,就是我們要找的結(jié)果。

class Solution {

public:

int findMin(vector& nums) {

int left=0,right=nums.size()-1;

int x=nums[right];

while(left

int mid=left+(right-left)/2;

if(nums[mid]>x)

left=mid+1;

else

right=mid;

}

return nums[left];

}

};

結(jié)束語

二分查找的講解就到此結(jié)束啦,各位,相信通過這些題目的講解大家對(duì)二分有了全新的認(rèn)識(shí)和理解,下個(gè)算法我們將學(xué)習(xí)前綴和,歡迎大家來指導(dǎo)交流。

最后感謝各位友友的支持?。?!

柚子快報(bào)邀請(qǐng)碼778899分享:算法魅力-二分查找實(shí)戰(zhàn)

http://yzkb.51969.com/

相關(guān)鏈接

評(píng)論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。

轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19842348.html

發(fā)布評(píng)論

您暫未設(shè)置收款碼

請(qǐng)?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄