【力扣Leetcode】二分查找专题(Python刷题)704,35,69,367,441,33,34,153,162,4

  • Post author:
  • Post category:python


leetcode刷题,python实现二分法,目前就这几个题目,后续遇到再补充。

题目:704,35,69,367,441,33,34,153,162,4……


目录


704.二分查找(简单)


35.  搜索插入位置(简单)


69.  x的平方根(简单)


367.有效的完全平方数(简单)


441.排列硬币(简单)


33.  搜索旋转排序数组(中等)


34.  在排序数组中查找元素的第一个和最后一个位置(中等)


153.寻找旋转排序数组中的最小值(中等)


162.寻找峰值(中等)


4.    寻找两个正序(困难)


704.二分查找(简单)


题目要求

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

请必须使用时间复杂度为 O(log n) 的算法。

来源:力扣(LeetCode)

链接:

力扣


题解

思路:典型二分查找

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        n=len(nums)
        if n==0:
            return -1
        left,right=0,n-1
        while left<=right:
            mid=(left+right)//2
            if nums[mid]>target:
                right=mid-1
            elif nums[mid]<target:
                left=mid+1
            else:
                return mid
        return -1

35.  搜索插入位置(简单)


题目要求

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

请必须使用时间复杂度为 O(log n) 的算法。

来源:力扣(LeetCode)

链接:

力扣


题解

方法一:

思路:因为left和right已经发生的交叉,left>right,所以返回的应该是left。

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        n = len(nums)
        left, right = 0, n-1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] < target:
                left = mid + 1
            elif nums[mid] > target:
                right = mid -1
            else:
                return mid
        return left

Java

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length -1;
        int mid = 0;
        while (left<=right){
            mid=(left+right)/2;
            if (nums[mid]<target){
                left=mid+1;
            }
            else if(nums[mid]>target){
                right=mid-1;
            }
            else{
                return mid;
            }
        }
        return left;
    }
}

方法二:

思路:Python可以直接用bisect模块。

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        return bisect.bisect_left(nums,target)

69.  x的平方根(简单)


题目要求

给你一个非负整数

x

,计算并返回

x



算术平方根

由于返回类型是整数,结果只保留

整数部分

,小数部分将被

舍去 。

来源:力扣(LeetCode)

链接:

力扣


题解


方法一

:暴力解决,从头开始算,直到找到

1.某个数的平方恰好=x,输出该数;2.某个数的平方<x,但(该数+1)的平方>x,输出该数。

但是,该方法时间超限。

class Solution:
    def mySqrt(self, x: int) -> int:
        if x==0 or x==1:
            return x
        else:
            for cur in range(x):
                if cur**2<x and (cur+1)**2>x:
                    return cur
                elif cur**2==x:
                    return cur


方法二

:直接用库函数。

import math
class Solution:
    def mySqrt(self, x: int) -> int:
        return int(math.sqrt(x)) 


方法三

:牛顿法


牛顿法原理

:若想求2的平方根,列公式f(x)=x^2-2。当f(x)=0时,x=根号2。

在f(x)上随便取一点,以(2,2)为例,在该点做f(x)的切线,列出切线方程y=4x-6,求该切线方程在y=0时候的x值,

x=1.5

,将所求x值带回f(x)=0.25,此时这点坐标为(切线方程和x轴交点,对应的f(x)值)=(1.5,0.25)。

此时对这个点做上述操作:求切线方程y=4x-5.75,切向方程与x轴交点

x=1.4375

可以发现切线方程与x轴的交点从x=1.5到x=1.4375,如果继续迭代,x会不断逼近根号二的真值1.4142……,如果最开始的初始值选择别的值,也不会耽误计算,影响的只是迭代次数。

参考链接(里面有图):

如何手工开根号?(简述Newton-Raphson 法) – 知乎

数学部分讲完了,接下来是总结规律:

class Solution:
    def mySqrt(self, x: int) -> int:
        t=x
        while t*t>x:
            t=(t+x/t)//2
        return int(t)


方法四

:二分法

