zoukankan      html  css  js  c++  java
  • 【Leetcode】二分系列

    【规律总结】

    1.l<r与l<=r的区别:

      中间记录结果用l<=r,最后返回结果用l<r此时l=r

    2.什么时候l和r取mid,什么时候都不取mid:

      2.1 取mid意思为:移动时l = mid,r=mid+1(或者l=mid+1,r=mid),不取意思为移动时l=mid+1,r=mid-1

      2.2 当mid已经确定不可能为所求值或者已经被记录过时不取(如题目【Leetcode-33】、【Leetcode-34】、【Leetcode-35】、【Leetcode-69】);

        否则取(如题目【Leetcode-剑指offer11】、【Leetcode-162】、【Leetcode-287】)

    3.一般用l<r时取mid,l<=r不取mid

    【Leetcode-剑指offer11】

    一、题目:旋转数组的最小数字

      把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。  

      注意:包含重复数字

    二、代码:

    def minArray(self, numbers: List[int]) -> int:
            """
            比最右边大,则往右走,比最右边小则往左走,相等则右-1。主要关注有重复值的情况
            """
            if numbers[-1] > numbers[0]:
                return numbers[0]
            n = len(numbers)
            l, r = 0, n-1
            while l < r:
                mid = (l+r) // 2
                if numbers[mid] > numbers[r]:
                    l = mid + 1
                elif numbers[mid] < numbers[r]:
                    r = mid
                else:
                    r -= 1
            return numbers[l]

    【Leetcode-33】

    一、题目:搜索旋转排序数组

      整数数组 nums 按升序排列,数组中的值 互不相同 。

      在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

      给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的索引,否则返回 -1 。

    二、代码:

    class Solution:
        def search(self, nums: List[int], target: int) -> int:
            index = -1
            if len(nums) == 0:
                return index
            l, r = 0, len(nums)-1
            while l <= r:
                middle = (l+r)//2
                if nums[middle] == target:
                    return middle
                elif nums[middle] >= nums[0]:  # 左升序
                    if nums[l] <= target < nums[middle]:
                        r = middle - 1
                    else:
                        l = middle + 1
                else:  # 右升序
                    if nums[middle] < target <= nums[r]:
                        l = middle + 1
                    else:
                        r = middle - 1
            return index

    【Leetcode-34】

    一、题目:在排序数组中查找元素的第一个和最后一个位置

      给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

      如果数组中不存在目标值 target,返回 [-1, -1]。

    二、代码:

    class Solution:
        def searchRange(self, nums: List[int], target: int) -> List[int]:
            res = [-1, -1]
            if len(nums) == 0:
                return res
    
            def find(nums, target, low):
                n = len(nums)
                l, r = 0, n - 1
                pos = n
                while l <= r:
                    m = (l+r)//2
                    if nums[m] > target or (low is True and nums[m] >= target):
                        r = m - 1
                        pos = m  # 第一个t或者>t的第一个
                    else:
                        l = m + 1
                return pos
            
            left_index = find(nums, target, True)
            right_index = find(nums, target, False) - 1
            n = len(nums)
            if (left_index <= right_index and right_index <= n-1 and nums[right_index] == target):
                return [left_index, right_index]
            return res

    【Leetcode-35】

    一、题目:搜索插入位置

      给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

      你可以假设数组中无重复元素。

    二、代码:

    def searchInsert(self, nums: List[int], target: int) -> int:
            """
            二分法找第一个>=target的位置
            """
            l, r = 0, len(nums) - 1 
            res = -1
            while l <= r:
                mid = (l+r)// 2
                if nums[mid] >= target:
                    res = mid
                    r = mid - 1
                else:
                    l = mid + 1
            if res == -1:  # 没找到>=target的,说明target应该插入在最后
                return len(nums)
            else:
                return res

    【Leetcode-69】

    一、题目:x的平方根

      实现 int sqrt(int x) 函数。

      计算并返回 x 的平方根,其中 x 是非负整数。

      由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

    二、代码:

    def mySqrt(self, x: int) -> int:
            """
            二分法,找第一个平方<=x的
            """
            l, r = 0, x
            res = 0
            while l <= r:
                mid = (l+r) // 2
                s = mid * mid
                if s <= x:
                    res = mid
                    l = mid + 1
                else:
                    r = mid - 1
            return res

    【Leetcode-162】

    一、题目:寻找峰值

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

      给你一个输入数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

    二、代码:

    def findPeakElement(self, nums: List[int]) -> int:
            """
            二分法,一定可以挤出一个峰值,峰值即最大值
            """
            n = len(nums)
            l, r = 0, n-1
            while l < r:
                mid = (l+r) // 2
                if nums[mid] < nums[mid+1]:
                    l = mid + 1
                else:
                    r = mid
            return l

    【Leetcode-287】

    一、题目:寻找重复数

      给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。

      假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。

    二、代码:

    def findDuplicate(self, nums: List[int]) -> int:
            """
            二分法:随意猜一个数mid,统计<=该数的数量cnt,如果cnt>mid,说明数在[left, mid]
            """
            n = len(nums)
            l, r = 1, n-1
            while l < r:
                mid = (l+r)//2
                cnt = 0
                for item in nums:
                    if item <= mid:
                        cnt += 1
                if cnt > mid:
                    r = mid
                else:
                    l = mid + 1
            return l

    【Leetcode-378】

    一、题目:有序矩阵中第K小的元素

      给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。

      请注意,它是 排序后 的第 k 小元素,而不是第 k 个 不同 的元素。

    二、代码:

    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
            """
            二分法的扩展,左上角为最小值,右下角为最大值,找个mid,统计<=mid的值,判断与k的关系,确定往哪走
            1.若<=mid的数目<k,显然需要从mid+1开始找
         2.该方法只适应于数组元素都是整数!!!
    """ def get_cnt(mid_val): cnt = 0 i, j = 0, n-1 # 右上角开始二分查找 while i <= m-1 and j >= 0: if matrix[i][j] <= mid_val: # 这个点的这一行左边都小于 cnt += j+1 # 0~j有j+1个元素 i += 1 # 往下一行找 else: j -= 1 # 往左找 return cnt m, n = len(matrix), len(matrix[0]) if k > m*n: return l = matrix[0][0] # 非下标而是元素,因为二位矩阵的下标不能代表元素大小 r = matrix[m-1][n-1] while l < r: mid_val = (l + r) // 2 cnt = get_cnt(mid_val) # 计算小于mid_val的有多少个 if cnt < k: # 这个值太小了,加上等于的都不够,需要往大了找 l = mid_val + 1 else: # 即使等于k也无法确定是哪个,还需要在小堆中在找 r = mid_val return l
    博文转载请注明出处。
  • 相关阅读:
    Code Review
    关于calendar修改前的代码和修改后的代码
    程序员必备的代码审查(Code Review)清单
    一个数组中最大子数组的和并且加上测试用例
    阅读build to win的个人感想
    结对编码(柳祎、张许君)
    Review(patener)
    Review [myself]
    Impressions
    Array
  • 原文地址:https://www.cnblogs.com/EstherLjy/p/14608582.html
Copyright © 2011-2022 走看看