二分查找法类算法题总结

Posted CSandCatti

tags:

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

这是我在CSandCatti的第一篇文章,以后会不定期分享我的LeetCode刷题总结。
Binary Search刷题总结

目录

  1. Introduction

  1. Find peek Element

  1. Find Minimum in Rotated Sorted Array

  2. Last Position of Target

一. Introduction

Binary Search在LeetCode中算是中等频率的题目。这类题目的变种非常之多,本文将讨论应用到二分查找法的三道题目。

我觉得Binary Search的核心思想在于在每次查找的时候都要淘汰掉平均一半的数据,从而实现O(logn)的时间复杂度。因此题目的难点就在于淘汰的条件。同时二分法非常容易写出死循环,因此在做题时最好举几个例子,避免出错。

二. Find peek Element

LeeCode 162.Find peek Element

A peak element is an element that is greater than its neighbors.

Given an input array where num[i] ≠ num[i+1], find a peak element and return its index.

The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.

?You may imagine that num[-1] = num[n] = -∞.

For example, in array [1, 2, 3, 1], 3 is a peak element and your function should return the index number 2.

class Solution {
   public int findPeakElement(int[] nums) {
       if(nums == null || nums.length < 2)
           return 0;
       if(nums[0] > nums[1])
           return 0;
       if(nums[nums.length - 1] > nums[nums.length - 2])
           return nums.length - 1;
       int start = 1, end = nums.length - 2;
       int mid = start;
       while(start < end){
           mid = (start + end) / 2;
           if(nums[mid + 1] > nums[mid]){
               start = mid + 1;
           }else if(nums[mid - 1] > nums[mid]){
               end = mid - 1;
           }else{
               
               return mid;
           }
           
       }
       return end;
   }
}

There are many ways to solve this problem. Apparently, the most naive way is to simply traverse the array, compare each element with its neighbors, whose time complexity is O(n) in the worst case.

Actually, this problem can be solved by using binary search with time complexity of O(logn).

Because num[-1] = num[n] = -∞, we can imagine that the number increases at first, then the number begins to decrease at certain position(which may happen many times, but we only need to find one peek).

这就是解这个题目的核心思路:若存在两个点,一个点在递增,一个点在递减,则这两个点之间必定有峰。

所以我们可以每次取mid, 当发现这个点是递增时,由于end点是下降的,所以mid和end之间必定存在峰,淘汰掉mid左边的点,把start挪到mid,反之如果mid递减,就把end点挪到mid,如果都不是,那mid就是峰,直接return mid, 当淘汰到只剩一个点。直接return end。

三. Find Minimum in Rotated Sorted Array

LeetCode 153. Find Minimum in Rotated Sorted Array

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

Find the minimum element.

You may assume no duplicate exists in the array.


class Solution {
   public int findMin(int[] nums) {
       if(nums == null || nums.length == 0)
           return -1;
       int l = 0, r = nums.length - 1;
       
       while(l < r){
           
           int mid = (l + r) / 2;
           
           if(nums[mid] > nums[r])
               l = mid + 1;
           else
               r = mid;
       }
       
       return nums[l];
   }
}

这道题其实很简单。只要这样在图上画一下,我们就可以看出,当数组被旋转后,就形成了这样的结构。

target左边的值都比end大,右边的值都比end小,所以可以比较mid和end的大小关系,如果比end大,淘汰mid左边,反之,淘汰mid右边。

注意,这道题有两个易出错的点。

1.如果mid比end小,那么mid可能就是target,所以要 r = mid; 不可以 r = mid - 1;

2.不能比较mid和start的大小从而判断淘汰哪一边,因为数组可能旋转的地方在index 0 处,就是说这个数组旋转后没变化,依旧是sorted array。

四.Last Position of Target

LintCode 458. Last Position of Target

Find the last position of a target number in a sorted array. Return -1 if target does not exist.

Example

Given [1, 2, 2, 4, 5, 5].

For target = 2, return 2.

For target = 5, return 5.

For target = 6, return -1.


public class Solution {
   /**
    * @param nums: An integer array sorted in ascending order
    * @param target: An integer
    * @return: An integer
    */
   public int lastPosition(int[] nums, int target) {
       if(nums == null || nums.length == 0)
           return -1;
       
       int l = 0, r = nums.length - 1;
       
       while(l + 1 < r){
           int mid = (l + r) / 2;
           if(nums[mid] > target){
               r = mid - 1;
           }else{
               l = mid;
           }
       }
       
       if(nums[r] == target)
           return r;
       if(nums[l] == target)
           return l;
       return -1;
   }
}

这道题的思路就不用说了,非常基础的二分查找思路。

写这道题的原因是它Corner case有点多,稍不注意就会写出死循环。

当mid的值大于target时,很好处理,直接淘汰左边的数据 r = mid - 1。

可是因为这道题要找最后一个target, 所以当mid 的值等于target的时候,我们无法直接得出解,因为mid右边的值也可能是target,所以要让  l = mid。mid的值小于target的时候一样让  l = mid即可。

可是这样有个问题,如果和平常的二分法用while(l < r)作为条件的话,就有可能出现死循环。

比如数组[1,1],target == 1, 可以看到,while(l < r) 作为循环条件的话,l的值会一直是0,而r 的值会一直是1,这辈子都别想跳出循环了。

所以将循环条件改成while(l + 1< r), 这样,当还存在两个数字的时候,就跳出循环,就不会出现上面这种情况了,在return前再做一下检查,这题就做完了。





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

44期盘点那些必问的数据结构算法题之二分查找算法

[算法总结]二分查找-binarySearch

二分查找无废话版总结

二分查找专题总结 - 基础篇

二分查找算法应用总结

LeetCode面试刷题技巧-二分查找算法代码思路解析