binary_search_special

二分法总结参考:
http://blog.csdn.net/ebowtang/article/details/50770315
精髓就是确定两个指针以及两个指针如何移动,注意二分法的一些变种。

33、Search 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).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        start, end = 0, len(nums) - 1
        while start <= end:
            mid = (start + end) / 2
            if nums[mid] == target:
                return mid
            if nums[mid] >= nums[start]:
                if target >= nums[start] and target < nums[mid]:
                    end = mid - 1
                else:
                    start = mid + 1
            else:
                if target > nums[mid] and target <= nums[end]:
                    start = mid + 1
                else:
                    end = mid - 1
        # if start == end and target != nums[start]:
        #     return -1
        return -1

有几个地方模糊,一是取等问题,三个地方都涉及到等号,还有就是对 start 和 end 重新赋值时 +- 1问题。

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = left + (right - left) // 2
            if nums[mid] == target:
                return mid

            if nums[mid] > nums[left]:
                if nums[left] <= target <= nums[mid]:
                    right = mid - 1
                else:
                    left = mid + 1
            elif nums[mid] < nums[left]:
                if nums[mid] <= target <= nums[right]:
                    left = mid + 1
                else:
                    right = mid - 1
            else:
                left += 1
        return -1

34. Search for a Range

Given an array of integers sorted in ascending order, find the starting and ending position of a given target value.
Your algorithm's runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].
For example,
Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].

这道题涉及到二分法中一些常用的变种,找到第一个与key值相等的元素下标以及找到最后一个与元素相等的元素下标。所以这道题的做法是运用两次二分法。

class Solution(object):
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        if nums == []:
            return [-1, -1]
        start = self.findfirst(nums, target)
        end = self.findlast(nums, target)
        return [start, end]

    def findfirst(self, nums, target):
        start, end = 0, len(nums) - 1
        while start + 1 < end:   # 如果直接是 start < end 会导致死循环
            mid = (start + end) / 2
            if nums[mid] < target:
                start = mid + 1
            else:
                end = mid
        if nums[start] == target:
            return start
        if nums[end] == target:
            return end
        return -1

    def findlast(self, nums, target):
        start, end = 0, len(nums) - 1
        while start + 1 < end:
            mid = (start + end) / 2
            if nums[mid] > target:
                end = mid - 1
            else:
                start = mid
        if nums[end] == target:
            return end
        if nums[start] == target:
            return start    
        return -1

对比下面方法:

int searchFirstEqual(int *arr, int n, int key)
{
    int left = 0, right = n-1;
    while(left < right)//根据两指针的意义,如果key存在于数组,left==right相等时已经得到解
    {
        int mid = (left+right)/2;
        if(arr[mid] > key)//一定在mid为止的左边,并且不包含当前位置
            right = mid - 1;
        else if(arr[mid] < key) 
            left = mid + 1;//一定在mid位置的右边,并且不包括当前mid位置
        else
            right=mid;
    }
    if(arr[left] == key) 
            return left;
    return -1;
}

int searchLastEqual(int *arr, int n, int key)
{
    int left = 0, right = n-1;
    while(left<right-1) {
        int mid = (left+right)/2;
        if(arr[mid] > key) 
            right = mid - 1;//key一定在mid位置的左边,并且不包括当前mid位置
        else if(arr[mid] < key) 
            left = mid + 1; //key一定在mid位置的右边,相等时答案有可能是当前mid位置
        else
            left=mid;//故意写得和参考博客不一样,见下面证明
    }
    if( arr[left]<=key && arr[right] == key) 
        return right;
    if( arr[left] == key && arr[right] > key)
        return left;
    return -1;
}

上面两种方法都易于理解,需要注意的有以下几点:循坏结束条件。判断循环结束条件关键要看指针移动情况。

1、 找到key值相等的元素

指针移动:end = mid - 1 和 start = mid + 1,还有一个判断条件,因为重新赋值都会有一个变化,因此start<=end不会死循环并且可以完整遍历。

2、找到第一个key值相等的元素

指针移动: start = mid + 1, end = mid,而mid是向下取整,当有一个取值没有变时,若循环条件是start<=end(最终判断数组为1个元素)就会进入死循环,因为一直赋值。而考虑start<end(最终判断数组是两个元素),符合避免死循环条件。但需要对这两个元素再次判断。

3、找到最后一个与key值相等的元素

指针移动: start = mid, end = mid - 1,关键还是要注意mid赋值是向下取整,如果start<end,则最后两个元素还是会进入死循环,因此条件为start + 1 < end。
还要注意溢出问题,一般不会写成mid = (start + end) / 2,而是mid = start + ( end - start ) / 2
其他解法:

