二分查詢法作為一種常見的查詢方法,將原本是線性時間提公升到了對數時間範圍,大大縮短了搜尋時間,但它有乙個前提,就是必須在有序資料中進行查詢。
二分查詢很好寫,卻很難寫對,據統計只有10%的程式設計師可以寫出沒有bug的的二分查詢**。出錯原因主要集中在判定條件和邊界值的選擇上,很容易就會導致越界或者死迴圈的情況。
下面對二分查詢及其變形進行總結:
public int binarysearch(int a, int target, int n)else if(a[mid] > target)else
}return -1;
}
其中,有幾個要注意的點:
迴圈的判定條件是:low <= high
為了防止數值溢位,mid = low + (high - low)/2
當a[mid]
不等於target
時,high = mid - 1
或low = mid + 1
leetcode參考:search insert position
a = [1,3,3,5,7,7,7,7,8,14,14]target = 7
return 4
public int binarysearchlowerbound(int a, int target, int n)else
}if(low < a.length && a[low] == target)
return low;
else
return -1;
}
a = [1,3,3,5,7,7,7,7,8,14,14]target = 7
return 7
public int binarysearchupperbound(int a, int target, int n)else
}if(high >= 0 && a[high] == target)
return high;
else
return -1;
}
此題以可變形為查詢第乙個大於目標值的數/查詢比目標值大但是最接近目標值的數
,我們已經找到了最後乙個不大於目標值的數,那麼再往後進一位,返回high + 1
,就是第乙個大於目標值的數。
劍指offer:數字在排序陣列**現的次數此題以可由第 2 題變形而來,我們已經找到了目標值區域的下(左)邊界,那麼再往左退一位,即
low - 1
,就是最後乙個小於目標值的數。其實low - 1
也是退出迴圈後high
的值,因為此時high
剛好等於low - 1
,它小於low
,所以 while 迴圈結束。我們只要判斷high
是否超出邊界即可。
a = [1,3,3,5,7,7,7,7,8,14,14]target = 7
return 3
int low = 0, high = n, mid;
while(low <= high)else
}return high < 0 ? -1 : high;
此題以可由第 3 題變形而來,我們已經找到了目標值區域的上(右)邊界,那麼再往右進一位,即high + 1
,就是第乙個大於目標值的數。其實high + 1
也是退出迴圈後low
的值,因為此時low
剛好等於high + 1
,它大於high
,所以 while 迴圈結束。我們只要判斷low
是否超出邊界即可。
a = [1,3,3,5,7,7,7,7,8,14,14]target = 7
return 8
int low = 0, high = n, mid;
while(low <= high)else
}return low > n ? -1 : low;
6.1 查詢旋轉陣列的最小元素(假設不存在重複數字)
leetcode: find minimum in rotated sorted arrayinput: [3,4,5,1,2]
output: 1
public int findmin(int nums)
}return nums[left];
}
注意這裡和之前的二分查詢的幾點區別:
迴圈判定條件為left < right
,沒有等於號
迴圈中,通過比較nums[left]與num[mid]的值來判斷mid所在的位置:
最後,left會指向最小值元素所在的位置。
6.2 查詢旋轉陣列的最小元素(存在重複項)
leetcode: find minimum in rotated sorted array ii劍指offer:旋轉陣列的最小數字
input: [2,2,2,0,1]
output: 0
public int findmin(int nums)
return nums[left];
}
和之前不存在重複項的差別是:當nums[mid] == nums[right]
時,我們不能確定最小值在mid
的左邊還是右邊,所以我們就讓右邊界減一。
7.1 不考慮重複項
leetcode: search in rotated sorted array法一:
public int search(int nums, int target)
int offset = left;
left = 0;
right = len - 1;
while(left <= right)
return -1;
}
法二:其實沒有必要找到旋轉陣列的分界點,對於搜尋左側還是右側我們是可以根據mid跟high的元素大小來判定出來的,直接根據target的值做二分搜尋就可以了。
public int search(int nums, int target) else if(nums[mid] <= nums[right])
}return -1;
}
7.2 存在重複項
leetcode: search in rotated sorted array ii
public boolean search(int nums, int target) else if(nums[mid] < nums[right])else
}return false;
}
劍指offer:二維陣列中的查詢二維陣列是有序的,從右上角來看,向左數字遞減,向下數字遞增。因此可以利用二分查詢的思想,從右上角出發:
演算法總結 二分查詢
本文首發於我的個人部落格 尾尾部落 二分查詢法作為一種常見的查詢方法,將原本是線性時間提公升到了對數時間範圍,大大縮短了搜尋時間,但它有乙個前提,就是必須在有序資料中進行查詢。二分查詢很好寫,卻很難寫對,據統計只有10 的程式設計師可以寫出沒有bug的的二分查詢 出錯原因主要集中在判定條件和邊界值的...
二分查詢 演算法總結
二分查詢也稱折半搜尋,是一種在有序陣列中查詢某一特定的元素的搜尋演算法。class solution else if nums mid mid left right 2 在left和right都很大的時候會出現溢位的情況,從而導致陣列訪問溢位。改進方法將加法變為減法 mid left right l...
二分查詢演算法c語言 演算法總結 二分查詢法
在leetcode的題目討論中,經常會有些大神將某一類的題目結題思路給整理了出來,感覺受益匪淺。受之啟發,萌生了自己也總結一下常見演算法的解題思路的想法,希望可以讓自己在這個總結的過程中可也融匯貫通這些思路,同時可以給後來者以啟發。從 開始好呢?就從經典的二分查詢法開始吧,這個演算法幾乎所有學過計算...