5.29力扣 二分查找

395. 至少有K个重复字符的最长子串
在这里插入图片描述
滑动窗口:
每次都要重新遍历整个字符串

class Solution:
    def longestSubstring(self, s: str, k: int) -> int:
        max_num=len(set(s))
        res=0
        for i in range(1,max_num+1):
            #当不同字符个数为i时,最长字符串的长度
            res=max(res,self.helper(i,s,k))
        return res
    def helper(self,n,s,k):
        hash=defaultdict(int)
        l=0
        maxl=0
        windows,strs=0,0
        for r in range(len(s)):
            if hash[s[r]]==0:
                #不同字符的个数
                strs+=1
            hash[s[r]]+=1
            #大于等于k的字符个数
            if hash[s[r]]==k:
                windows+=1
            #当字符串不同个数大于n时.左移窗口找到子字符串
            while strs>n:
                if hash[s[l]]==k:
                    windows-=1
                hash[s[l]]-=1
                if hash[s[l]]==0:
                    strs-=1
                l+=1
            if strs==windows:
                maxl=max(r-l+1,maxl)
        return maxl

递归分治
递归条件
当前处理的字符出现的次数小于k,以当前字符为分隔符切割当前字符串,对切割好的子字符串依次调用函数本身
基线条件:
当前处理的字符串每个字母重复的次数均不小于k时,返回当前字符串的长度
当前字符串长度小于k,返回0
如果某个字符在s中出现次数少于k, 就不用考虑这个字符了, 因此以这个字符分割然后递归

class Solution:
    def longestSubstring(self, s: str, k: int) -> int:
        for c in set(s):
            if s.count(c)<k:
        # 若重复的字符小于k,以此字符分割字符串,形成字符串列,对列中每个字符串 递归调用,返回字符重复个数大于k的字符串列中的最大值 
                return max(self.longestSubstring(t,k) for t in s.split(c))
         # 若每个字符重复的个数都大于k,则返回整个字符串的长度。
        return len(s)

1053. 交换一次的先前排列
在这里插入图片描述
在这里插入图片描述
升序排列的数组无需交换
因为是比A小的最大可能排列,应该尽量处理后面的值,这样变动小

从后往前找到一个降序对,即A[I]>A[I+1],那么将A[I]换成一个比他小的值,生成的字典序一定小于A,
寻找在 A[i] 最左边且小于 A[i] 的最大的数字 A[j]

一次交换后字典序就变小,交换的两个数,肯定原先是大数在前,小数在后。交换后,小数换到前面来,大数换到后面去
那么被改变的那两位中,较高的一位肯定在序列中的位数越低越好(因为一旦位数更高,变小的就更多了),我们要先找到变化的两位中较高的那一位,再找较低的那一位;且找较高的那一位时,只能从后往前找(因为找到了就可以得到答案了呀,这个答案自然就是所有满足条件的答案中,较高位最低的一个),由条件1,为了最大,较低位一定是这个较高位右边比它更小且位数较高的那一位,这样变小的最少

class Solution:
    def prevPermOpt1(self, A: List[int]) -> List[int]:
        idx=-1
        maxi=-1
        flag=False
        for i in range(len(A)-2,-1,-1):
            if A[i]>A[i+1]:
                #找到A[I]最左边的比A[I]小的最大值,这样两数交换可以得到比原数组小的最大排列
                #必须满足 A[i] > A[j],否则不能满足交换后的字典序小于原始字典序
                for j in range(i+1,len(A)):
                    if A[i]>A[j]:
                        flag=True
                        if A[j]>maxi:
                            maxi=A[j]
                            idx=j
                if flag:
                    A[i],A[idx]=A[idx],A[i]
                    return A
        return A

162. 寻找峰值
在这里插入图片描述
单调递增栈

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        nums.append(float('-inf'))
        stack=[]
        for i in range(len(nums)):
            if stack and nums[stack[-1]]>nums[i]:
                return stack[-1]
            stack.append(i) 

二分查找:
因为题目告诉我们可以返回数组中的任意一个峰顶。所以我们只要确定某一半至少存在一个峰顶,那么另一半就可以抛弃掉。
log(n)的复杂度:二分查找
如果 nums[mid] < nums[mid + 1],此时在上升阶段,因为 nums[n] 看做负无穷,也就是最终一定会下降,所以 mid + 1 到 end 之间至少会存在一个峰顶,可以把左半部分抛弃。

如果 nums[mid] > nums[mid + 1],此时在下降阶段,因为 nums[0] 看做负无穷,最初一定是上升阶段,所以 start 到 mid 之间至少会存在一个峰顶,可以把右半部分抛弃。

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

852. 山脉数组的峰顶索引
在这里插入图片描述
求最大值的索引

class Solution:
    def peakIndexInMountainArray(self, A: List[int]) -> int:
        return A.index(max(A))

二分法

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

