二分查詢不得不說的事
文章目錄
關於二分查詢
參考二分查詢。
二分查詢演算法的編寫框架大致如下:
int binarySearch(int[] nums, int target) {
int left = 0, right = ...;
while(...) {
int mid = (right + left) / 2;
if (nums[mid] == target) {
...
} else if (nums[mid] < target) {
left = ...
} else if (nums[mid] > target) { // 寫成else更簡化
right = ...
}
}
return ...;
}
計算mid
時有技巧可以防止溢位,寫成mid = left + (right - left) / 2
。
查詢一個數(基本的二分查詢)
這個場景是最簡單的,也是我們最熟悉的。搜尋一個數,如果存在則返回其下標,否則返回 -1。
- 假設陣列中有
n
個元素,下標範圍為[0 : n-1]
,要查詢的元素為key
。 - 初始化時,令
min = 0, max = n-1
。這裡提出一個搜尋區間的定義,此時演算法的搜尋區間就是閉區間[min, max]
,搜尋區間的特點決定了min, max
的移動方式。 - 迴圈條件為
while(min <= max)
,計算中間元素mid = (left + right) / 2
if A[mid] > key, then max = mid - 1
if A[mid] < key, then min = mid + 1
- 因為搜尋區間兩邊都是閉的,所以
min, max
移動時都是需要加一或者減一 - 該演算法的終止條件要麼為
nums[mid] = target
,要麼為left = right + 1
。
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; // 注意
while(left <= right) { // 注意
int mid = (right + left) / 2;
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1; // 注意
else if (nums[mid] > target)
right = mid - 1; // 注意
}
return -1;
}
- 注意啊注意,如果不修改
min, max
移動方法的話,最基本的二分查詢不可以直接修改為min=0. max=n, while(min < max)
這種寫法,可以舉個例子,nums = [1, 2, 3], target = 1
,min=0, max=3, mid=1, nums[1] > target
,移動max
之後min = max
直接結束,所以這種寫法是錯的。 - 如果非要寫成
min = 0, max = n, while(min < max)
,那麼移動時left = mid + 1
不用動,但是right = mid
! - 初始化,
min = 0, max = n
。那麼此時演算法的搜尋區間為左閉右開的[left, right)
。 - 迴圈條件為
while(min < max)
,計算中間元素mid = (left + right) / 2
if A[mid] > key, then max = mid
!!!!!if A[mid] < key, then min = mid + 1
- 該演算法的終止條件要麼為
nums[mid] = target
,要麼為left = right
。
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length; // 注意
while(left < right) { // 注意
int mid = (right + left) / 2;
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid; // 注意
}
return -1;
}
也就是說,初始化的min, max
決定了搜尋區間的特性,進而決定了演算法中迴圈的判斷條件以及min, max
的移動方式。(即,修改min. max
的初始化條件的話,只需要修改while
內的判斷條件以及min, max
的移動方式即可)
二分查詢的變種
參考二分查詢的變種:其中連結中的的2.5, 2.6
寫的有問題,沒有考慮min
可能越界的情況,正確寫法參考下面的5, 6
。
關於二分查詢,如果條件稍微變換一下,比如:陣列之中的資料可能可以重複,要求返回匹配的資料的最小(或最大)的下標;更進一步, 需要找出陣列中第一個大於key的元素(也就是最小的大於key的元素的)下標,等等。 這些,雖然只有一點點的變化,實現的時候確實要更加的細心。
二分查詢的變種和二分查詢原理一樣,主要就是變換判斷條件(也就是邊界條件)。
1. 查詢第一個與key相等的元素
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if(nums[mid] == target)
right = mid - 1; // 演算法能夠搜尋第一個key的關鍵所在!!
else if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
}
if(left < nums.leghth && nums[left] == target) {
return left;
}
return -1; // 還有可能沒有找到!
}
- 因為初始化
left = 0, right = n - 1
,迴圈條件中為left <= right
,因此迴圈終止條件為left = right + 1
,所以left
的取值範圍為[0, n]
,所以最後需要判斷left < nums.length?
。 - 假設搜尋過程的中間過程中就遇到了第一個與key相等的元素,我們稱此時的
mid
為final
,那麼下一步搜尋區間會變成[left, final - 1]
,因為這個區間裡面全是小於 key 的元素,所以最後結束時,left = final, right = final - 1
,left
整好指到了第一個與 key 相等的元素位置。 - 假設搜尋的最後一步才遇到與 key 相等的元素,也就是
left = right = final
,下一步left = final, right = final - 1
,不滿足迴圈條件終止。此時left
同樣指向了第一個也是唯一一個與 key 相等的元素位置。
分析的時候,將nums[mid]
與target
的關係分三部分進行討論,分析完可以合併:
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if(nums[mid] >= target) // 合併
right = mid - 1;
else if (nums[mid] < target)
left = mid + 1;
}
if(left < nums.leghth && nums[left] == target) {
return left;
}
return -1;
}
2. 查詢最後一個與key相等的元素
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if(nums[mid] <= target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
}
if(right >= 0 && nums[right] == target) {
return right;
}
return -1;
}
3. 查詢最後一個小於或等於key的元素
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if(nums[mid] <= target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
}
if(right >= 0 && nums[right] <= target) {
return right;
}
return -1; // 還有可能沒有找到!
}
其實最後可以直接寫成reutrn right
,因為對於特殊情況,沒有小於等於 key 的元素,也就是所有的都大於 key,那麼在搜尋過程中left
一直不變,right
一直在減小,結束時left = 0, right = -1
,要返回的就是-1;
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if(nums[mid] <= target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
}
return right;
}
4. 查詢最後一個小於key的元素
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if(nums[mid] < target)
left = mid + 1;
else if (nums[mid] >= target)
right = mid - 1;
}
return right;
}
5. 查詢第一個大於或等於key的元素
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if(nums[mid] >= target)
right = mid - 1;
else if (nums[mid] < target)
left = mid + 1;
}
if(left < nums.length && nums[left] >= key) {
return left;
}
return -1;
}
6. 查詢第一個大於key的元素
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if(nums[mid] > target)
right = mid - 1;
else if (nums[mid] <= target)
left = mid + 1;
}
if(left < nums.length && nums[left] > key) {
return left;
}
return -1;
}
相關文章
- 二分查詢(一)——純粹的二分查詢
- 查詢——二分查詢
- 二分查詢 | 二分查詢的一種推薦寫法
- 二分查詢
- 說說你對二分查詢的理解?如何實現?應用場景?
- 查詢演算法__二分查詢演算法
- 二分查詢的定義
- 二分查詢法
- PHP二分查詢PHP
- leetcode——二分查詢LeetCode
- leetcode -- 二分查詢LeetCode
- 查詢演算法之二分查詢演算法
- 二分查詢和折半插入排序一塊說說-很合適~~~排序
- 面試:誰說的無序就不能用二分查詢?面試
- 二分查詢的簡單理解
- 二分查詢函式的使用函式
- labuladong_二分查詢
- 二分查詢(c++)C++
- 704.二分查詢
- 詳解二分查詢
- Leetcode 704 二分查詢LeetCode
- 每日leetcode——二分查詢LeetCode
- python二分查詢模板Python
- 陣列的查詢(搜尋):線性查詢和二分法查詢陣列
- 二分查詢【折半查詢】演算法 PHP 版演算法PHP
- 精細化運營不得不說的那些事兒
- PHP 實現二分查詢PHP
- 圖解--二分查詢樹圖解
- 二分查詢成長錄
- 二分查詢-不套用模板
- 演算法->二分查詢演算法
- 【演算法】二分查詢演算法
- 力扣之二分查詢力扣
- 二分搜尋(折半查詢)
- 牛客網 查詢(二分查詢、北郵機試)
- 開源二三事|ShardingSphere 與 Database Mesh 之間不得不說的那些事Database
- 第二章 :查詢與排序-------二分查詢的遞迴解法排序遞迴
- 二分查詢基礎專題——二分模板