class Solution(object):
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        # Find the first idx where nums[idx] >= target
        left = self.binarySearch(lambda x, y: x >= y, nums, target)
        if left >= len(nums) or nums[left] != target:
            return [-1, -1]
        # Find the first idx where nums[idx] > target
        right = self.binarySearch(lambda x, y: x > y, nums, target)
        return [left, right - 1]
    
    def binarySearch(self, compare, nums, target):
        left, right = 0, len(nums)
        while left < right:
            mid = left + (right - left) / 2
            if compare(nums[mid], target):
                right = mid
            else:
                left = mid + 1
        return left

    def binarySearch2(self, compare, nums, target):
        left, right = 0, len(nums) - 1
        while left <= right:
            mid = left + (right - left) / 2
            if compare(nums[mid], target):
                right = mid - 1
            else:
                left = mid + 1
        return left

    def binarySearch3(self, compare, nums, target):
        left, right = -1, len(nums)
        while left + 1 < right:
            mid = left + (right - left) / 2
            if compare(nums[mid], target):
                right = mid
            else:
                left = mid
        return left if left != -1 and compare(nums[left], target) else right

35、Search Insert Position

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You may assume no duplicates in the array.

Example 1:

Input: [1,3,5,6], 5
Output: 2
Example 2:

Input: [1,3,5,6], 2
Output: 1
Example 3:

Input: [1,3,5,6], 7
Output: 4

题意本质为找到第一个大于或等于该key的元素位置。

