Leetcode中几道二分查找(Binary Search)的算法题总结

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。可是,折半查找要求线性表必须采用顺序存储结构,并且表中元素按关键字有序排列。二分查找法的时间复杂度是对数级别的,O(log2n)java

public int binarySearch(int [] array, double key) {
  int l = 0;
  int r = array.length - 1;
  while (l <= r) {
    int m = (l + r)/ 2;
    if (key == array[m])
      return m;
    else if (key < array[m])
      r = m - 1;
    else
      l = m + 1;
  }
  return l;
}

若是key在array中,返回的是key在array中的位置,若是不在array中,返回的是key应该插入的位置也就是第一个大于key的位置。这里和java.util.Arrays类返回值-(low + 1)不太同样,我的以为我这种写法作某些算法题更方便。算法

1. Sqrt(x)

实现 int sqrt(int x) 函数。计算并返回 x 的平方根。x 保证是一个非负整数。数组

分析:函数

这道题有两种解法,二分法和拟牛顿法spa

二分法 .net

class Solution(object):
    def mySqrt(self, x):
        """
        :type x: int
        :rtype: int
        """
        l = 0
        r = x // 2 + 1
        while l <= r:
            m = (l + r) // 2
            if m ** 2 <= x and (m + 1) ** 2 > x:
                return m
            elif m ** 2 > x:
                r = m - 1
            else:
                l = m + 1

牛顿法code