class Solution:
    def mySqrt(self, x: int) -> int:
        left,right=0,x+1
        while left<=right:
            mid=(left+right)//2
            if mid**2>x:
                right=mid-1
            elif mid**2<x:
                left=mid+1
            else:
                return mid
        return left-1   

牛顿法思路来源负雪明烛,参考链接:

【LeetCode】69. Sqrt(x) 解题报告(Python & C++)_负雪明烛的博客-CSDN博客

367.有效的完全平方数(简单)


题目要求

给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。

进阶:不要 使用任何内置的库函数,如  sqrt 。

来源:力扣(LeetCode)

链接:

力扣


题解


方法一

:二分法

class Solution:
    def isPerfectSquare(self, num: int) -> bool:
        left,right=0,num+1
        while left<=right:
            mid=(left+right)//2
            if mid**2>num:
                right=mid-1
            elif mid**2<num:
                left=mid+1
            else:
                return True
        return False


方法二

:利用完全平方数的性质(是从1开始若干连续奇数的和)

class Solution:
    def isPerfectSquare(self, num: int) -> bool:
        i=1
        while num>0:
            num-=i
            i+=2
        return num==0


方法三

:牛顿法

class Solution:
    def isPerfectSquare(self, num: int) -> bool:
        t=num
        while t**2>num:
            t=(t+num/t)//2
        return t**2==num

方法二(利用完全平方性质)、方法三(牛顿法)思路来源负雪明烛

参考链接:

【LeetCode】367. Valid Perfect Square 解题报告(Java & Python)_负雪明烛的博客-CSDN博客

441.排列硬币(简单)


题目要求

你总共有 n 枚硬币,并计划将它们按阶梯状排列。对于一个由 k 行组成的阶梯,其第 i 行必须正好有 i 枚硬币。阶梯的最后一行 可能 是不完整的。

给你一个数字 n ,计算并返回可形成 完整阶梯行 的总行数。

来源:力扣(LeetCode)

链接:

力扣


题解

方法一:暴力尝试,判断是否正好排布,如果正好就返回行数;如果不正好就返回行数-1。

class Solution:
    def arrangeCoins(self, n: int) -> int:
        row=0
        while n>0:
            row+=1
            n-=row
        if n==0:
            return row
        else:
            return row-1

方法二:等差数列前n项和+二分法

#等差数列求和公式
sn=a1*n+n*(n-1)*d/2   # a1:第一项; d:公差
sn=(a1+an)*n/2        # an:第n项
class Solution:
    def arrangeCoins(self, n: int) -> int:
        left,right=0,n+1
        while left<=right:
            mid=(left+right)//2  
            sn=mid+mid*(mid-1)/2  # 等差数列求和公式,第一项a1=1,d=1,所以省略了
                                  # 两个求和公式,用哪个都行
            if sn>n:
                right=mid-1
            elif sn<n:
                left=mid+1
            else:
                return mid
        return left-1

方法三:等差数列前n项和,反向求解行数,并向下取整

sn=(a1+an)*n/2
2*sn=(1+n)*n
n**2+n+1/4=2*sn+1/4        # 中学数学,构成完全平方项
(n+1)**2=2*sn+1/4
n=math.sqrt(2*sn+1/4)-1/2  # 此处的sn是题目要求的目标值n
import math
class Solution:
    def arrangeCoins(self, n: int) -> int:
        return int(math.sqrt(2*n+1/4)-1/2)

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 。

来源:力扣(LeetCode)

链接:

力扣


题解

二分法:根据二分法以mid为分界,划分数组左右两侧,使得各自有序,然后在用二分法在target满足的区间内进行寻找。

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left,right=0,len(nums)-1
        while left<=right:
            mid=(left+right)//2
            if nums[mid]==target:
                return mid
            if nums[mid]>nums[right]:     # 左侧有序
                if nums[mid]>target and nums[left]<=target:   # target在有序的左侧,则开始缩小区间搜索
                    right=mid-1
                else:          # target不在有序一侧,则重新划定有序区间
                    left=mid+1
            else:   #nums[mid]<nums[right]: 右侧有序
                if nums[mid]<target and nums[right]>=target:  # target在有序的右侧,则开始缩小区间搜索
                    left=mid+1
                else:          # target不在有序一侧,则重新划定有序区间
                    right=mid-1
        return -1
                

