将它印入骨髓:二分查找及其变种算法

将它印入骨髓:二分查找及其变种算法

这两天阿清在想程序员的基础该如何评价?我认为手边的代码是非常重要的一环。在你的脑海里留下了哪些算法,给你一张纸和笔,能否手到擒来,信手拈来,说来就来?

就现在,你能写出归并排序吗?能写出快速排序吗?全排列的解法呢?还有二叉树的非递归前中序遍历?…

有句成语,死去活来,我想确实需要先”死记硬背“下那些经典的东西,你才能在实际场景”灵活自如”。

所以,阿清决定接下来和大家一起将一些经典算法印入骨髓,将它们内化成身体的一部分。

今天的主题是二分查找及其变种

今天介绍二分查找的三个代码:查找一个数、查找左边界、查找右边界。



01


查找一个数


大家都听过二分查找算法,但要想准确的写出二分查找,还是需要关注几个细节。

分别是起始位置定义边界判断

我们都知道数组的索引是从0开始的,即数组arr储存数据的位置包括:0,1,2,… , arr.length – 1

在定义二分查找时,我们都会定义low与high分别代表,数组的起始左边界与 起始右边界。

在这里,要注意定义的起始位置。即 high = arr.length 还是 high = arr.length – 1 ?

若 high = arr.length , 循环的判断条件就为 low < high , 即当low = high 时,跳出循环。

若 high = arr.length – 1, 循环的判断条件就为 low <= high , 即当low > high 时,跳出循环。

先来看 high = arr.length – 1的代码:

public int binarySearch(int[] arr, int target) 
 int ans = -1;
    int low = 0;
    int high = arr.length - 1;
    int mid;
    while (low <= high) {
        mid = low + (high - low) / 2;
        if (arr[mid] == target) {
            ans = mid;
            break;
        } else if (arr[mid] > target) {
            high = mid - 1;
        } else if (arr[mid] < target) {
            low = mid + 1;
        }
    }
    return ans;
}

再来看 high = arr.length的代码:

public int binarySearch(int[] arr, int target) 
 int ans = -1;
    int low = 0
    // 注意!
    int high = arr.length;
    int mid;
    // 注意!
    while (low < high) {
        mid = low + (high - low) / 2;
        if (arr[mid] == target) {
            ans = mid;
            break;
        } else if (arr[mid] > target) {
            // 注意!
            high = mid; 
        } else if (arr[mid] < target) {
            low = mid + 1;
        }
    }
    return ans;
}

上面这块代码中注释的几处地方,就是与 high = arr.length – 1不同的地方。

当定义high = arr.length – 1时,你定义的是一个闭区间,

而当定义high = arr.length时,你定义的是一个左闭右开的区间。

针对你定义的区间,在迭代中,每一个子区间应与你最开始定义的区间保持一致。

在这里,阿清不做过度的描述,大家可以针对上面两段代码,选择一个用例,自己一步一步调试,当你记住一个后,另外一个就很容易记了。



02


查找左边界


如何查找某个数在数组中左边界,注意两个点:

1、在找到target时,应继续向左边探寻它的位置,

2、在循环退出来时,应判断当前位置是否还是数组的有效索引,若是,则找到左边界,若不是,则找到右边界。

以下代码以 mid = ans.length 为例:

public int leftBound(int[] arr, int target) 
 int ans = -1;
    int low = 0
    int high = arr.length - 1;
    int mid;
    while (low <= high) {
        mid = low + (high - low) / 2;
        // 与 查找一个数 不同的是 当找到target之后,接着向左缩短区间
        if (arr[mid] == target) {
            high = mid - 1
        } else if (arr[mid] > target) {
            high = mid - 1
        } else if (arr[mid] < target) {
            low = mid + 1;
        }
    }
    // 当low = arr.length时,说明数组中的数都小于target 没找到左边界 
    // 当 low < arr.length时,且arr[low] 不等于target,则说明没找到左边界
    if (low == arr.length || tartet != arr[low]) {
        return -1;
    }
    return low;
}



03


查找右边界


那查找右边界,其实与查找边界类似,无非就是要更改 查找左边界 那段代码中注释的两个地方。

我们直接看代码:

public int rightBound(int[] arr, int target) 
 int ans = -1;
    int low = 0
    int high = arr.length - 1;
    int mid;
    while (low <= high) {
        mid = low + (high - low) / 2;
        // 与 查找一个数 不同的是 当找到target之后,接着向右缩短区间
        if (arr[mid] == target) {
            low = mid + 1
        } else if (arr[mid] > target) {
            high = mid - 1
        } else if (arr[mid] < target) {
            low = mid + 1;
        }
    }
    // 当high < 0时,说明数组中的数都大于target 
    // 当high > 0时,且arr[high]不等于target,则说明没找到右边界
    if (high < 0 || target != arr[high]) {
        return -1;
    }
    return high;
}



04


总结


今天阿清主要介绍了二分查找的三段代码,分别是查找一个数、查找左边界、查找有边界。

主要注意以下几点(可对照上面的代码来看):

1、high的定义,

high = arr.length – 1,对应左闭右闭区间, while 的判断条件 为 low <= high,且 当arr[mid] > target时,操作为 high = mid - 1

high = arr.length ,  对应左闭右开区间,while 的判断条件 为 low < high 且 当arr[mid] > target时,操作为 high = mid

2、在查找边界时,

在找到 target时,继续向左或向右缩短区间。

找左边界时,用low来判断是否找到左边界,

找右边界时,用high来判断是否找到右边界。

最后阿清想说,想记住这些算法没有什么巧,就是不停地写,不停地快速从脑海里把它们拿出来。

如果记不住,只能说明自己练习少了,继续练习即可。

关注六只栗子,面试不迷路!



作者    阿清

编辑   一口栗子  




将它印入骨髓:二分查找及其变种算法

将它印入骨髓:二分查找及其变种算法

将它印入骨髓:二分查找及其变种算法

将它印入骨髓:二分查找及其变种算法


原文始发于微信公众号(六只栗子):将它印入骨髓:二分查找及其变种算法

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/88508.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!