二分查找

Posted wisedu1成长之路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分查找相关的知识,希望对你有一定的参考价值。

GitHub上有这样一个热门的开源项目,它介绍了如何刷算法,培养框架思维,这正是我正需要的。于是我把我学习过程中的笔记记录在此。原项目地址:

https://github.com/labuladong/fucking-algorithm

喜迎元旦节


HAPPY NEW YEAR

中国传统的元旦是指正月初一,“元旦”的概念,在不同时代、不同国家,具体所指也不尽相同。中国的“元旦”这一概念,历来指的是正月一日。“正月”的计算方法,在汉武帝时期以前也是很不统一的。因此,历代的元旦月、日也并不一致。夏时期的夏历以春季一月为正月,商时期的殷历以冬季十二月为正月,周时期的周历以冬季十一月为正月。

寻找右侧边界的二分查找

类似的,不用多言。

int right_bound(int[] nums, int target) {
   int left = 0, right = nums.length - 1;
   while (left <= right) {
       int mid = left + (right - left) / 2;
       if (nums[mid] < target) {
           left = mid + 1;
       } else if (nums[mid] > target) {
           right = mid - 1;
       } else if (nums[mid] == target) {
           left = mid + 1;
       }
   }
   if (right < 0 || nums[right] != target)
       return -1;
   return right;
}

逻辑统一

第一个,最基本的二分查找算法:

因为我们初始化 right = nums.length - 1
所以决定了我们的搜索区间是 [left, right]
所以决定了 while (left <= right)
同时也决定了 left = mid + 1 right = mid - 1

因为我们只需要找到一个 target 的索引即可
所以当 nums[mid] == target 时可以立即返回

第二个,寻找左侧边界的二分查找:

因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 right = mid
因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target 时不要⽴即返回
⽽要收紧右侧边界以锁定左侧边界

第三个,寻找右侧边界的二分查找:

因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 right = mid
因为我们需找到 target 的最右侧索引
所以当 nums[mid] == target 时不要⽴即返回
⽽要收紧左侧边界以锁定右侧边界
⼜因为收紧左侧边界时必须 left = mid + 1
所以最后⽆论返回 left 还是 right,必须减⼀

根据搜索区间,全部统一成两端都闭,便于记忆,只需修改两处即可变化出三种写法:

int binarySearch(int[] nums, int target) {
   int left = 0;
   int right = nums.length - 1;
   
   while (left <= right) {
       int mid = left + (right - left) / 2;
       if (nums[mid] == target)
           return mid;
       else if (nums[mid] < target)
           left = mid + 1;
       else if (nums[mid] > target)
           right = right - 1;
   }
   return -1;
}

int left_bound(int[] nums, int target) {
   int left = 0, right = nums.length - 1;
   while(left <= right) {
       int mid = left + (right - left) / 2;
       if (nums[mid] < target) {
           left = mid + 1;
       } else if (nums[mid] > target) {
           right = mid - 1;
       } else if (nums[mid] == target) {
           right = mid - 1;
       }
   }
   if (left >= nums.length || nums[left] != target)
       return -1;
   return left;
}

int right_bound(int[] nums, int target) {
   int left = 0, right = nums.length - 1;
   while (left <= right) {
       int mid = left + (right - left) / 2;
       if (nums[mid] < target) {
           left = mid + 1;
       } else if (nums[mid] > target) {
           right = mid - 1;
       } else if (nums[mid] == target) {
           left = mid + 1;
       }
   }
   if (right < 0 || nums[right] != target)
       return -1;
   return right;
}

二分查找算法的细节不过如此。



以上是关于二分查找的主要内容,如果未能解决你的问题,请参考以下文章

java 二分查找法

代码题(12)— 二分查找

二分查找代码

PHP实现二分查找算法(代码详解)

「算法笔记」一文摸秃二分查找

C语言二分查找