class Solution(object):
    def searchInsert(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        start, end = 0, len(nums) - 1
        while start < end:
            mid = start + (end - start) / 2
            if nums[mid] < target:
                start = mid + 1
            else:
                end = mid
        if nums[start] >= target:
            return start
        if nums[end] >= target:
            return end
        else:
            return end + 1

74、 Search a 2D Matrix

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
Integers in each row are sorted from left to right.
The first integer of each row is greater than the last integer of the previous row.

For example,
Consider the following matrix:
[
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
Given target = 3, return true.
class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        row = len(matrix)
        if row == 0:
            return False
        cow = len(matrix[0])
        if cow == 0:
            return False
        temp = []
        for i in xrange(row):
            temp.append(matrix[i][cow-1])
        print temp
        res = self.first_not_less_than(temp, target)
        print res
        if res is True:
            return True
        elif res is False:
            return False
        else:
            return self.first_not_less_than(matrix[res], target) is True

    def first_not_less_than(self, nums, target):
        start, end = 0, len(nums) - 1
        while start < end:
            mid = start + (end - start) / 2
            if nums[mid] < target:
                start = mid + 1
            else:
                end = mid
        if nums[start] == target:
            return True
        elif nums[start] > target:
            return start
        else:
            return False


if __name__ == '__main__':
    s = Solution()
    print s.searchMatrix([[1,3,5,7],[10,11,16,20],[23,30,34,50]],3)

需要注意两个问题,一是

row = len(matrix)
        if row == 0:
            return False
        cow = len(matrix[0])
        if cow == 0:
            return False

不能直接一步写成 row, cow = len(matrix), len(matrix[0]),然后判断。
二是注意 res == True 和 res is True的区别,本题中不能用 res == True.
==只是判断两者的值是不是相等,而True代表的值是1,False代表0,is就是要求两者结构必须一样。

81、 Search in Rotated Sorted Array II

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).
Write a function to determine if a given target is in the array.
The array may contain duplicates.

这道题是33题的变种,需要多考虑一种情况,即中点和边界值相等。

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: bool
        """
        if nums == []:
            return False
        start, end = 0, len(nums) - 1
        while start < end:
            mid = start + (end - start) / 2
            if nums[mid] == target:
                return True
            if nums[mid] > nums[start]:
                if nums[mid] >= target and nums[start] <= target:
                    end = mid
                else:
                    start = mid + 1
            elif nums[mid] < nums[start]:
                if nums[mid] <= target and nums[end] >= target:
                    start = mid
                else:
                    end = mid - 1
            else:
                start += 1
        if nums[start] == target or nums[end] == target:
            return True
        return False

153. Find Minimum in Rotated Sorted Array

在一个旋转后的数组中找到最小的元素。这里有一点需要注意,就是将中点值与end处值进行比较更方便,如果是和start值进行比较处理较麻烦。

class Solution(object):
    def findMin(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        start, end = 0, len(nums) - 1
        while start + 1 < end:
            mid = start + (end - start) / 2
            if nums[mid] > nums[end]:
                start = mid + 1
            else:
                end = mid

        return min(nums[start], nums[end])

162、 Find Peak 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.

很多解法都说用二分法,但由于序列是无序的,用二分法的依据在于:

首先我们找到中间节点mid,如果大于两边返回当前的index就可以了,如果左边的节点比mid大,那么我们可以继续在左半区间查找,这里面一定存在一个peak,为什么这么说呢?假设此时的区间范围为[0,mid-1],因为num[mid-1]一定大于num[mid],如果num[mid-2]<=num[mid-1],那么num[mid-1]就是一个peak。如果num[mid-2]>num[mid-1],那么我们就继续在[0,mid-2]区间查找,因为num[-1]为负无穷,所以我们最终绝对能在左半区间找到一个peak。同理右半区间一样。

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

还是要多理解,没有看起来这么简单。

79、 Two Sum II - Input array is sorted

class Solution(object):
    def twoSum(self, numbers, target):
        """
        :type numbers: List[int]
        :type target: int
        :rtype: List[int]
        """
        for i in xrange(len(numbers) - 1):
            if numbers[i] > target:
                break
            temp = self.binary_search(numbers, target - numbers[i], i+1)
            if temp != -1:
                return [i+1, temp+1]
        return [-1, -1]

    def binary_search(self, nums, target, start):
        end =len(nums) - 1
        while start <= end:
            mid = start + (end - start) / 2
            if nums[mid] == target:
                return mid
            if nums[mid] > target:
                end = mid - 1
            if nums[mid] < target:
                start = mid + 1
        return -1

209、Minimum Size Subarray Sum (滑动窗口)

Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn't one, return 0 instead.
For example, given the array [2,3,1,2,4,3] and s = 7,
the subarray [4,3] has the minimal length under the problem constraint

可以用滑动窗口思想去求解。

class Solution(object):
    def minSubArrayLen(self, s, nums):
        """
        :type s: int
        :type nums: List[int]
        :rtype: int
        """
        sum, left = 0, 0
        res = float('inf')
        for i in xrange(len(nums)):
            sum += nums[i]
            while sum >= s:
                res = min(res, i-left+1)
                sum -= nums[left]
                left += 1
        return 0 if res == float('inf') else res

这种方法接触的还是比较少,需要进一步掌握理解。

278、First Bad Version

You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad.
Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad.
You are given an API bool isBadVersion(version) which will return whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.

class Solution(object):
    def firstBadVersion(self, n):
        """
        :type n: int
        :rtype: int
        """
        start, end = 1, n
        while start < end:
            mid = start + (end - start) / 2
            if isBadVersion(mid):
                end = mid
            else:
                start = mid + 1
        return start

275、 H-Index II

Follow up for H-Index: What if the citations array is sorted in ascending order? Could you optimize your algorithm?

class Solution(object):
    def hIndex(self, citations):
        """
        :type citations: List[int]
        :rtype: int
        """
        length, res = len(citations), 0
        start, end = 0, length - 1
        while start <= end:
            mid = start + (end - start) / 2
            if length - mid <= citations[mid]:
                res = max(length - mid,res)
                end = mid - 1
            else:
                start = mid + 1
        return res

简化:

    def hIndex1(self,citations):
        length, res = len(citations), 0
        start, end = 0, length - 1
        while start <= end:
            mid = start + ((end - start) >> 1)
            if length - mid <= citations[mid]:
                end = mid - 1
            else:
                start = mid + 1
        return length - start

287、 Find the Duplicate Number

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
Note:
You must not modify the array (assume the array is read only).
You must use only constant, O(1) extra space.
Your runtime complexity should be less than O(n2).
There is only one duplicate number in the array, but it could be repeated more than once.

class Solution(object):
    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        length = len(nums)
        start, end = 0, length - 1
        nums = sorted(nums)
        while start < end:
            mid = start + (end - start) / 2
            if nums[mid] >= mid + 1:
                start = mid + 1
            else:
                end = mid
        return start

上面二分法的思路不符合题意,要求不能修改数组。修改为:

class Solution(object):
    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        length = len(nums)
        start, end = 0, length - 1
        while start <= end:
            mid = start + (end - start) / 2
            count = 0
            for item in nums:
                if item <= mid:
                    count += 1
            if count > mid:
                end = mid - 1
            else:
                start = mid + 1
        return start

另一种思路,利用循环链表

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容