coding刷题总结-二分法类型题目-使用python语言-由易到难,梯度设置合理

  1. 本文整理了Lintcode二分法题目的coding练习,参考了九章算法课程的配套习题,题目顺序由易到难,非常典型。算法入门的同学可以按照这个题目顺序进行练习。
  2. 代码都是用python实现,基本上是参考了九章用的解法,如果发现有错误或者不理解的地方,可以留言告诉我。
  3. 体会:二分法题目基本就是找满足某个条件的first position或者last position问题。怎么将题目转化成这种问题就是考验是否聪明了,勤能补拙,不够聪明的话,多练习,见多识广也行。
  4. 目前在持续更新中

457. 经典二分查找问题

在一个排序数组中找一个数,返回该数出现的任意位置,如果不存在,返回 -1

class Solution:
    """
    @param nums: An integer array sorted in ascending order
    @param target: An integer
    @return: An integer
    """
    def findPosition(self, nums, target):
        # write your code here
        # 二分查找问题最简单的形式,不必考虑first postion还是last position了
        if len(nums) == 0:
            return -1
        start = 0
        end = len(nums) - 1 
        while start + 1 < end:
            mid = start + (end - start) // 2
            if nums[mid] == target:
                 return mid  # 体会这里和14题的区别
            if nums[mid] > target:
                end = mid
            if nums[mid] < target:
                start = mid
        if nums[start] == target:
            return start
        if nums[end] == target:
            return end
        return -1

二刷

题目很简单,和一刷时代码一样

三刷,经典的二分题目,要背住模板

  1. 先判断异常情况,即数组为空
  2. while 循环条件是start + 1 < end:
  3. start, end初始化是0, n - 1
  4. 对于其它题目,再留意一下是要first position 还是last position

14. 二分查找

给定一个排序的整数数组(升序)和一个要查找的整数target,用O(logn)的时间查找到target第一次出现的下标(从0开始),如果target不存在于数组中,返回-1。

class Solution:
    """
    @param nums: The integer array.
    @param target: Target to find.
    @return: The first position of target. Position starts from 0.
    """
    def binarySearch(self, nums, target):
        # write your code here
        # 典型的二分查找题目,应该是最简单的了
        # first positon 
        start = 0
        end = len(nums) - 1 
        while start + 1 < end:
            mid = start + (end - start) // 2
            if nums[mid] == target:
                end = mid
            if nums[mid] > target:
                end = mid
            if nums[mid] < target:
                start = mid
        if nums[start] == target:
            return start
        if nums[end] == target:
            return end
        return -1

二刷

典型的二分查找题目,注意是要target第一次出现的下标。二刷代码和一刷的没什么不同。加深了对二分法题目的理解。

三刷,典型题目,再熟悉一下模板

141. 对x开根

实现 int sqrt(int x) 函数,计算并返回 x 的平方根。

class Solution:
    """
    @param x: An integer
    @return: The sqrt of x
    """
    def sqrt(self, x):
        # write your code here
        # 要灵活运用二分法,看这个题目怎么转化成二分查找的思路
        # 寻找最后一个数y使得y * y <= x 
        # last position问题
        start = 0
        end = x
        while start + 1 < end:
            mid = start + (end - start) // 2
            if mid * mid == x:
                return mid
            if mid * mid > x:
                end = mid
            if mid * mid < x:
                start = mid
        
        if end * end <= x:
            return end
        if start * start <= x:
            return start

二刷

    def sqrt(self, x):
        # write your code here
        # 这是二刷了
        # 这个题目就不是那么典型的二分了,思路不是那么直接
        # x的平方根肯定是在0和x之间,所以在0-x之间二分,不断缩小范围找到square root
        # 找到最大的square root,使得square root * square root <= x
        # last position问题
        if x < 0:
            return -1

        start = 0
        end = x 
        while start + 1 < end:
            mid = start + (end - start) // 2
            if mid * mid == x:
                return mid
            if mid * mid < x:
                start = mid
            if mid * mid > x:
                end = mid
        if end * end <= x:
            return end
        if start * start <= x:
            return start 
        return -1

三刷,最后的判断那里不是很熟练

力扣240. 搜索二维矩阵 II

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

