面試官,我會寫二分查詢法了!對,沒有 bug 的那種!
第一篇二分搜尋論文是 1946 年發表,然而第一個沒有 bug 的二分查詢法卻是在 1962 年才出現,中間用了 16 年的時間。
2019 年的你,在面試的過程中能手寫出沒有 bug 的二分查詢法麼?
定義
在電腦科學中, 二分查詢 (英語:binary search),也稱 折半搜尋 (英語:half-interval search)、 對數搜尋 (英語:logarithmic search),是一種在 有序陣列 中查詢某一特定元素的 搜尋演算法 。
搜尋過程從陣列的中間元素開始,如果中間元素正好是要查詢的元素,則搜尋過程結束;
如果某一特定元素大於或者小於中間元素,則在陣列大於或小於中間元素的那一半中查詢,而且跟開始一樣從中間元素開始比較。
如果在某一步驟陣列為空,則代表找不到。
這種搜尋演算法每一次比較都使搜尋範圍縮小一半。
二分查詢法程式碼
按照上面的定義,我們來嘗試寫一下二分查詢法的程式碼。
public static int binary(int[] arr, int data) { int min = 0; int max = arr.length - 1; int mid; while (min <= max) { mid = (min + max) / 2; if (arr[mid] > data) { max = mid - 1; } else if (arr[mid] < data) { min = mid + 1; } else { return mid; } } return -1; } 複製程式碼
現在問你,上面的程式碼有沒有問題?哪段程式碼會出現 bug ?
請思考一分鐘後再往下檢視。
對於上面這段程式碼而言,問題出在第 6 行程式碼處:
mid = (min + max) / 2; 複製程式碼
這句程式碼在 min 和 max 很大的時候,會出現溢位的情況,從而導致陣列訪問出錯。
別看現在輕描淡寫的指出了這個錯誤,但這個錯誤在當時可是存在了好些年。
那怎麼改進呢?一般的做法是這樣的: 將加法變成減法。
public static int binary(int[] arr, int data) { int min = 0; int max = arr.length - 1; int mid; while (min <= max) { // 防止溢位 mid =min + (max - min) / 2; if (arr[mid] > data) { max = mid - 1; } else if (arr[mid] < data) { min = mid + 1; } else { return mid; } } return -1; } 複製程式碼
還有一種更高逼格的寫法,也是官方的二分搜尋法的實現寫法:使用 位運算 。
public static int binary(int[] arr, int data) { int min = 0; int max = arr.length - 1; int mid; while (min <= max) { // 無符號位運算子的優先順序較低,先括起來 mid =min + ((max - min) >>> 1); if (arr[mid] > data) { max = mid - 1; } else if (arr[mid] < data) { min = mid + 1; } else { return mid; } } return -1; } 複製程式碼
希望通過今天的文章能幫助讀者們在面試中手寫對程式碼,畢竟,對於很多小公司來說,二分查詢法會出現在他們的筆試題中的。
