前幾天,在論壇看到有統計說90%的程序員不能寫對簡單的二分法。二分法不是很簡單的嗎?
其實,二分法真的不是那麼簡單,尤爲是二分法的各個變種。最最簡單的二分法,就是從一個排好序的數組之查找一個key值。以下面程序程序員
/** * 二分查找 找到該值在數組中的下標 不然爲-1 */ static int binarySearch(int [] array, int key) { int left = 0; int right = array.length - 1; while(left <= right) { int mid = (left + right) / 2; if(array[mid] == key) { return mid; } else if(array[mid] < key) { left = mid + 1; }else { right = mid - 1; } } return -1; }
這個程序,相信只要是一個合格的程序員應該都會寫。稍微注意點,每次移動left和right 指針的時候, 須要在mid的基礎上 + 1 或者 - 1, 防止出現死循環, 程序也就也可以正確運行。算法
但若是條件稍微變化一下, 你還會寫嗎?如,數組之中的數據可能能夠重複,要求返回匹配的數據的最小(或最大)的下標;更近一步, 須要找出數組中第一個大於key的元素(也就是最小的大於key的元素的)下標,等等。這些,雖然只有一點點的變化,實現的時候確實要更加的細心。下面列出了這些二分檢索變種的實現。數組
// 查找第一個相等的元素 static int findFirstEqual(int [] array, int key) { int left = 0; int right = array.length - 1; while (left <= right ) { int mid = (left + right) / 2; if ( array[mid] >= key) { right = mid - 1; }else{ left = mid + 1; } } if(left < array.length && array[left] == key){ return left; } return -1; }
// 查找最後一個相等的元素 static int findLastEqual(int[] array, int key) { int left = 0; int right = array.length - 1; // 這裏必須是 <= while (left <= right) { int mid = (left + right) / 2; if (array[mid] <= key) { left = mid + 1; } else { right = mid - 1; } } if (right >= 0 && array[right] == key) { return right; } return -1; }
3.查找第一個等於或者大於Key的元素測試
// 查找第一個等於或者大於key的元素 static int findFirstEqualLarger(int[] array, int key) { int left = 0; int right = array.length - 1; // 這裏必須是 <= while (left <= right) { int mid = (left + right) / 2; if (array[mid] >= key) { right = mid - 1; } else { left = mid + 1; } } return left; }
// 查找第一個大於key的元素 static int findFirstLarger(int[] array, int key) { int left = 0; int right = array.length - 1; // 這裏必須是 <= while (left <= right) { int mid = (left + right) / 2; if (array[mid] > key) { right = mid - 1; } else { left = mid + 1; } } return left; }
// 查找最後一個等於或者小於key的元素 static int findLastEqualSmaller(int[] array, int key) { int left = 0; int right = array.length - 1; // 這裏必須是 <= while (left <= right) { int mid = (left + right) / 2; if (array[mid] > key) { right = mid - 1; } else { left = mid + 1; } } return right; }
// 查找最後一個小於key的元素 static int findLastSmaller(int[] array, int key) { int left = 0; int right = array.length - 1; // 這裏必須是 <= while (left <= right) { int mid = (left + right) / 2; if (array[mid] >= key) { right = mid - 1; } else { left = mid + 1; } } return right; }
接下來,你們能夠對這四種變種算法進行相應的測試。
不少的時候,應用二分檢索的地方都不是直接的查找和key相等的元素,而是使用上面提到的二分檢索的各個變種,熟練掌握了這些變種,當你再次使用二分檢索的檢索的時候就會感受的更加的駕輕就熟了。這裏,我留給你們一個問題,這種 mid = (left + right) / 2 的寫法有什麼不足,該怎麼改進呢?指針