二分法有多少種寫法都不重要,數組
重要的是要會寫一種對的。函數
首先有幾個數字要注意測試
中位數有兩個,spa
經常使用的是下位中位數,通用的寫法以下,語言int常常自動向下取整,翻譯
median = (length - 1) / 2
指針的區間固然能夠開區間,也能夠閉區間,也能夠半開半閉。但老老實實兩頭取閉區間老是不會錯。上面的中位數,轉換成兩頭閉區間 [low,high] 就變成下面這樣:指針
median = low + (high - low) / 2
這裏還有個常見的坑,不要圖快用加法,會溢出,code
median = ( low + high ) / 2 // OVERFLOW
另一個關鍵點是「終結條件」。blog
不要以 low == high 作終結條件。會被跳過的,遞歸
if (low == high) { return (nums[low] >= target)? low : ++low; }
不相信在 [1, 5] 裏找 0 試試?ip
正確的終結條件是:
low > high
也就是搜索空間爲空。
知足終結條件之後,返回值徹底不須要糾結,直接返回低位 low。
由於回過頭去放慢鏡頭,二分查找的過程就是一個 維護 low 的過程:
low從0起始。只在中位數遇到肯定小於目標數時才前進,而且永不後退。low一直在朝着第一個目標數的位置在逼近。知道最終到達。
至於高位 high,就放心大膽地縮小目標數組的空間吧。
因此最後的代碼很是簡單,
public int binarySearch(int[] nums, int target) { int low = 0, high = nums.length-1; while (low <= high) { int mid = low + (high - low) / 2; if (nums[mid] < target) { low = mid + 1; } if (nums[mid] > target) { high = mid - 1; } if (nums[mid] == target) { return mid; } } return low; }
遞歸版也同樣簡單,
public int binarySearchRecur(int[] nums, int target, int low, int high) { if (low > high) { return low; } //base case int mid = low + (high - low) / 2; if (nums[mid] > target) { return binarySearchRecur(nums,target,low,mid-1); } else if (nums[mid] < target) { return binarySearchRecur(nums,target,mid+1,high); } else { return mid; } }
但上面的代碼能正常工做,有一個前提條件:
元素空間沒有重複值
推廣到有重複元素的空間,二分查找問題就變成:
尋找元素第一次出現的位置。
也能夠變相理解成另外一個問題,對應C++的 lower_bound() 函數
尋找第一個大於等於目標值的元素位置。
但只要掌握了上面說的二分查找的心法,代碼反而更簡單,
public int firstOccurrence(int[] nums, int target) { int low = 0, high = nums.length-1; while (low <= high) { int mid = low + (high - low) / 2; if (nums[mid] < target) { low = mid + 1; } if (nums[mid] >= target) { high = mid - 1; } } return low; }
翻譯成遞歸版也是同樣,
public int firstOccurrenceRecur(int[] nums, int target, int low, int high) { if (low > high) { return low; } int mid = low + (high - low) / 2; if (nums[mid] < target) { return firstOccurrenceRecur(nums,target,mid + 1,high); } else { return firstOccurrenceRecur(nums,target,low,mid-1); } }
以上代碼均經過leetcode測試。標準銀彈。天天早起寫一遍,鍛鍊肌肉。
最後想說,不要怕二分查找難寫,邊界狀況複雜。實際狀況是,你以爲煩躁,大牛也曾經由於這些煩躁過。一些臭名昭著的問題下面,常常是各類大牛的評論(噁心,變態,F***,等等)。並且這並不考驗什麼邏輯能力,只是仔細的推演罷了。拿個筆出來寫一寫,算一算不丟人。不少問題完全搞清楚之後,常常就是豁然開朗,而後之後妥妥觸類旁通。以上。