704. 二分查找
在这里插入图片描述
在这里插入图片描述

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

1095. 山脉数组中查找目标值
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
先找到山顶元素 mountaintop 所在的索引
在前有序且升序数组中找 target 所在的索引,如果找到了,就返回,如果没有找到,就执行第 3 步;
如果步骤 2 找不到,就在后有序且降序数组中找 target 所在的索引。
一个数组找某个元素出现的最小下标,而这个数组的排列特点是先升后降
先二分搜索找到山顶,在进行左右两边二分搜索
有一个单调递增序列(峰值左边)和一个单调递减序列(峰值右边),我们只是不知道两个序列的分割点,即峰值在哪里。所以我们第一步应该首先找到峰值。

# """
# This is MountainArray's API interface.
# You should not implement it, or speculate about its implementation
# """
#class MountainArray:
#    def get(self, index: int) -> int:
#    def length(self) -> int:

class Solution:
    def findInMountainArray(self, target: int, mountain_arr: 'MountainArray') -> int:
        n=mountain_arr.length()
        left,right=0,n-1
        #寻找峰顶
        while left<right:
            mid=left+(right-left)//2
            #[left,mid + 1]为递增区间,需要往右边搜索,left右移
            if mountain_arr.get(mid)<mountain_arr.get(mid+1):
                left=mid+1
            else:
                right=mid
        # 山峰左边上升区间
        l=0
        r=left
        #当l==r时退出循环
        while l<r:
            mid=l+(r-l)//2
            if mountain_arr.get(mid)==target:
                return mid
            elif mountain_arr.get(mid)>target:
                r=mid-1
            else:
                l=mid+1
        #如果在左侧找到了,则返回索引
        if mountain_arr.get(l)==target:
            return l
        #左边没有找到,去右边
        l,r=left,n-1
        while l<r:
            mid=l+(r-l)//2
            if mountain_arr.get(mid)==target:
                return mid
            elif mountain_arr.get(mid)<target:
                r=mid-1
            else:
                l=mid+1
        return l if  mountain_arr.get(l) == target else -1

845. 数组中的最长山脉
在这里插入图片描述
先找上坡,后找下坡,用mid记录山顶位置。
新山脉的起点为下坡的终点

class Solution:
    def longestMountain(self, A: List[int]) -> int:
        res=0
        start=end=0
        while start<len(A):
            end=start
            #山脉一开始是上升的,扩展山脉终点
            while end+1<len(A) and A[end]<A[end+1]:
                end+=1
            #记录山脉的山峰点
            mid=end
            #山脉开始下降
            while end+1<len(A) and A[end]>A[end+1]:
                end+=1
            #符合山脉要求,记录长度
            if start<mid<end:
                res=max(res,end-start+1)
            #如果山脉是平的,起点往后移
            if start==end:
                start+=1
            #新一轮的山脉起点是上一轮山脉终点
            else:
                start=end
        return res

457 环形数组循环
在这里插入图片描述
在这里插入图片描述
做深度优先搜素,利用字典visited来标记已经搜素过的节点。
利用numSet记录在一个方向上遇到的节点,如果新节点在numSet就有环。但需要在三个情况清空numSet:
从i节点开始DFS到底了,从i+1节点开始搜素时清空numSet.
当搜素方向direction改变符号,按题意要求同方向,清空numSet.
当一个节点的下一个节点是自身,清空numSet.

class Solution:
    def circularArrayLoop(self, nums: List[int]) -> bool:
        n=len(nums)
        #每个点作为循环起点开始遍历
        for i in range(n):
            #标记遍历过的点
            visited=[False for _ in range(n)]
            j=i
            count=0
            #j还没被遍历过
            while not visited[j]:
                visited[j]=True
                #方向是向前
                if nums[j]>0:
                    #下一个点的索引
                    j=(j+nums[j])%n
                    if nums[j]<0:
                        break
                    else:
                        count+=1
                #方向向后
                else:
                    if (j+nums[j])%n>=0:
                        j=(j+nums[j])%n
                    else:
                        j=n+(j+nums[j])%n
                    if nums[j]>0:
                        break
                    count+=1
            # 循环完判断是否回到原点且长度大于1
            if i==j and count>1:
                return True
        return False

1160. 拼写单词
在这里插入图片描述
在这里插入图片描述

class Solution:
    def countCharacters(self, words: List[str], chars: str) -> int:
        res=0
        for word in words:
            for i in word:
                if word.count(i)<=chars.count(i):
                    flag=1
                    continue
                else:
                    flag=0
                    break
            if flag==1:
                res+=len(word)
        return res

35. 搜索插入位置
在这里插入图片描述
二分查找

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        l,r=0,len(nums)-1
        while l<=r:
            mid=l+(r-l)//2
            if nums[mid]==target:
                return mid
            elif nums[mid]>target:
                r=mid-1
            else:
                l=mid+1
        return l
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值