每行的元素从左到右升序排列。
每列的元素从上到下升序排列。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/search-a-2d-matrix-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        m, n = len(matrix), len(matrix[0])

        # for i in range(m):
        #     for j in range(n):
        #         if matrix[i][j] == target:
        #             return True 
        for i in range(m):
            if self.searchNums(matrix[i], target):
                return True 

        return False 

    def searchNums(self, nums, target):
        start, end = 0, len(nums) - 1

        while start + 1 < end:
            mid = start + (end - start) // 2
            if nums[mid] == target:
                return True 
            elif nums[mid] > target:
                end = mid
            else:
                start = mid 

        if nums[start] == target or nums[end] == target:
            return True 
        
        return False 

二刷,和一刷解法一样,没有充分利用该矩阵的性质

28. 搜索二维矩阵

写出一个高效的算法来搜索 m × n矩阵中的值。
这个矩阵具有以下特性:
每行中的整数从左到右是排序的。
每行的第一个数大于上一行的最后一个整数。

class Solution:
    """
    @param matrix: matrix, a list of lists of integers
    @param target: An integer
    @return: a boolean, indicate whether matrix contains target
    """
    def searchMatrix(self, matrix, target):
        # write your code here
        # 这个二位矩阵是递增的,不知道是否有重复的数。不过当成first position来做就行
        # 可以把二维矩阵转化成一维数组来做,这样就是自己熟悉的题目了
        # m*n的二位矩阵,在一维数组中的index // n就是行数,index % n就是列数
        m, n = len(matrix), len(matrix[0])
        start = 0
        end = m * n -1 
        while start + 1 < end:
            mid = start + (end - start) // 2
            if matrix[mid // n][mid % n] == target:
                return True
            if matrix[mid // n][mid % n] > target:
                end = mid
            if matrix[mid // n][mid % n] < target:
                start = mid
        if matrix[start // n][start % n] == target:
            return True
        if matrix[end // n][end % n] == target:
            return True
        return False

二刷,加深理解了怎么将二维数组转为以为数组

class Solution:
    """
    @param matrix: matrix, a list of lists of integers
    @param target: An integer
    @return: a boolean, indicate whether matrix contains target
    """
    def searchMatrix(self, matrix, target):
        # write your code here
        if len(matrix) == 0:
            return False 

        m, n = len(matrix), len(matrix[0])
        start, end = 0, m * n - 1 
        while start + 1 < end:
            mid = start + (end - start) // 2
            row = mid // n
            col = mid % n 
            if matrix[row][col] == target:
                return True 
            if matrix[row][col] < target:
                start = mid
            if matrix[row][col] > target:
                end = mid 

        if matrix[start // n][start % n] == target:
            return True 
        if matrix[end // n][end % n] == target:
            return True 

        return False 

61. 搜索区间

给定一个包含 n 个整数的排序数组,找出给定目标值 target 的起始和结束位置。
如果目标值不在数组中,则返回[-1, -1]

class Solution:
    """
    @param A: an integer sorted array
    @param target: an integer to be inserted
    @return: a list of length 2, [index1, index2]
    """
    def searchRange(self, A, target):
        # write your code here
        # 起始位置是first position,结束位置是last position
        # 这是一个排序数组,可能有重复数字
        # 可以简化成先找first position当成左边界,再找last position当成右边界
        start = 0
        end = len(A) - 1 
        # 什么时候要考虑数组为空,什么时候不考虑呢??
        if A == []:
            return [-1, -1]
        
        while start + 1 < end:
            mid = start + (end - start) // 2 
            if A[mid] == target:
                end = mid
            if A[mid] > target:
                end = mid
            if A[mid] < target:
                start = mid
        if A[start] == target:
            leftbound = start
        elif A[end] == target:
            leftbound = end
        else:
            return [-1, -1]
        
        start = leftbound
        end = len(A) - 1 # 这里也要重新对end赋值
        while start + 1 < end:
            mid = start + (end - start) // 2 
            if A[mid] == target:
                start = mid
            if A[mid] > target:
                end = mid
            if A[mid] < target:
                start = mid
        if A[end] == target:
            rightbound = end
        elif A[start] == target:
            rightbound = start
            
        return [leftbound, rightbound]

二刷,基本掌握

458. 目标最后位置

给一个升序数组,找到 target 最后一次出现的位置,如果没出现过返回 -1

样例 1:
输入:nums = [1,2,2,4,5,5], target = 2
输出:2

样例 2:
输入:nums = [1,2,2,4,5,5], target = 6
输出:-1

class Solution:
    """
    @param nums: An integer array sorted in ascending order
    @param target: An integer
    @return: An integer
    """
    def lastPosition(self, nums, target):
        # write your code here
        # last position问题,升序数组,可能有重复
        start = 0
        end = len(nums) - 1
        
        if nums == None or len(nums) == 0:
            return -1
        
        while start + 1 < end:
            mid = start + (end - start) // 2
            # last position问题,先缩小范围
            if nums[mid] <= target:
                start = mid
            else:
                end = mid
        # 先判断end,等于target的话,返回索引。
        if nums[end] == target:
            return end
        if nums[start] == target:
            return start
        return -1

二刷

典型的last position问题,心里思考了一下,和一刷的思路和代码基本一样。

585. 山脉序列中的最大值

给 n 个整数的山脉数组,即先增后减的序列,找到山顶(最大值)

例1:
输入: nums = [1, 2, 4, 8, 6, 3]
输出: 8

例2:
输入: nums = [10, 9, 8, 7],
输出: 10
注意事项
数组严格递增,严格递减

class Solution:
    """
    @param nums: a mountain sequence which increase firstly and then decrease
    @return: then mountain top
    """
    def mountainSequence(self, nums):
        # write your code here
        # 不是first/last position问题了,通过二分逐步缩小范围
        # 长度应该是默认大于等于3的
        start = 0
        end = len(nums) - 1
        
        while start + 1 < end:
            mid = start + (end - start) // 2
            # nums[mid]恰好是最大的情况
            if nums[mid] > nums[mid-1] and nums[mid] > nums[mid+1]:
                return nums[mid]
            # nums[mid]是左边最大的情况
            elif nums[mid] > nums[mid-1] and nums[mid] < nums[mid+1]:
                start = mid
            # nums[mid]是右边最大的情况
            elif nums[mid] > nums[mid+1] and nums[mid] < nums[mid-1]:
                end = mid
        # 考虑只有递增或者只有递减的情况
        if nums[start] > nums[end]:
            return nums[start]
        else:
            return nums[end]

二刷

忘记了一刷时的思路,自己独立想出来的,和一刷时的基本一样,但是更清晰了。

    def mountainSequence(self, nums):
        # write your code here
        # 这是二刷,可以用遍历整个数组来找到最大值,
        # 但是用二分法怎么做呢?
        # 思考题目,山顶的特点是比左边的值大,也比右边的值大
        # 如果找到mid,满足这个特点,那么就是山顶了
        if len(nums) == 0 or nums == None:
            return None 

        start = 0
        end = len(nums) - 1
        while start + 1 < end:
            mid = start + (end - start) // 2
            # mid恰好是山峰的情况
            if nums[mid] >= nums[mid + 1] and nums[mid] >= nums[mid - 1]:
                return nums[mid]
            # mid在左边山坡的情况
            if nums[mid] >= nums[mid - 1] and nums[mid] <= nums[mid + 1]:
                start = mid
            # mid在右边山坡的情况
            if nums[mid] <= nums[mid - 1] and nums[mid] >= nums[mid + 1]:
                end = mid
        if nums[start] > nums[end]:
            return nums[start]
        else:
            return nums[end]

三刷,代码更简洁了

class Solution:
    """
    @param nums: a mountain sequence which increase firstly and then decrease
    @return: then mountain top
    """
    def mountainSequence(self, nums):
        # write your code here
        start, end = 0, len(nums) - 1
        while start + 1 < end:
            mid = start + (end - start) // 2
            if nums[mid] < nums[mid + 1]:
                start = mid
            else:
                end = mid

        return nums[start] if nums[start] > nums[end] else nums[end]

460. 在排序数组中找最接近的K个数

给一个目标数 target, 一个非负整数 k, 一个按照升序排列的数组 A。在A中找与target最接近的k个整数。返回这k个数并按照与target的接近程度从小到大排序,如果接近程度相当,那么小的数排在前面。

样例 1:
输入: A = [1, 2, 3], target = 2, k = 3
输出: [2, 1, 3]

样例 2:
输入: A = [1, 4, 6, 8], target = 3, k = 3
输出: [4, 1, 6]

class Solution:
    """
    @param A: an integer array
    @param target: An integer
    @param k: An integer
    @return: an integer array
    """
    def kClosestNumbers(self, A, target, k):
        # write your code here
        # 自己想是没有想出来,看了答案以后觉得也很简单。
        # 采用二分法加双指针,用二分法确定一个位置,左边是<target的,右边是>=target的
        # 然后用两根指针从这个位置向两边走,依次找到最接近的K个数
        
        # 找到最接近target的两个数,A[right] >= target,A[left] < target
        right = self.findUpperClosest(A, target)
        left = right - 1
        
        # 两根指针从中间向两边走,依次找到最接近的K个数
        res = []
        for _ in range(k):
            if self.isLeftCloser(A, target, left, right):
                res.append(A[left])
                left = left - 1
            else:
                res.append(A[right])
                right = right + 1
        return res
    # 找到>=target的第一个数的位置,first position
    def findUpperClosest(self, A, target):
        start = 0
        end = len(A) - 1
        while start + 1 < end:
            mid = start + (end - start) // 2
            if A[mid] >= target:
                end = mid
            else:
                start = mid
        if A[start] >= target:
            return start
        if A[end] >= target:
            return end
        # 找不到的情况,即target比A中的任何数都大。
        return len(A)
            
    def isLeftCloser(self, A, target, left, right):
        if left < 0:
            return False
        if right >= len(A):
            return True
        return target - A[left] <= A[right] - target

二刷

二刷的时候完全没有思路,感觉很难。最后自己静静心,认真分析题目的条件,把问题拆解开,一步一步做出来了,好开心。

def kClosestNumbers(self, A, target, k):
        # write your code here
        # 这是二刷了
        # 二刷的时候,简单的题目基本都没有问题,
        # 中等难度的题目,有的思考一下可以做出来,非常有成就感
        # 有的完全没有思路,想了半天也不行,还得看答案,非常有挫败感
        # 这个题目可能是属于后者,怕怕。。。
        # 审题,思考。先用二分法找到与target最接近的那个数
        # 然后依次比较其左边和右边的数,看哪个更接近target
        # 最后经过自己的思考,独立做出来了。好开心!!!
        # 而且感觉我的思路比答案的更清晰
        if len(A) == 0 or A == None:
            return None 
        if k == 0:
            return []
        index = self.closestNumber(A, target)
        left = index - 1
        right = index + 1
        res = [A[index]]
        for i in range(k - 1):
            if left >= 0 and right <= len(A) - 1:
                if abs(A[left] - target) <= abs(A[right] - target):
                    res.append(A[left])
                    left -= 1
                else:
                    res.append(A[right])
                    right += 1
            elif left < 0 and right <= len(A) - 1:
                res.append(A[right])
                right += 1
            elif left >= 0 and right > len(A) - 1:
                res.append(A[left])
                left -= 1   
        return res  

    def closestNumber(self, A, target):
        # 注意要的是first position
        start = 0
        end = len(A) - 1
        while start + 1 < end:
            mid = start + (end - start) // 2
            if A[mid] == target:
                return mid
            if A[mid] > target:
                end = mid
            if A[mid] < target:
                start = mid
        if abs(A[start] - target) <= abs(A[end] - target):
            return start 
        else:
            return end

三刷,时间复杂度是O(n),空间复杂度是O(n)

class Solution:
    """
    @param A: an integer array
    @param target: An integer
    @param k: An integer
    @return: an integer array
    """
    def kClosestNumbers(self, A, target, k):
        # write your code here
        tmp = []
        for index, item in enumerate(A):
            tmp.append((abs(item - target), index))

        tmp.sort()
        res = []
        for i in range(k):
            index = tmp[i][1]
            res.append(A[index])

        return res 

没有想出来二分法这个方法,看了答案后也可以理解,思路也非常好,时间复杂度是O(logn),空间复杂度是O(1)。自己尝试做一做。

class Solution:
    """
    @param A: an integer array
    @param target: An integer
    @param k: An integer
    @return: an integer array
    """
    def kClosestNumbers(self, A, target, k):
        # write your code here
        # 用二分法找到最接近的那一个数
        # 然后从这个数分别向两边移动,依次找到次接近的数
        if len(A) == 0:
            return []
        if len(A) < k:
            return []
        if k == 0:
            return []
        
        index = self.closestNumber(A, target)
        res = [A[index]]
        k = k - 1
        left = index - 1
        right = index + 1

        # for _ in range(k - 1):
        while left >= 0 and right <= len(A) - 1 and k > 0:
            if abs(A[left] - target) <= abs(A[right] - target):
                res.append(A[left])
                left -= 1
                k -= 1
            else:
                res.append(A[right])
                right += 1
                k -= 1

        while k > 0 and left >= 0:
            res.append(A[left])
            left -= 1
            k -= 1
        while k > 0 and right <= len(A) - 1:
            res.append(A[right])
            right += 1
            k -= 1

        return res 

    def closestNumber(self, A, target):
        if len(A) == 0:
            return None 

        start, end = 0, len(A) - 1

        while start + 1 < end:
            mid = start + (end - start) // 2
            if A[mid] == target:
                end = mid 
            elif A[mid] < target:
                start = mid 
            else:
                end = mid 
        
        if abs(A[start] - target) <= abs(A[end] - target):
            return start 
        return end 

428. x的n次幂

实现 pow(x, n). (n是一个整数)

样例 1:
输入: x = 9.88023, n = 3
输出: 964.498

样例 2:
输入: x = 2.1, n = 3
输出: 9.261

样例 3:
输入: x = 1, n = 0
输出: 1

class Solution:
    """
    @param x {float}: the base number
    @param n {int}: the power number
    @return {float}: the result
    """
    def myPow(self, x, n):
        # write your code here
        
        # 注意考虑n为负数的情况。如果n为负数,令x = 1/x,n = -n
        if n < 0:
            x = 1 / x
            n = -n
        ans = 1
        tmp = x
        while n != 0:
            if n % 2 == 1:
                ans = ans * tmp
            tmp = tmp * tmp
            n = n // 2
        return ans

二刷

先是尝试暴力法,即依次进行n个x相乘,时间复杂度是O(n),这样是会超时的。
然后将问题转化为x的m次幂和x的n次幂,并且记住已经计算过的答案,不用再次计算。这样可以通过。
不过这都没有一刷的解法好。

class Solution:
    """
    @param x {float}: the base number
    @param n {int}: the power number
    @return {float}: the result
    """
    def myPow(self, x, n):
        # write your code here
        # if n == 0:
        #     return 1
        # if n > 0:
        #     tmp = 1
        #     for _ in range(n):
        #         tmp = tmp * x
            
        #     return tmp
 
        # if n < 0:
        #     n = -n 
        #     tmp = 1
        #     for _ in range(n):
        #         tmp = tmp * x
            
        #     return 1 / tmp

        memo = {}

        return self.help(x, n, memo)
    
    def help(self, x, n, memo):
        if n == 0:
            return 1
        if n == 1:
            return x 
        if n in memo:
            return memo[n]

        if n > 0:
            left = n // 2
            right = n - left
            res = self.help(x, left, memo) * self.help(x, right, memo)
            memo[n] = res 

            return res 

        if n < 0:
            n = -n 
            return 1 / self.help(x, n, memo)

159. 寻找旋转排序数组中的最小值

假设一个排好序的数组在其某一未知点发生了旋转(比如0 1 2 4 5 6 7 可能变成4 5 6 7 0 1 2)。你需要找到其中最小的元素。

样例 1:
输入:[4, 5, 6, 7, 0, 1, 2]
输出:0
解释:
数组中的最小值为0

样例 2:
输入:[2,1]
输出:1
解释:
数组中的最小值为1

注意事项:你可以假设数组中不存在重复元素。

class Solution:
    """
    @param nums: a rotated sorted array
    @return: the minimum number in the array
    """
    def findMin(self, nums):
        # write your code here
        # 题目也没写啥是旋转数组,自己瞎理解了好久。。。
        # 看到leetcode上的定义:
        # 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转
        # 根据定义可以得出旋转后的数组先升序,然后是一个最小值,再继续升序
        # 理解到这一步,最小值左边的数都比它大,右边的数也都比它大
        start = 0
        end = len(nums) - 1
        # 居然有不发生旋转的情况,题目都说了是旋转数组,我也是醉了。
        # 判断是否进行了旋转,如果进行了旋转再进行while循环
        if nums[start] > nums[end]:
            while start + 1 < end:
                mid = start + (end - start) // 2
                if nums[mid] > nums[end]:
                    start = mid
                if nums[mid] < nums[end]:
                    end = mid
        
        if nums[start] < nums[end]:
            return nums[start]
        if nums[start] > nums[end]:
            return nums[end]              

二刷,掌握得不好,没有充分理解旋转数组的形状

75. 寻找峰值

给出一个整数数组(size为n),其具有以下特点:
相邻位置的数字是不同的
A[0] < A[1] 并且 A[n - 2] > A[n - 1]
假定P是峰值的位置则满足A[P] > A[P-1]且A[P] > A[P+1],返回数组中任意一个峰值的位置。

样例 1:
输入: [1, 2, 1, 3, 4, 5, 7, 6]
输出: 1 or 6
解释:
返回峰顶元素的下标

样例 2:
输入: [1,2,3,4,1]
输出: 3

注意事项
数组保证至少存在一个峰
如果数组存在多个峰,返回其中任意一个就行
数组至少包含 3 个数

class Solution:
    """
    @param A: An integers array.
    @return: return any of peek positions.
    """
    def findPeak(self, A):
        # write your code here
        # 和LeetCode上的题目描述一对比,这个啰嗦极了。
        # 下面这句话似乎也是错的,理解不了。。。
        # A[0] < A[1] 并且 A[n - 2] > A[n - 1]
        
        # 峰值元素比它左边的数大,也比它右边的数大。至少存在一个峰值元素。
        # 理解到这一层的话,只要沿着一个上升的线找,就一定可找到峰值
        # 计算中间位置mid,并比较A[mid]和A[mid+1]的值,
        # 如果A[mid]大,说明左侧有峰值,end=mid
        start = 0
        end = len(A) - 1 
        while start + 1 < end:
            mid = start + (end - start) // 2
            if A[mid] > A[mid+1]:
                end = mid
            if A[mid] < A[mid+1]:
                start = mid
        return end

二刷,不如一刷代码简洁

class Solution:
    """
    @param A: An integers array.
    @return: return any of peek positions.
    """
    def findPeak(self, A):
        # write your code here
        # 用二分来实现,时间复杂度低
        if len(A) < 3:
            return -1 
        
        start, end = 0, len(A) - 1
        while start + 1 < end:
            mid = start + (end - start) // 2
            if A[mid] > A[mid - 1] and A[mid] > A[mid + 1]:
                return mid 
            elif A[mid] > A[mid - 1]:
                start = mid 
            else:
                end = mid 

        if A[start] > A[start + 1] and A[start] > A[start - 1]:
            return start
        elif A[end] > A[end + 1] and A[end] > A[end - 1]:
            return end
        return -1

74. 第一个错误的代码版本

代码库的版本号是从 1 到 n 的整数。某一天,有人提交了错误版本的代码,因此造成自身及之后版本的代码在单元测试中均出错。请找出第一个错误的版本号。

#class SVNRepo:
#    @classmethod
#    def isBadVersion(cls, id)
#        # Run unit tests to check whether verison `id` is a bad version
#        # return true if unit tests passed else false.
# You can use SVNRepo.isBadVersion(10) to check whether version 10 is a 
# bad version.
class Solution:
    """
    @param n: An integer
    @return: An integer which is the first bad version.
    """
    def findFirstBadVersion(self, n):
        # write your code here
        start = 1  
        end = n
        while start + 1 < end:
            mid = start + (end - start) // 2
            if SVNRepo.isBadVersion(mid):
                end = mid
            else: 
                start = mid
        if SVNRepo.isBadVersion(start):
            return start
        if SVNRepo.isBadVersion(end):
            return end 

62. 搜索旋转排序数组

假设有一个排序的按未知的旋转轴旋转的数组(比如,0 1 2 4 5 6 7 可能成为4 5 6 7 0 1 2)。给定一个目标值进行搜索,如果在数组中找到目标值返回数组中的索引位置,否则返回-1。你可以假设数组中不存在重复的元素。

class Solution:
    """
    @param A: an integer rotated sorted array
    @param target: an integer to be searched
    @return: an integer
    """
    def search(self, A, target):
        # write your code here
        # 我本来考虑先找到最小值的位置,然后判断target在最小值左边还是右边,
        # 然后在从二分法来搜索target,但是觉得这样太麻烦了,不是一个好办法
        # 看了答案以后,发现九章给的方法也是这样
        # 体会是:做事情先完成再完美,有解决方案比没有强
        # 不要有洁癖,先完成再说
        if not A:
            return -1      
        min, minIdx = self.findMin(A)
        if A[minIdx] <= target <= A[-1]:
            return self.binarySearch(A, minIdx, len(A)-1, target)
        return self.binarySearch(A, 0, minIdx-1, target)
        
    def binarySearch(self, A, start, end, target):
        while start + 1 < end:
            mid = start + (end - start) // 2
            if A[mid] > target:
                end = mid
            if A[mid] < target:
                start = mid
            if A[mid] == target:
                return mid
        if A[start] == target:
            return start
        if A[end] == target:
            return end 
        return -1
        
    def findMin(self, A):
        # A是一个旋转排序数组,找到里面的最小值,返回这个值和索引
        # 这和之前的159题目类似,题目顺序设置的真是好呀
        start = 0
        end = len(A) - 1 
        # 先判断数组是否旋转了,不旋转的话第一个数就是最小值
        if A[start] > A[end]:
            while start + 1 < end:
                mid = start + (end - start) // 2
                if A[mid] > A[start]:
                    start = mid
                if A[mid] < A[end]:
                    end = mid
        if A[start] < A[end]:
            return A[start], start
        return A[end], end

160. 寻找旋转排序数组中的最小值 II

假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2)。你需要找到其中最小的元素。

Example 1:
Input :[2,1]
Output : 1.

Example 2:
Input :[4,4,5,6,7,0,1,2]
Output : 0.
注意事项:该数组可能包含重复项。

class Solution:
    """
    @param nums: a rotated sorted array
    @return: the minimum number in the array
    """
    def findMin(self, nums):
        # write your code here
        # 刚刚做了这个类似的题目,不同点是没有重复项。
        # 这个题目说可能存在重复项,会有哪些影响呢?
        # 不太清楚,按照二分的思路先做做看
        # 这个题目的难点在于想到最坏的情况[1, 1, 1, 1, 1, 1, 1, 1, 0, 1]类似这样
        start = 0
        end = len(nums) - 1
        
        while start + 1 < end:
            mid = start + (end - start) // 2
            if nums[mid] == nums[end]:
                end = end - 1 # 两个相等,去掉最后一个数,也不妨碍找到最小值
            if nums[mid] > nums[end]:
                start = mid
            if nums[mid] < nums[end]:
                end = mid
        if nums[start] <= nums[end]:
            return nums[start]
        else:
            return nums[end]
class Solution:
    """
    @param nums: a rotated sorted array
    @return: the minimum number in the array
    """
    def findMin(self, nums):
        # write your code here
        # 题目也没说要O(logn)
        # 其实不妨就直接用O(n)的方法来解决,不要有方法洁癖
        min = nums[0]
        for item in nums:
            if item < min:
                min = item
        return min

63. 搜索旋转排序数组 II

跟进“搜索旋转排序数组”,假如有重复元素又将如何?
是否会影响运行时间复杂度?
如何影响?
为何会影响?
写出一个函数判断给定的目标值是否出现在数组中。
例1:
输入:
[]
1
输出:
false

例2:
输入:
[3,4,4,5,7,0,1,2]
4
输出:
true

class Solution:
    """
    @param A: an integer ratated sorted array and duplicates are allowed
    @param target: An integer
    @return: a boolean 
    """
    def search(self, A, target):
        # write your code here
        # 如果没有重复元素的话,可以用两个二分法来实现
        # 现在这种情况,不如直接for循环吧,O(n)的时间复杂度
        # 但是这个方法估计不能让面试官满意。。。
        for item in A:
            if item == target:
                return True
        return False
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值