二分查詢的講義和視訊

2023-06-30 09:00:43

 

原始碼下載:

https://pan.baidu.com/s/1wMsUK4hZpdttFzOK66n3mQ?pwd=x7a7 提取碼 x7a7

先進入《 視訊教學及配套源碼》,再進入《C++演演算法》。

線上看視訊:

https://www.bilibili.com/video/BV1nL411Q7DY/ 

 

1.1. 基礎

1.1.1. 最簡單的情況

a. 情況簡述

陣列已經按升序排好序。

假定要找的數一定存在。

如果存在重複的數,返回任意一個。

b. 原理

如果中間數和目標數相等,返回中間數索引。

如果中間數小於目標數,則目標數一定不在前半部分。

如果中間數大於目標數,則目標數一定不在後半部分。

假定陣列區域是左閉右開區間,中間索引=(左索引+右索引)/2

c. 測試資料

04中找1

輪次

待查資料

中間數

第一輪

0 1 2 3 4

2

第二輪

0 1

1

 

15中查詢4

輪次

待查資料

中間數

第一輪

1 2 3 4 5

3

第二輪

4 5

5

第三輪

4

4

 

1.1.2. 重複資料返回第一個

a. 情況簡述

陣列已經按升序排好序。

假定要找的數一定存在。

如果存在重複的數,返回第一個。

b. 原理

如果中間數小於目標數,則目標數一定不在前半部分。

如果中間數大於目標數,則目標數一定不在後半部分。

如果中間數等於目標數,則目標數一定不在後半部分。由於左半部分必須包括中間數,所以左開右閉區間。

c. 測試資料

1,2中尋找2

輪次

待查資料

索引

中間數

第一輪

1 2

(-1,1]

1

第二輪

2

(0,1]

2

 

2,2,3中尋找2

 

輪次

待查資料

索引

中間數

第一輪

2 2 3

(-1,2]

2

第二輪

2 2

(-1,1]

2

第三輪

2

(-1,0]

2

 

2,3,4中尋找2

輪次

待查資料

索引

中間數

第一輪

2 3 4

(-1,2]

3

第二輪

2 3

(-1,1]

2

第三輪

2

(-1,0]

2

1.1.3. 重複資料返回最後一個

a. 情況簡述

陣列已經按升序排好序。

假定要找的數一定存在。

如果存在重複的數,返回最後一個。

b. 原理

一,如果中間數小於目標數,則目標數一定不在左邊、中間,可能在右邊。

二,如果中間數大於目標數,則目標數一定不在右邊、中間,可能在左邊。

三,如果中間數等於目標數,則目標數一定不在左邊,可能在中間、右邊。

資料

左閉右開

0,1,2,3

0,1

2((0+4)/2)

3

0,1,2

0

1

2

0,1

0

1

 

左開右閉

0,1,2,3

0

1(-1+3)/2

2,3

0,1,2

 

0

1,2

0,1

 

0

1

結論:右(左)開,中間數就和右(左)區間合併,可以保持資料量均衡,左右數量相差不超過1。結合原理三,用左閉右開區間。

 

使用左閉右開(中右合併)後,情況分類

中間數<目標數

中右

>

=

中右

結論:小於等於可以合併

c. 測試資料

 

尋找

期望值

0123

0

0

0123

1

1

0123

2

2

1123

1

1

0113

1

2

 

 

 

 

1.1.4. 迴圈代替遞迴

迴圈結束的條件:元素數量小於2。計算的元素數量可能是0,也可能是負數(非法資料)。由於是二分,所以左右邊界一個不變, 一個變成中間位置。

1.1.5. 目標數不一定存在

a. 如果目標數不存在,返回-1

解決方法:返回前,判斷是否等於目標值,如果不是返回-1

b. 如果目標數不存在,返回它應該插入的位置

左開右閉

 

資料(尋找0

左右中間索引

1,3,5,7

0,4,2

1,3

0,2,1

1

0,1,0結束

資料(尋找2

左右中間索引

1,3,5,7

0,4,2

1,3

0,2,1

1

0,1結束

資料(尋找4

左右中間索引

1,3,5,7

0,4,2

1,3

0,2,1

3

1,2,1結束

資料(尋找6

左右中間索引

1,3,5,7

0,4,2

5,7

2,4,3

5

2,3,2結束

資料(尋找8

左右中間索引

1,3,5,7

0,4,2

5,7

2,4,3

7

3,4,3結束

規律:

if (vec[left] < iTarget)

{

return left+1;

}

1.1.6. stl二分函數

a. 小於、等於、大於iNum的數

const int iNum = 3;

auto it1 = data.lower_bound(iNum);

auto it2 = data.upper_bound(iNum);

[data.begin(),it1) 表示小於iNum的數。

[it1,it2)表示等於iNum的數。

[it2,data.end()) 表示大於iNum的數。

b. 大於、等於iNum1小於等於iNum2的數

const int iNum1 = 3,iNum2=7;

auto it1 = data.lower_bound(iNum1);

auto it2 = data.upper_bound(iNum2);

視訊順便演示了大於iNum1小於iNum2

c. 對向量二分

const int iNum1 = 3,iNum2=7;

std::sort(data.begin(), data.end());

auto it1 = std::upper_bound(data.begin(), data.end(),iNum1);

auto it2 = std::lower_bound(data.begin(), data.end(), iNum2);

d. 降序

見視訊。

e. 通過vector<int>v[1]查詢

std::sort(data.begin(), data.end(), [](const std::vector<int>& v1, const std::vector<int>& v2)

{return v1[1] < v2[1]; });

auto it1 = std::lower_bound(data.begin(), data.end(), iNum1, [](const std::vector<int>& v1, int iNum)

{

return v1[1] < iNum;

});

auto it2 = std::upper_bound(data.begin(), data.end(), iNum1, [](int iNum, const std::vector<int>& v2)

{return iNum < v2[1]; });

1.2. 習題

1.2.1. 最大親密度

有若干包餅乾,每包餅乾的數量記錄在陣列nums中,比如:{4,175} ,分配給若干(如:3)小朋友。

每種分配方案的親密度:任意兩個小朋友餅乾數的差的絕對值的最小值。

求所有分配方案中的最大親密度。

分配方案{1,7,5}的親密度=min(7-1,5-1,7-5}=2

分配方案{4,7,5}的親密度=min(7-4,5-4,7-5}=1

分配方案{4,1,5}的親密度=min(4-1,5-4,5-1}=1

分配方案{4,1,7}的親密度=min(4-1,7-4,7-1)=3

1. 解決思路

先按升序排序。

完成函數is,判斷是否存在方案能夠滿足親密只大於等於iQinMi

 

因為要找最大值,所以中值符合的時候,拋棄左邊。中值跟隨右邊,用左開右閉。