class Solution:
    def mySqrt(self, x):
        """
        :type x: int
        :rtype: int
        """
        r = x
        while r*r > x:
            r = (r + x//r) // 2
        return r

 

2.Search in Rotated Sorted Array I 、II

假设按照升序排序的数组在预先未知的某个关键点上旋转。xml

(即 0 1 2 4 5 6 7 将变成 4 5 6 7 0 1 2)。blog

给你一个目标值来搜索,若是数组中存在这个数则返回它的索引,不然返回 -1。排序

你能够假设数组中不存在重复。

分析:

这是二分查找的一道变形题,由于rotate的缘故,当咱们切取一半的时候可能会出现误区,因此咱们要作进一步的判断。具体来讲,假设数组是A,每次左边缘为l,右边缘为r,还有中间位置是m。在每次迭代中,分三种状况:
(1)若是target==A[m],那么m就是咱们要的结果,直接返回;
(2)若是A[m]<A[r],那么说明从m到r必定是有序的(没有受到rotate的影响),那么咱们只须要判断target是否是在m到r之间,若是是则把左边缘移到m+1,不然就target在另外一半,即把右边缘移到m-1。
(3)若是A[m]>=A[r],那么说明从l到m必定是有序的,一样只须要判断target是否在这个范围内,相应的移动边缘便可。
class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        l = 0
        r = len(nums) - 1
        while l <= r:
            m = (l + r) // 2
            if nums[m] == target:
                return m
            if nums[m] < nums[r]:
                if target > nums[m] and target <= nums[r]:
                    l = m + 1
                else:
                    r = m - 1
            else:
                if target < nums[m] and target >= nums[l]:
                    r = m - 1
                else:
                    l = m + 1
        return -1

  follow up:

若是数组元素容许重复,怎么办?

这会影响到程序的时间复杂度吗?会有怎样的影响,为何?

分析:

和Search in Rotated Sorted Array惟一的区别是这道题目中元素会有重复的状况出现。不过正是由于这个条件的出现,出现了比较复杂的case,甚至影响到了算法的时间复杂度。原来咱们是依靠中间和边缘元素的大小关系,来判断哪一半是不受rotate影响,仍然有序的。而如今由于重复的出现,若是咱们遇到中间和边缘相等的状况,咱们就丢失了哪边有序的信息,由于哪边都有多是有序的结果。假设原数组是{1,2,3,3,3,3,3},那么旋转以后有多是{3,3,3,3,3,1,2},或者{3,1,2,3,3,3,3},这样的咱们判断左边缘和中心的时候都是3,若是咱们要寻找1或者2,咱们并不知道应该跳向哪一半。解决的办法只能是对边缘移动一步,直到边缘和中间不在相等或者相遇,这就致使了会有不能切去一半的可能。因此最坏状况(好比所有都是一个元素,或者只有一个元素不一样于其余元素,而他就在最后一个)就会出现每次移动一步,总共是n步,算法的时间复杂度变成O(n)。代码以下:

class Solution:
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        if not nums:
            return False
        l = 0
        r = len(nums) - 1
        while l <= r:
            m = (l + r) // 2
            if nums[m] == target:
                return True
            if nums[m] < nums[r]:
                if target > nums[m] and target <= nums[r]:
                    l = m + 1
                else:
                    r = m - 1
            elif nums[m] > nums[r]:
                if target < nums[m] and target >= nums[l]:
                    r = m -1
                else:
                    l = m + 1
            else:
                r -= 1
        return False

 

3.Find Minimum in Rotated Sorted Array I 、II

假设一个按照升序排列的有序数组从某未知的位置旋转。

(好比 0 1 2 4 5 6 7 可能变成 4 5 6 7 0 1 2)。

找到其中最小的元素。

你能够假设数组中不存在重复的元素。

分析:

二分法O(log2n):

若是num[m] < num[r],说明pivot也就是最小的元素在m左边,极端状况有可能num[m]就是pivot,因此r = m 而不是 r= m -1,若是num[m] > num[r],则pivot在m的右边,因此l = m + 1

class Solution(object):
    def findMin(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        l = 0
        r = len(nums) - 1
        while l < r:
            m = (l + r) // 2
            if nums[m] < nums[r]:
                r = m
            else:
                l = m + 1
        return nums[l]  

  线性扫描O(n):  

class Solution(object):
    def findMin(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        for i in range(len(nums) - 1):
            if nums[i] > nums[i + 1]:
                return nums[i + 1]
        return nums[0]

  follow up:

数组中存在重复元素,处理方法与上一道题Search in Rotated Sorted Array同样,对边缘移动一步,直到边缘和中间不在相等或者相遇,这就致使了会有不能切去一半的可能。因此最坏状况(好比所有都是一个元素,或者只有一个元素不一样于其余元素,而他就在最后一个)就会出现每次移动一步,总共是n步,算法的时间复杂度变成O(n)

class Solution(object):
    def findMin(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        l = 0 
        r = len(nums) - 1
        while l < r:
            m = (l + r) // 2
            if nums[m] < nums[r]:
                r = m
            elif nums[m] > nums[r]:
                l = m + 1
            else:
                r -= 1
        return nums[l]

 

4.Find Peak Element

峰值元素是指其值大于左右相邻值的元素。

给定一个输入数组,其中 num[i] ≠ num[i+1],找到峰值元素并返回其索引。

数组可能包含多个峰值,在这种状况下,返回到任何一个峰值所在位置均可以。

你能够想象获得  num[-1] = num[n] = -∞

例如,在数组 [1, 2, 3, 1]中 3 是峰值元素您的函数应该返回索引号2。

你的解决方案应该是对数复杂度的。

分析:

这道题与Find Minimum in Rotated Sorted Array很像,依然能够用二分法或者线性扫描两种方法解决。

二分法O(log2n):

若是nums[m] > nums[m+1],则说明m的左侧确定存在峰值,由于若是nums[m-1] < nums[m],则说明m是峰值元素,若是说nums[m -1] > nums[m],则m -1的左侧还存在峰值,若是一直到m = 0的话,那0这个位置的元素就是峰值。

class Solution(object):
    def findPeakElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        l = 0
        r = len(nums) - 1
        while l < r:
            m = (l + r) // 2
            if nums[m] < nums[m + 1]:
                l = m + 1
            else:
                r = m
        return l

线性扫描O(n):

若是当前元素m比前一元素m-1大的话,则继续向后搜索,若是小的话,说明前一元素m-1即为峰值,由于m-1以前的都比m-1小。

class Solution(object):
    def findPeakElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        for i in range(1,len(nums)):
            if nums[i] < nums[i - 1]:
                return i - 1
        return len(nums) - 1

 

 

参考连接:

http://blog.csdn.net/linhuanmars/article/details/20588511 

相关文章
相关标签/搜索