二分法思路来源:负雪明烛。链接:

【LeetCode】33. Search in Rotated Sorted Array 解题报告(Python & C++)_负雪明烛的博客-CSDN博客

34.  在排序数组中查找元素的第一个和最后一个位置(中等)


题目要求

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

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

来源:力扣(LeetCode)

链接:

力扣


题解


二分法一

:利用二分法找target,找到后确认与target相同的左右边界。

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        left,right=0,len(nums)-1
        while left<=right:
            mid=(left+right)//2
            if nums[mid]>target:  
                right=mid-1
            elif nums[mid]<target:
                left=mid+1
            else:                    # 当利用二分法找到target后,确认左右边界。
                left,right=mid,mid   # 以mid为中心向左右移动
                # 指针左移直到找到与target不同的值(注意左边界不能超过0)
                while left>-1 and nums[left]==nums[mid]: 
                    left-=1
                # 指针右移直到找到与target不同的值(注意右边界不能超过nums的长度)
                while right<len(nums) and nums[right]==nums[mid]: 
                    right+=1
                return left+1,right-1 
        return -1,-1   


二分法二

:用python内置函数

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        left=bisect.bisect_left(nums,target)   # left=第一个与target相同的元素下标
        right=bisect.bisect_right(nums,target) # right=最后一个与target相同的元素下标+1
        print("left= ",left,"right= ",right)
        if left==right: #没找到target时,left=right=0
            return -1,-1
        return left,right-1


二分法三

:定义两个函数,实现bisect_left,bisect_right。

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        left=self.lowwer_bound(nums,target)
        right=self.higher_bound(nums,target)
        if left==right:
            return -1,-1
        return left,right-1
    
    # target存在时,返回第一个与target相同元素的下标;target不存在时,返回target插入位置。
    def lowwer_bound(self,nums,target): 
        left,right=0,len(nums)-1
        while left<=right:
            mid=(left+right)//2
            if nums[mid]<target:  # 左右边界两个函数只在这个位置有区别
                left=mid+1
            else:
                right=mid-1
        return left
    
    # target存在时,返回右侧第一个与target不同元素下标;target不存在时,返回target插入位置。
    def higher_bound(self,nums,target):
        left,right=0,len(nums)-1
        while left<=right:
            mid=(left+right)//2
            if nums[mid]<=target: # 找到比target大的第一个数作为left
                left=mid+1
            else:
                right=mid-1
        return left               # 此处返回是left作为右边界

二分法二,三参考负雪明烛,链接:

【LeetCode】34. Find First and Last Position of Element in Sorted Array 解题报告(Python & C++)_负雪明烛的博客-CSDN博客

153.寻找旋转排序数组中的最小值(中等)


题目要求

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]

若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。

来源:力扣(LeetCode)

链接:

力扣


题解


二分法一:

class Solution:
    def findMin(self, nums: List[int]) -> int:
        n=len(nums)
        left,right=0,n-1
        while left<=right:
            mid=(left+right)//2
            if nums[mid]>nums[right]:
                left=mid+1
            elif nums[left]<=nums[mid]<=nums[right]: # left,mid,right是按照顺序排列的时候,返回left
                return nums[left]
            else:       # nums[mid]<left:
                right=mid


二分法二:

思路类似负雪明烛方法三,传送门在下方。

class Solution:
    def findMin(self, nums: List[int]) -> int:
        n=len(nums)
        if n==1:
            return nums[0]
        left,right=0,n-1
        while nums[left]>nums[right]: # 当子序列也是经过旋转的时候,不断寻找有序的部分
            if right==left+1:         # 当左边界与有边界相邻的时候,返回右边界
                return nums[right]
            mid=(left+right)//2
            if nums[mid]>nums[right]:
                left=mid+1
            else:  # nums[mid]<nums[left]
                right=mid
        return nums[left]

二分法三:参考负雪明烛,传送门在下方。

参考链接:

【LeetCode】153. Find Minimum in Rotated Sorted Array 解题报告(Python)_负雪明烛的博客-CSDN博客

162.寻找峰值(中等)


