二分查找

前幾天,在論壇看到有統計說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的元素的)下標,等等。這些,雖然只有一點點的變化,實現的時候確實要更加的細心。下面列出了這些二分檢索變種的實現。數組

  1. 找出第一個與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;
}
  1. 找出最後一個與key相等的元素
// 查找最後一個相等的元素
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;
}
  1. 查找第一個大於key的元素
// 查找第一個大於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;
}
  1. 查找最後一個等於或者小於key的元素
// 查找最後一個等於或者小於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;
}
  1. 查找最後一個小於key的元素
// 查找最後一個小於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 的寫法有什麼不足,該怎麼改進呢?指針

相關文章
相關標籤/搜索