题目要求

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

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

来源:力扣(LeetCode)

链接:

力扣


题解


二分法

:参考负雪明烛

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        n=len(nums)
        if n==1:
            return 0
        left,right=0,n-1
        while left<right:
            mid=(left+right)//2
            if nums[mid]<nums[mid+1]: # 找升序数组,逐渐逼近坡顶
                left=mid+1
            else:                     # nums[mid]>=nums[mid+1]的时候即为坡顶
                right=mid
        return left

参考链接:

【LeetCode】162. Find Peak Element 解题报告(Python)_负雪明烛的博客-CSDN博客

4.    寻找两个正序(困难)


题目要求

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

来源:力扣(LeetCode)

链接:

力扣


题解

第一遍理解错了,搞了个求中值(代码就不放了),第二遍搞成了每个列表的中值再求平均(代码先放这里吧),但是题目是让我们合并数组的中值。干饭去了,明天接着弄。

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:  
        nums1=self.median_array(nums1)
        nums2=self.median_array(nums2)
        nums1.extend(nums2)
        nums1.sort()
        n=len(nums1)
        if n==0:
            return 0
        elif n==1:
            return nums1[0]
        elif n==3:
            return nums1[1]
        else:
            return (nums1[n//2]+nums1[n//2-1])/2

    def median_array(self,num):
        n=len(num)
        if n==0:
            return []
        if n==1:
            return [num[0]]
        mid=n//2
        print(mid)
        if n%2==0:
            return [num[mid-1],num[mid]]
        else:
            return [num[mid]]
        

重新来过


方法一

:两个数组合并,掉包排序,根据合并后的数组长度求中位数。

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        nums1.extend(nums2)     # 合并数组
        nums1.sort()            # 排序
        n=len(nums1) 
        if n%2==0:              # 数组长度为偶数个,则返回中间两个数的平均值
            return (nums1[n//2]+nums1[n//2-1])/2
        else:                   # 数组长度为奇数个,则返回中间那个数
            return nums1[n//2]


方法二

:思路参考第88题,边排序边合并数组。然后再根据有序数组求中位数

第88题传送门:

力扣


【刷题笔记(一起刷题)】Leetcode 面试最常考100题(负雪明烛整理题集)_Paige_sci的博客-CSDN博客

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        m=len(nums1)
        n=len(nums2)
        nums=nums1
        nums.extend(nums2)
        while m>0 and n>0:
            if nums1[m-1]>nums2[n-1]:
                nums[m+n-1]=nums1[m-1]
                m-=1
            else:
                nums[m+n-1]=nums2[n-1]
                n-=1
        nums[:n]=nums2[:n]
        print(nums)
        l=len(nums)
        if l%2==0:
            return (nums[l//2]+nums[l//2-1])/2
        else:
            return nums[l//2]


方法三:二分法

,参考链接在下方,照着扒了个python的,但是我也没太看明白。/捂脸

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        m=len(nums1)
        n=len(nums2)
        left,right=(m+n+1)//2,(m+n+2)//2
        return (self.getkth(nums1,0,m-1,nums2,0,n-1,left)+self.getkth(nums1,0,m-1,nums2,0,n-1,right))/2

    def getkth(self,nums1,start1,end1,nums2,start2,end2,k):
        len1=end1-start1+1
        len2=end2-start2+1
        # 保证nums1是更短一点的数组
        if len1>len2:
            return self.getkth(nums2,start2,end2,nums1,start1,end1,k)
        if len1==0:
            return nums2[start2+k-1]
        if k==1:
            return min(nums1[start1],nums2[start2])
        i=start1+min(len1,k//2)-1
        j=start2+min(len2,k//2)-1
        if nums1[i]>nums2[j]:
            return self.getkth(nums1,start1,end1,nums2,j+1,end2,k-(j-start2+1))
        else:
            return self.getkth(nums1,i+1,end1,nums2,start2,end2,k-(i-start1+1))

参考链接:

Leetcode4.寻找两个有序数组的中位数——巧妙使用二分法_No_Game_No_Life_的博客-CSDN博客_寻找两个有序数组的中位数



版权声明:本文为m0_52023710原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。