刷力扣算法--蓝桥杯备战

刷题网站:https://leetcode-cn.com/problems/container-with-most-water/submissions/
参考书籍:LeetCode 101:和你一起你轻松刷题(C++)
使用语言:python
记录刷题过程中的学到的一些知识点
坚持就是胜利鸭

1、数组

两数之和

class Solution:
    def twoSum(self,nums, target):
        #用len()方法取得nums列表长度
        n = len(nums)
        #创建一个空字典
        d = {}
        for x in range(n):
            a = target - nums[x]
            #字典d中存在nums[x]时
            if nums[x] in d:
                return d[nums[x]],x
            #否则往字典增加键/值对
            else:
                d[a] = x
        #边往字典增加键/值对,边与nums[x]进行对比

盛最多水的容器

暴力解法
import math
a=[1,8,6,2,5,4,8,3,7]
n=len(a)
maxarea=0
for i in range(n):
    for j in range(i+1,n):
        maxarea=max(maxarea,min(a[i],a[j])*(j-i))
print(maxarea)
双指针法

在初始时,左右指针分别指向数组的左右两端。然后每次移动数组较小的那个指针

import math
a=[1,8,6,2,5,4,8,3,7]
n=len(a)
maxarea=0
l=0
r=n-1
while l<r:
    maxarea=max(maxarea,min(a[l],a[r])*(r-l))
    if a[l]<a[r]:
        l+=1
    else:
        r-=1
print(maxarea)

2、贪心算法

分配问题

455.分发饼干
import math
g=[1,2]
s=[1,2,3]
#贪心算法
#首先对孩子和饼干进行从小到大排序
#其次,给剩余孩子里最小饥饿的孩子分配最小能满足其要求的饼干
g.sort()
s.sort()
child=0
cookie=0
while child<len(g) and cookie<len(s):
    if g[child]<=s[cookie]:
       child+=1
    
    cookie+=1
print(child)      
135.分发糖果

贪心策略:每次遍历中,只考虑并更新相邻一侧的大小关系

import math
ratings=[1,2,2]
#把所有孩子的糖果数初始化为1
#先从左往右遍历一遍,如果右边孩子的评分高,右边的糖果更新为左边的糖果加一
#再从右往左遍历一遍,如果左边孩子的评分高,且左边孩子当前糖果数不大于右边孩子的糖果数,则左边孩子的糖果数更新为右边孩子的糖果数加一
num=[1]*len(ratings)
for i in range(1,len(ratings)):
    if ratings[i]>ratings[i-1]:
        num[i]=num[i-1]+1
for i in range(len(ratings)-1,0,-1):
    if ratings[i]<ratings[i-1]:
        num[i-1]=max(num[i-1],num[i]+1)
print(num)        
print(sum(num))   

区间问题

435.无重叠区间
import math
#贪心策略:优先保留结尾小且不相交的区间
intervals=[[1, 2], [2, 3], [3, 4],[1, 3]]
#先按第一个元素排序再按第二个元素排序
#intervals.sort(key=lambda x:(x[0],x[1]))
#先按组内排序,再按照第一个元素排序
#a.sort(key=lambda x: (x.sort(), x[0], x[1]))
#先按照结尾的大小进行增序排序,每次选择结尾最小且和前一个选择的区间不重叠的区间
intervals.sort(key=lambda x:x[1])
total=0
prev=intervals[0][1]
#如果这次的区间的开头小于上一个区间的结尾,则删除的个数增加一
for i in range(1,len(intervals)):
    if intervals[i][0]<prev:
        total+=1
    else:
        prev=intervals[i][1]
print(intervals)

练习

605.种花问题
#贪心策略:每次都找3个连续的0
class Solution:
    def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
        length=len(flowerbed)
        if length==1 and flowerbed[0]==0:
            n-=1
        else:    
            if flowerbed[0]==0 and flowerbed[1]==0:
                n-=1
                flowerbed[0]=1
                
            for i in range(1,length-1):
                if flowerbed[i]==0 and flowerbed[i-1]==0 and flowerbed[i+1]==0:
                    n-=1
                    flowerbed[i]=1
            if flowerbed[length-1]==0 and flowerbed[length-2]==0:
                n-=1        
        if n<=0:
            return True
        else:
            return False
452.用最少数量的箭引爆气球
#本题先找到重叠分区的个数,然后用总共的分区减去重叠的个数即为无重叠的分区
class Solution:
    def findMinArrowShots(self, points: List[List[int]]) -> int:
        #贪心策略:优先保留结尾小且不相交的区间
        #先按第一个元素排序再按第二个元素排序
        #points.sort(key=lambda x:(x[0],x[1]))
        #先按组内排序,再按照第一个元素排序
        #a.sort(key=lambda x: (x.sort(), x[0], x[1]))
        #先按照结尾的大小进行增序排序,每次选择结尾最小且和前一个选择的区间不重叠的区间
        points.sort(key=lambda x:x[1])
        total=0
        prev=points[0][1]
        #如果这次的区间的开头小于上一个区间的结尾,则删除的个数增加一
        for i in range(1,len(points)):
            if points[i][0]<=prev:
                total+=1
            else:
                prev=points[i][1]
        return len(points)-total
763.划分字母区间
#统计一遍信息(个数,第一次出现位置,最后一次出现位置)
def last_index(s,letter):
    num=s.count(letter)
    for i in range(len(s)):
        if s[i]==letter:
            num-=1
            if num==0:
                return i

s="ababcbacadefegdehijhklij"
b=list(set(s))
c=[]
for i in s:
    first=s.index(i)
    last=last_index(s,i)
    if [first,last] not in c:
        c.append([first,last])
print(c)    

d=[]
total=0
prev=c[0][1]
first=c[0][0]
#如果这次的区间的开头小于上一个区间的结尾,则删除的个数增加一
for i in range(1,len(c)):
    if c[i][0]<prev:
        total+=1
        if c[i][1]>prev:  
            prev=c[i][1]         
    else:
        d.append(c[i][0]-first)
        prev=c[i][1]
        first=c[i][0]
    if i==len(c)-1:
        d.append((prev-first)+1)
print(d)
122.买卖股票的最佳时机
#方法:简单的一次遍历
#若第i天的价格比第i-1天的价格高,就将i天与i-1天的价格差计入总利润中
prices = [7,1,5,3,6,4]
profit=0
for i in range(1,len(prices)):
    if prices[i]>prices[i-1]:
        profit+=(prices[i]-prices[i-1])
print(profit)        
406.根据身高重建队列

套路:一般这种数对,还涉及排序的,要么根据第一个元素正向排序,根据第二个元素反向排序;或者反过来

按照元素1进行降序排序,对于每个元素,在其之前的元素的个数就是大于等于他的元素

class Solution:
    def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
        res = []
        people = sorted(people, key = lambda x: (-x[0], x[1]))
        for p in people:
            if len(res) <= p[1]:
                res.append(p)
            elif len(res) > p[1]:
                res.insert(p[1], p)
        return res
参考:https://leetcode-cn.com/problems/queue-reconstruction-by-height/solution/xian-pai-xu-zai-cha-dui-dong-hua-yan-shi-suan-fa-g/
665.非递减数列

给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。

我们是这样定义一个非递减数列的: 对于数组中任意的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。

输入: nums = [4,2,3]
输出: true
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。

遇到递减的情况要么将前面的元素缩小,要么将后面的元素放大

易错点:

  1. 如果将nums[i]缩小,可能导致前面的不是非递减数列
  2. 如果将nums[i+1]放大,可能会导致后面的继续出现递减

贪心策略:每次只看连续的三个元素

  1. 尽可能的不放大nums[i+1]
  2. 如果缩小nums[i],但不破坏前面的子序列的非递减性

[4,2,5]改4或者2

[1,4,2,5]改4或者2

[3,4,2,5]改2

贪心算法:

class Solution:
    def checkPossibility(self, nums: List[int]) -> bool:
            for i in range(1,len(nums)):
                if nums[i]<nums[i-1]:
                    if i==1 or nums[i]>=nums[i-2]:
                        nums[i-1]=nums[i]
                        break
                    else:
                        nums[i]=nums[i-1]   
                        break
            for i in range(len(nums)-1):
                if nums[i]>nums[i+1]:
                    return False
            return True

3、双指针

167. Two Sum II - Input array is sorted (Easy)
numbers=[2,7,11,15]
target=9
def judge(numbers,target):
    l=0
    r=len(numbers)-1
    while l<r:
        if numbers[l]+numbers[r]==target:
            return [l+1,r+1]
        elif numbers[l]+numbers[r]>target:
            r-=1
        else:
            l+=1
print(judge(numbers,target))
88、merge sorted array(easy)
nums1 = [1,2,3,0,0,0]
m = 3
nums2 = [2,5,6]
n = 3
p=m+n-1
m=m-1
n=n-1
while m>=0 and n>=0:
    if nums1[m]>nums2[n]:
        nums1[p]=nums1[m]
        p-=1
        m-=1
    else:
        nums1[p]=nums2[n]
        p-=1
        n-=1
while n>=0:
    nums1[p]=nums2[n]
    p-=1
    n-=1
print(nums1)
142. Linked List Cycle II (Medium)
class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        fast,slow=head,head
        while True:
            if not(fast and fast.next):return
            fast,slow=fast.next.next,slow.next
            if fast==slow:break
        fast=head
        while fast!=slow:
            fast,slow=fast.next,slow.next
        return fast
76. Minimum Window Substring (Hard)
import collections
s = "ADOBECODEBANC"
t = "ABC"
#滑动窗口
#首先定义一个字典,记录t中需要覆盖的字符及数目
need=collections.defaultdict(int)
for c in t:
    need[c]+=1
#定义neednum记录需要的总共个数
neednum=len(t)
#定义左边界
i=0
res=(0,float('inf'))
for j,c in enumerate(s):
    if need[c]>0:
        neednum-=1
    need[c]-=1
    if neednum==0:#此时滑动窗口中包含了所有的t,尝试缩小左边界去除掉不需要的字符
        while True:
            c=s[i]
            if need[c]==0:
                break
            need[c]+=1
            i+=1#左边框向右移动
        if j-i<res[1]-res[0]:
            res=(i,j)
        need[s[i]]+=1
        neednum+=1
        i+=1
if res[1]>len(s):
    print("")
else:
    print(s[res[0]:res[1]+1])

633. Sum of Square Numbers (Easy)
numbers=[i for i in range(0,int(c**0.5)+1)]
        a=0
        sum=0
        b=len(numbers)-1
        while a<=b:
            sum=numbers[a]**2+numbers[b]**2
            if c==sum:
                return True
            if sum<c:
                a+=1
            else:
                b-=1
        return False
class Solution:
    def judgeSquareSum(self, c: int) -> bool:
        l=0
        r=int(c**0.5+1)
        while l<=r:
            sum=l**2+r**2
            if sum==c:
                return True
            elif sum>c:
                r-=1
            else:
                l+=1
        return False
680. Valid Palindrome II (Easy)
s='aba'
#首先定义一个检查是否是回文字符串的函数
def judge(a,b):
    while a<b:
        if s[a]==s[b]:
            a+=1
            b-=1
        else:
            return False
    return True
def huiwen(s):
    a=0
    b=len(s)-1
    while a<b:
        if s[a]==s[b]:
            a+=1
            b-=1
        else:
            return judge(a+1,b) or judge(a,b-1)
    return True
print(huiwen(s))
524、Longest Word in Dictionary through Deleting (Medium)
class Solution:
    def findLongestWord(self, s: str, dictionary: List[str]) -> str:
            dictionary.sort()
            def check(d,s):
                ii=0
                jj=0
                while jj<len(s):
                    if d[ii]==s[jj]:
                        if ii==(len(d)-1):
                           return True
                        ii+=1
                        jj+=1
                    else:
                        jj+=1       
                return False
            newdic=[]
            for i in dictionary:
                if check(i,s):
                    newdic.append(i)
            newdic.sort(key=lambda x:len(x),reverse=True)
            if len(newdic):
                return newdic[0]
            return ""
340、[滑动窗口]至多包含K个不同字符的最长字串

问题描述:给定一个字符串s,找到至多包含k个不同字符得最长子串的长度。

比如s=“cebea”,k=2,那么输出结果就是3,因为此时"ebe"满足条件:至多包含两个不同字符,且子串最长

比如s=“world”,k=4,那么输出结果就是4,因为"worl"和"orld"满足条件:至多包含4个不同字符,且子串最长

def lengthstring(s,k):
    tmp=0#用于记录满足条件的最大值
    for i in range(1,len(s)+1):
        for j in range(len(s)-i+1):
            print(s[j:j+i])
            if len(set(s[j:j+i])) == k:#如果窗口中取集合后的不同字符就是k个
                    tmp = max(tmp,i)#更新tmp的值
                    print("tmp:{}".format(tmp))
    return tmp #最后返回即可
print(lengthstring(s,k))
#一个hash表和一个左边界标记,遍历字符串将其加入到hash表中,不同字符多于k个了,就从左边开始删字符,知道hash表不同字符长度等于k.此时字符串的长度就是当前字符和左边界的距离。               
from collections import defaultdict
#字典中存储的整型的值默认为0
hash=defaultdict(int)
max_num=0#用于存放最大值
start=0#滑动窗口的左端
#从字符串左开始遍历
for i in range(len(s)):
    hash[s[i]]+=1
    while len(hash)>k:
        hash[s[start]]-=1
        if hash[s[start]]==0:
            del hash[s[start]]
        start+=1
    max_num=max(max_num,i-start+1)
print(max_num)

4、二分查找

69、Sqrt(x)求平方
34、在排序数组中查找元素的第一个和最后一个位置
nums=[5,7,7,8,8,10]
target=8
def search(nums,target):
    if len(nums)==0:
        return [-1,-1]
    elif nums[0]>target or nums[-1]<target:
        return [-1,-1]
    else:
        l,r=0,len(nums)-1
        while l<=r:
            mid=(l+r)//2
            if target>nums[mid]:
                l=mid+1
            elif target<nums[mid]:
                r=mid-1
            elif target==nums[mid]:
                l=r=mid
                while l-1>=0 and nums[l-1]==target:
                    l-=1
                while r+1<=len(nums)-1 and nums[r+1]==target:
                    r+=1
                return [l,r]
    return [-1,-1]
print(search(nums,target))

81、搜索旋转 排序数组
class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        left=0
        right=len(nums)-1
        while left<=right:
            mid=left+(right-left)//2
            if nums[mid]==target:
                return True
            #10111 和 1110111101 这种。此种情况下 nums[start] == nums[mid],分不清到底是前面有序还是后面有序,将left++相当于去掉一个重复的项
            if nums[left]==nums[mid]:
                left+=1
            elif nums[mid]<=nums[right]:#后半部分有序
                if target>nums[mid] and target<=nums[right]:#target在后半部分
                    left=mid+1
                else:#否则,去后半部分去找
                    right=mid-1
            else:#前半部分有序
                if target>=nums[left] and target<nums[mid]:#target在前半部分
                    right=mid-1
                else:
                    left=mid+1
        return False
154、寻找旋转排序数组中的最小值
nums = [1,3,5]
def find(nums):
    low,heih=0,len(nums)-1
    while low<high:
        pivot=low+(high-low)//2
        if nums[pivot]<nums[high]:
            high=pivot
        elif nums[pivot]>nums[high]:
            low=pivot+1
        else:
            high-=1
    return nums[low]
540、有序数组中的单一元素
class Solution:
    def singleNonDuplicate(self, nums: List[int]) -> int:
        if len(nums)==1:
            return nums[0]
        for i in range(0,len(nums),2):
            if i+1==len(nums) or nums[i]!=nums[i+1]:
                return nums[i]
4、寻找两个正序数组的中位数

需要对两个数组同时进行二分搜索

nums1=[1,2]
nums2=[3,4]
#返回中位数:奇数需要最后一次遍历的结果,偶数需要最后一次和上一次遍历的结果
#所以left和right,left保存上一次遍历的结果,right保存更新的最后一次的结果
def find(nums1,nums2):
    m=len(nums1)
    n=len(nums2)
    length=m+n
    aStart=0
    bStart=0
    left,right=-1,-1
    for i in range(length//2+1):
        left=right
        if (aStart<m and(bStart>=n or nums1[aStart]<nums2[bStart])):
            
            right=nums1[aStart]
            aStart+=1
        else:
            
            right=nums2[bStart]
            bStart+=1
    if length%2==0:
        return (left+right)/2.0
    else:
        return right
print(find(nums1,nums2))

5、排序算法

快速排序

归并排序

插入排序

冒泡排序

选择排序

215、【快速选择】数组中的第K个最大元素
#partition函数
def partition(nums, left, right):
    pivot = nums[left]#初始化一个待比较数据
    i,j = left, right
    while(i < j):
        while(i<j and nums[j]>=pivot): #从后往前查找,直到找到一个比pivot更小的数
            j-=1
        nums[i] = nums[j] #将更小的数放入左边
        while(i<j and nums[i]<=pivot): #从前往后找,直到找到一个比pivot更大的数
            i+=1
        nums[j] = nums[i] #将更大的数放入右边
    #循环结束,i与j相等
    nums[i] = pivot #待比较数据放入最终位置 
    return i #返回待比较数据最终位置
#快速排序
def quicksort(nums, left, right):
    if left < right:
        index = partition(nums, left, right)
        quicksort(nums, left, index-1)
        quicksort(nums, index+1, right)

arr = [1,3,2,2,0]
quicksort(arr, 0, len(arr)-1)
print(arr) 
#topk切分
'''寻找一个位置,这个位置左边是k个比这个位置上的数更小的数,右边是n-k个比该位置上的数大的数'''
def topk_split(nums,k,left,right):
    if left<right:
        index=partition(nums,left,right)
        if index==k:
            return
        elif index<k:
            topk_split(nums,k,index+1,right)
        else:
            topk_split(nums,k,left,index-1)
#获得前k小的数
def topk_smalls(nums,k):
    topk_split(nums,k,0,len(nums)-1)
    return nums[:k]
arr = [1,3,2,3,0,-19]
k = 2
print(topk_smalls(arr, k))
print(arr)
#获取第k小的数
def topk_small(nums,k):
    topk_split(nums,k,0,len(nums)-1)
    return nums[k-1]

arr = [1,3,2,3,0,-19]
k = 3
print(topk_small(arr, k))
print(arr)
#获取前k大的数
def topk_larges(nums,k):
 #partition是按从小到大划分的,如果让index左边为前n-k个小的数,则index右边为前k个大的数
    topk_split(nums,len(nums)-k,0,len(nums)-1)
    return nums[len(nums)-k:]
arr = [1,3,-2,3,0,-19]
k = 3
print(topk_larges(arr, k))
print(arr)
#只排序前k个小的数
def topk_sort_left(nums,k):
    #获得前k个小的数,进行快排
    topk_split(nums,k,0,len(nums)-1)
    topk=nums[:k]
    quicksort(topk,0,len(topk)-1)
    return topk+nums[k:]
arr = [0,0,1,3,4,5,0,7,6,7]
k = 4
print('--------------')
topk_sort_left(arr, k)
print(arr)
#只排序后k个大的数
def topk_sort_right(nums,k):
    topk_split(nums,len(nums)-k,0,len(nums)-1)
    topk=nums[len(nums)-k:]
    quicksort(topk,0,len(topk)-1)
    return nums[:len(nums)-k]+topk
arr = [0,0,1,3,4,5,0,-7,6,7]
k = 4
print(topk_sort_right(arr, k))
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        nums.sort()
        return nums[-k]
347、【桶排序】前k个高频元素
import collections
'''
方法一:直接排序
1、记录每个数字出现的次数
2、返回次数最高的k个数
'''
def topKFrequent(nums,k):
    count=collections.Counter(nums)
    print(count.most_common(k))
    return [item[0] for item in count.most_common(k)]
nums = [1,1,1,1,2,2,3,4]
k = 2
print(topKFrequent(nums,k))

'''
方法二:堆排序
1、记录每个数字出现的次数
2、把数字和对应出现次数放入堆中
3、返回堆的前k大元素
'''
import heapq
def topKFrequent1(nums,k):
    count=collections.Counter(nums)
    heap=[(val,key) for key,val in count.items()]
    print(heap)
    return [item[1] for item in heapq.nlargest(k, heap)]
print(topKFrequent1(nums,k))

'''
方法三:推排序2
1、记录每个数字出现的次数
2、把数字和对应出现次数放入堆中(小顶堆)
3、如果堆已满(大小>=k)且当前数的次数比堆顶大,用当前元素替换堆顶元素
4、返回堆中的数字部分
'''
def topKFrequent2(nums,k):
    count=collections.Counter(nums)
    heap=[]
    for key,val in count.items():
        if len(heap)>=k:
            if val>heap[0][0]:
                heapq.heapreplace(heap,(val,jey))
        else:
            heapq.heappush(heap,(val,key))
    return [item[1] for item in heap]
print(topKFrequent2(nums,k))

def topKFrequent(nums,k):
    counter=collections.Counter(nums)
    h=[]
    for key,val in counter.items():
        heapq.heappush(h,(val,key))
        if len(h)>k:
            heapq.heappop(h)
    return [x[1] for x in h]
451、【桶排序】根据字符出现频率排序
def frequencySort(s):
    count=collections.Counter(s)
    h=[]
    for key,val in count.items():
        h.append((val,key))
    h.sort(key=lambda x:-x[0])
    news=[]
    for item in h:
        news.append(item[0]*item[1])
    news=''.join(news)
    return news
s="raaeaedere"
print(frequencySort(s))
75、颜色分类–循环不变量
def sortColors(nums):
    def swap(nums,index1,index2):
        nums[index1],nums[index2]=nums[index2],nums[index1]
    size=len(nums)
    if size<2:
        return
    zero=0
    two=size
    i=0
    while i<two:
        if nums[i]==0:
            swap(nums,i,zero)
            i+=1
            zero+=1
        elif nums[i]==1:
            i+=1
        else:
            two-=1
            swap(nums,i,two)
nums=[2,0,1]
sortColors(nums)
print(nums)
26、删除有序数组中的重复项
def removeDuplicates(nums):
    size=len(nums)
    if size<2:
        return size
    j=1
    for i in range(1,size):
        if nums[i]==nums[j-1]:
            continue
        else:
            nums[j]=nums[i]
            j+=1
    return j
nums=[0,0,1,1,1,2,2,3,3,4]
print(removeDuplicates(nums))
283、移动零
def moveZeros(nums):
    size=len(nums)
    j=0
    for i in range(0,size):
        if nums[i]==0:
            continue
        else:
            nums[j]=nums[i]
            j+=1
    while j<size:
        nums[j]=0
        j+=1
nums=[0,1,0,3,12]
moveZeros(nums)
print(nums)
27、移除元素
def removeElement(nums,val):
    size=len(nums)
    j=0
    for i in range(0,size):
        if nums[i]==val:
            continue
        else:
            nums[j]=nums[i]
            j+=1
    return j
nums = [3,2,2,3]
val = 3
removeElement(nums,val)
print(nums)
80、删除有序数组中的重复项
def removeDuplicates(nums):
    size=len(nums)
    j=2
    for i in range(2,size):
        if nums[i]!=nums[j-2]:
            nums[j]=nums[i]
            j+=1
    return j
nums=[0,0,1,1,1,1,2,3,3]
removeDuplicates(nums)
print(nums)

6、搜索

6.1、深度优先搜索DFS

在搜索到一个新的结点时,立即对该新节点进行遍历;因此遍历需要用先入后出的栈来实现。

695、岛屿最大的面积
'''
1、从某位置出发,项四个方向探索相连的土地
2、每探寻一块土地,计数加一
3、确保每块土地只会被探寻一次
'''

'''
从每一个陆地出发,遍历该陆地所在的岛屿
在遍历某一岛屿的时候:
1、从隶属于该岛屿的某一块陆地出发,向四个方向递归地DFS
2、每次递归对下标进行判断,以区域的边界作为递归边界
3、为保证每块陆地只访问一次,将已访问过的陆地置0
4、递归地返回整块岛屿的面积
'''
def dfs(grid,i,j):
    if i < 0 or j < 0 or i == len(grid) or j == len(grid[0]) or grid[i][j] != 1:
            return 0
    grid[i][j]=0
    ans=1
    for di,dj in [[0,1],[0,-1],[1,0],[-1,0]]:
        nexti,nextj=i+di,j+dj
        ans+=dfs(grid,nexti,nextj)
    return ans
def maxarea(grid):
    ans=0
    #从每一个陆地出发,遍历该陆地所在的岛屿
    for i,l in enumerate(grid):
        for j,n in enumerate(l):
            ans=max(dfs(grid,i,j),ans)
    return ans
grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],
        [0,0,0,0,0,0,0,1,1,1,0,0,0],
        [0,1,1,0,1,0,0,0,0,0,0,0,0],
        [0,1,0,0,1,1,0,0,1,0,1,0,0],
        [0,1,0,0,1,1,0,0,1,1,1,0,0],
        [0,0,0,0,0,0,0,0,0,0,1,0,0],
        [0,0,0,0,0,0,0,1,1,1,0,0,0],
        [0,0,0,0,0,0,0,1,1,0,0,0,0]]

        
#用栈实现深度优先算法
#把想要遍历的土地放在栈里,然后再取出这些土地的时候访问他们
def maxarea(grid):
    ans=0
    for i,l in enumerate(grid):
        for j,n in enumerate(l):
            cur=0
            stack=[(i,j)]
            while stack:
                cur_i,cur_j=stack.pop()
                if cur_i<0 or cur_j<0 or cur_i==len(grid) or cur_j==len(grid[0]) or grid[cur_i][cur_j]!=1:
                    continue
                cur+=1
                grid[cur_i][cur_j]=0
                for di,dj in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
                        next_i, next_j = cur_i + di, cur_j + dj
                        stack.append((next_i, next_j))
            ans=max(ans,cur)
    return ans

print(maxarea(grid))

#广度优先搜索
#每次从队首取出土地,并将接下来想要遍历的土地放在队尾
import collections
def maxarea(grid):
    ans=0
    for i,l in enumerate(grid):
        for j,n in enumerate(l):
            cur=0
            q=collections.deque([(i,j)])
            while q:
                cur_i,cur_j=q.popleft()
                if cur_i<0 or cur_j<0 or cur_i==len(grid) or cur_j==len(grid[0]) or grid[cur_i][cur_j]!=1:
                    continue
                cur+=1
                grid[cur_i][cur_j]=0
                for di,dj in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
                        next_i, next_j = cur_i + di, cur_j + dj
                        q.append((next_i, next_j))
            ans=max(ans,cur)
    return ans
print(maxarea(grid))


def findcity(isConnected):
    city=0
    for i in range(1,len(isConnected)):
        for j in range(i):
            if isConnected[i][j]==1:
                city+=1
    if len(isConnected)-city==0:
        return 1
    else:
        return len(isConnected)-city

isConnected = [[1,0,0],[0,1,0],[0,0,1]]
print(findcity(isConnected))
417、太平洋大西洋水流问题
heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]
if not heights or not heights[0]:return []
#从两个大洋开始向上流
res1=set()#流向太平洋的位置
res2=set()#流向大西洋的位置
row=len(heights)
col=len(heights[0])
def dfs(i,j,res):
    res.add((i,j))
    for di,dj in [[0,1],[0,-1],[1,0],[-1,0]]:
        nexti,nextj=di+i,dj+j
        if 0<=nexti<row and 0<=nextj<col and heights[i][j]<=heights[nexti][nextj] and (nexti,nextj) not in res:
            dfs(nexti,nextj,res)
for i in range(row):
    dfs(i,0,res1)
for i in range(col):
    dfs(0,i,res1)
for i in range(row):
    dfs(i,col-1,res2)
for i in range(col):
    dfs(row-1,i,res2)
print(list(res1&res2))
6.2、并查集
#并查集
'''
并--合并
查--查找
集--字典,合并集合中的元素,查找集合中的元素
并查集--应用:连通分量
'''
class UnionFind:
    def __init__(self):
        #记录每个节点的父节点
        self.father={}
    def find(self,x):
        #查找根节点--如果节点的父结点不为空,那就不断迭代
        root=x
        while self.father[root]!=None:
            root=self.father[root]
        #路径压缩--如果树很深,需要做一下路径压缩,把树的深度固定为二
        while x!=root:
            original_father=self.father[x]
            self.father[x]=root
            x=original_father
    def merge(self,x,y,val):
        #合并两个节点
        root_x,root_y=self.find(x),self.find(y)
        if root_x!=root_y:
            self.father[root_x]=root_y
    def is_connected(self,x,y):
        #判断两节点是否相连
        return self.find(x)==self.find(y)
    def add(self,x):
        #添加新节点
        if x not in self.father:
            self.father[x]=None
**547、省份数量
class UnionFind:
    def __init__(self):
        self.father = {}
        # 额外记录集合的数量
        self.num_of_sets = 0
    
    def find(self,x):
        root = x
        
        while self.father[root] != None:
            root = self.father[root]
        
        while x != root:
            original_father = self.father[x]
            self.father[x] = root
            x = original_father
        
        return root
    
    def merge(self,x,y):
        root_x,root_y = self.find(x),self.find(y)
        
        if root_x != root_y:
            self.father[root_x] = root_y
            # 集合的数量-1
            self.num_of_sets -= 1
    
    def add(self,x):
        if x not in self.father:
            self.father[x] = None
            # 集合的数量+1
            self.num_of_sets += 1

class Solution:
    def findCircleNum(self, M: List[List[int]]) -> int:
        uf = UnionFind()
        for i in range(len(M)):
            uf.add(i)
            for j in range(i):
                if M[i][j]:
                    uf.merge(,j)
        
        return uf.num_of_sets
**261、以图判树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EgtvSYSo-1650247907471)(C:\Users\Tian\AppData\Roaming\Typora\typora-user-images\image-20220403101255877.png)]

#并查集
'''
并--合并
查--查找
集--字典,合并集合中的元素,查找集合中的元素
并查集--应用:连通分量
'''
class UnionFind:
    def __init__(self):
        #记录每个节点的父节点
        self.father={}
    def find(self,x):
        if self.father[x]!=x:
            self.father[x]=self.find(self.father[x])
        return self.father[x]
    def merge(self,x,y):
        #合并两个节点
        root_x,root_y=self.find(x),self.find(y)
        if root_x==root_y:
            return False
        else:
            self.father[y]=x
        return True
    def is_connected(self,x,y):
        #判断两节点是否相连
        return self.find(x)==self.find(y)
    def add(self,x):
        #添加新节点
        if x not in self.father:
            self.father[x]=x
#初始时将结点的父亲定义为自身,在合并时如果两个节点的父亲时相同的,则属于同一个子集也就是有环存在
def tree(edges,n):
    a=UnionFind()
    for i in range(n):
        a.add(i)
    for edge in edges:
        if a.merge(edge[0],edge[1])==False:
            return False
    par=a.find(0)
    for i in range(1,n):
        if par!=a.find(i):
            return False
    return True
edges = [[0,1], [1,2], [2,3], [1,3], [1,4]]
n=5
print(tree(edges,n))
323、无向图中连通分量的数目
class UnionFind:
    def __init__(self):
        #记录每个节点的父节点
        self.father={}
        self.num=0
    def find(self,x):
        if self.father[x]!=None:
            self.father[x]=self.find(self.father[x])
        return self.father[x]
    def merge(self,x,y):
        #合并两个节点
        root_x,root_y=self.find(x),self.find(y)
        if root_x!=root_y:
            self.father[y]=x
        self.num-=1
    def is_connected(self,x,y):
        #判断两节点是否相连
        return self.find(x)==self.find(y)
    def add(self,x):
        #添加新节点
        if x not in self.father:
            self.father[x]=None
            self.num+=1
            
#初始时将结点的父亲定义为自身,在合并时如果两个节点的父亲时相同的,则属于同一个子集也就是有环存在
def tree(edges,n):
    a=UnionFind()
    for edge in edges:
        a.add(edge[0])
        a.add(edge[1])
        a.merge(edge[0],edge[1])
    return a.num
edges = [[0,1], [1,2],[2,3] ,[3,4]]
n=5
print(tree(edges,n))
200、岛屿数量
def gridnum(grid):
    ans=[]
    for i,l in enumerate(grid):
        for j,n in enumerate(l):
            cur=0
            stack=[(i,j)]
            while stack:
                cur_i,cur_j=stack.pop()
                if cur_i<0 or cur_j<0 or cur_i==len(grid) or cur_j==len(grid[0]) or grid[cur_i][cur_j]!=1:
                    continue
                cur+=1
                grid[cur_i][cur_j]=0
                for di,dj in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
                        next_i, next_j = cur_i + di, cur_j + dj
                        stack.append((next_i, next_j))
            ans.append(cur)
    num=0
    for i in ans:
        if i!=0:
            num+=1
    return num
grid=[[1,1,1,1,0],
      [1,1,0,1,0],
      [1,1,0,0,0],
      [0,0,0,0,0]]
grid1=[[1,1,0,0,0],
       [1,1,0,0,0],
       [0,0,1,0,0],
       [0,0,0,1,1]]
print(gridnum(grid1))
**399、除法求值
class UnionFind:
    def __init__(self):
        """
        记录每个节点的父节点
        记录每个节点到根节点的权重
        """
        self.father = {}
        self.value = {}
    
    def find(self,x):
        """
        查找根节点
        路径压缩
        更新权重
        """
        root = x
        # 节点更新权重的时候要放大的倍数
        base = 1
        while self.father[root] != None:
            root = self.father[root]
            base *= self.value[root]
        
        while x != root:
            original_father = self.father[x]#保存上一个节点
            ##### 离根节点越远,放大的倍数越高
            self.value[x] *= base
            base /= self.value[original_father]
            #####
            self.father[x] = root#改变当前父亲节点
            x = original_father
         
        return root
    
    def merge(self,x,y,val):
        """
        合并两个节点
        """
        root_x,root_y = self.find(x),self.find(y)
        
        if root_x != root_y:
            self.father[root_x] = root_y
            ##### 四边形法则更新根节点的权重
            self.value[root_x] = self.value[y] * val / self.value[x]

    def is_connected(self,x,y):
        """
        两节点是否相连
        """
        return x in self.value and y in self.value and self.find(x) == self.find(y)
    
    def add(self,x):
        """
        添加新节点,初始化权重为1.0
        """
        if x not in self.father:
            self.father[x] = None
            self.value[x] = 1.0
            

def calcEquation(equations, values, queries):
        uf = UnionFind()
        for (a,b),val in zip(equations,values):
            uf.add(a)
            uf.add(b)
            uf.merge(a,b,val)
    
        res = [-1.0] * len(queries)

        for i,(a,b) in enumerate(queries):
            if uf.is_connected(a,b):
                res[i] = uf.value[a] / uf.value[b]
        return res

    

equations = [["a","b"],["b","c"]]
values = [2.0,3.0]
queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
print(calc(equations,values,queries))
**721、账户合并
class UnionFind:
    def __init__(self):
        self.father = {}
        # 根节点所在集合的所有账户
        self.accounts = {}
        
    def find(self,x):
        if not self.father[x]: return x
        
        # 递归的路径压缩处理
        self.father[x] = self.find(self.father[x])
        
        return self.father[x]
    
    def merge(self,x,y):
        root_x,root_y = self.find(x),self.find(y)
        
        if root_x == root_y:
            return
        
        # 按秩合并,更新根节点和所属的账户
        if len(self.accounts[root_x]) > len(self.accounts[root_y]):
            self.father[root_y] = root_x
            self.accounts[root_x] += self.accounts[root_y]
            del self.accounts[root_y]
        else:
            self.father[root_x] = root_y
            self.accounts[root_y] += self.accounts[root_x]
            del self.accounts[root_x]
    
    def add(self,x):
        if x not in self.father:
            self.father[x] = None
            self.accounts[x] = [x]


def accountsMerge(accounts):
    uf=UnionFind()
    for account in accounts:
        #找到性名和主账户
        name,master=account[0],account[1]
        uf.add((name,master))
        #和其余的账户一一合并
        account=list(set(account[2:]))
        for i in range(len(account)):
            uf.add((name,account[i]))
            uf.merge((name,master),(name,account[i]))
    res=[]
    for key,value in uf.father.items():
        #是根节点
        if not value:
            #添加user
            usr=[uf.accounts[key][0][0]]
            acc=[]
            #添加账户
            for account in uf.accounts[key]:
                acc.append(account[1])
            res.append(usr+sorted(acc))
    return res
 #深度优先搜索
import collections
class Solution:
    def build_graph(self,accounts):
        """
        建图
        """
        graph = collections.defaultdict(list)
        for account in accounts:
            master = account[1]
            # 对剩余账户做一个去重
            for email in list(set(account[2:])):
                graph[master].append(email)
                graph[email].append(master)
        
        return graph
    
    def dfs(self,email,graph,visited,emails):
        """
        深搜遍历
        """
        # 已经访问过的就剪枝
        if email in visited:
            return 
        
        visited.add(email)
        emails.append(email)
        
        # 对邻居节点继续深搜
        for neighbor in graph[email]:
            self.dfs(neighbor,graph,visited,emails)
    
    def accountsMerge(self, accounts):
        graph = self.build_graph(accounts)

        res = []
        visited = set()
        for account in accounts:
            emails = []
            self.dfs(account[1],graph,visited,emails)
            if emails:
                res.append([account[0]] + sorted(emails))
        
        return res

accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]]
a=Solution()
print(a.accountsMerge(accounts))
6.4、回溯法
46、全排列
def dfs(nums,size,depth,path,used,res):
    if depth==size:
        res.append(path[:])
        return
    for i in range(size):
        if not used[i]:
            used[i]=True
            path.append(nums[i])
            dfs(nums,size,depth+1,path,used,res)
            used[i]=False#状态重置
            path.pop()#恢复现场
def permute(nums):
    size=len(nums)
    if len(nums)==0:
        return []
    used=[False for _ in range(size)]
    res=[]
    dfs(nums,size,0,[],used,res)
    return res
nums=[1,2,3]
print(permute(nums))
47、全排列II

会搜索到一些不需要的解,可以提前剪枝

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        def dfs(nums,size,depth,path,used,res):
            if depth==size:
                res.append(path[:])
                return
            for i in range(size):
                if not used[i]:
                    if i>0 and nums[i]==nums[i-1] and not used[i-1]:
                        continue
                    used[i]=True
                    path.append(nums[i])
                    dfs(nums,size,depth+1,path,used,res)
                    used[i]=False#状态重置
                    path.pop()

        size=len(nums)
        if len(nums)==0:
                return []
        nums.sort()
        used=[False for _ in range(size)]
        res=[]
        dfs(nums,size,0,[],used,res)
        return res      
39、组合总和
def combinationSum( candidates,target):

        def dfs(candidates, begin, size, path, res, target):
            if target < 0:
                return
            if target == 0:
                res.append(path)
                return

            for index in range(begin, size):
                dfs(candidates, index, size, path + [candidates[index]], res, target - candidates[index])

        size = len(candidates)
        if size == 0:
            return []
        path = []
        res = []
        dfs(candidates, 0, size, path, res, target)
        return res

candidates = [1,2]
target = 4
print(combinationSum(candidates,target))  

回溯算法入门级详解 + 练习(持续更新) - 全排列 - 力扣(LeetCode) (leetcode-cn.com)

40、组合总和II
def combinationSum( candidates,target):

        def dfs(candidates, begin, size, path, res, target):
            if target < 0:
                return
            if target == 0:
                path.sort()
                if path not in res:
                    res.append(path)
                return
            for index in range(begin, size):
                if index > begin and candidates[index - 1] == candidates[index]:
                    continue
                dfs(candidates, index+1, size, path + [candidates[index]], res, target - candidates[index])

        size = len(candidates)
        if size == 0:
            return []
        
        path = []
        res = []
        dfs(candidates, 0, size, path, res, target)
        return res

candidates = [1,1]
target = 2
print(combinationSum(candidates,target))  
这个避免重复当思想是在是太重要了。
这个方法最重要的作用是,可以让同一层级,不出现相同的元素。即
                  1
                 / \
                2   2  这种情况不会发生 但是却允许了不同层级之间的重复即:
               /     \
              5       5
                例2
                  1
                 /
                2      这种情况确是允许的
               /
              2  
                
为何会有这种神奇的效果呢?
首先 cur-1 == cur 是用于判定当前元素是否和之前元素相同的语句。这个语句就能砍掉例1。
可是问题来了,如果把所有当前与之前一个元素相同的都砍掉,那么例二的情况也会消失。 
因为当第二个2出现的时候,他就和前一个2相同了。
                
那么如何保留例2呢?
那么就用cur > begin 来避免这种情况,你发现例1中的两个2是处在同一个层级上的,
例2的两个2是处在不同层级上的。
在一个for循环中,所有被遍历到的数都是属于一个层级的。我们要让一个层级中,
必须出现且只出现一个2,那么就放过第一个出现重复的2,但不放过后面出现的2。
第一个出现的2的特点就是 cur == begin. 第二个出现的2 特点是cur > begin.
77、组合
def combinationSum(n,k):
        def dfs(path, res, k,n,i):
            if k == 0:
                print(path)
                path.sort()
                if path not in res:
                    res.append(path[:])
                return
            for index in range(i,n):
                path.append(index+1)
                dfs(path , res, k-1,n,index+1)
                path.pop()
        path = []
        res = []
        used=[False for _ in range(n)]
        dfs(path, res,k,n,0)
        return res
print(combinationSum(4,2))  
78、子集
def combinationSum(nums):
        def dfs(path,res, n,i):
            if path not in res:
                    res.append(path[:])
            for index in range(i,n):
                dfs(path+[nums[index]] , res,n,index+1)
        path = []
        res = []
        n=len(nums)
        dfs(path, res,n,0)
        return res
nums=[4,1,0]
print(combinationSum(nums))  
90、子集II
        def dfs(path,res, n,i):
            if path not in res:
                    res.append(path[:])
            for index in range(i,n):
                if nums[index-1]==nums[index] and index>i:
                    continue
                dfs(path+[nums[index]] , res,n,index+1)
        path = []
        res = []
        nums.sort()
        n=len(nums)
        dfs(path, res,n,0)
        return res
60、排列排序
#0!=1表示没有数可选,即表示到达叶子节点了,排列数只剩下1个
#可以先把阶乘算好,放到一共数组里,根据索引直接获得阶乘值

def getPermutation(n, k):
    def dfs(n, k, index, path):
            cnt = factorial[n - 1 - index]
            for i in range(1, n + 1):
                if used[i]:
                    continue
                if cnt < k:
                    k -= cnt
                    continue
                path.append(i)
                used[i] = True
                dfs(n, k, index + 1, path)
                # 注意:这里要加 return,后面的数没有必要遍历去尝试了
                return

    if n == 0:
            return ""

    used = [False for _ in range(n + 1)]
    path = []
    factorial = [1 for _ in range(n + 1)]
    for i in range(2, n + 1):
            factorial[i] = factorial[i - 1] * i

    dfs(n, k, 0, path)
    return ''.join([str(num) for num in path])

print(getPermutation(4,9))  
93、复原IP地址
def restireIpAddresses(s):
    def dfs(s,idx,path,res):
        if idx>4:
            return
        if idx==4 and not s:
            res.append(path[:-1])
            return
        for i in range(len(s)):
            #当s的首字符为'0'时,可以直接将'0'作为IP地址中四个整数之一
            #当s的首字符不为0时,需要保证s[:i+1]处于IP地址整数的范围之内
            if s[:i+1]=='0' or (s[0]!='0' and 0<int(s[:i+1])<256):
                #将下标i之后的s[i+1:]作为新的s进行递归参数传入
                #分割次数idx+1
                #将中间字符串结果patn后连接下标i之前的s[:i+1]字符串,然后再拼接'.'
                dfs(s[i+1:],idx+1,path+s[:i+1]+'.',res)
    if not s or len(s)<4:
        return []
    res=[]
    dfs(s,0,'',res)
    return res
s='25525511135'
print(restireIpAddresses(s))
6.5、Flood Fill
733、图像渲染
def floodFill(image,sr,sc,newColor):
    def dfs(image,src,newColor,i,j):
        if i<0 or i==len(image) or j<0 or j==len(image[0]) or image[i][j]==newColor:
            return image
        if image[i][j]!=src:
            return image
        image[i][j]=newColor
        for di,dj in [[0,1],[0,-1],[1,0],[-1,0]]:
            nexti,nextj=i+di,j+dj
            image=dfs(image,src,newColor,nexti,nextj)
        return image
    src=image[sr][sc]
    dfs(image,src,newColor,sr,sc)
    return image
image =  [[0,0,0],[0,0,0]]
print(floodFill(image,0,0,2))
130、被围绕的区域
#寻找和边界联通的O
def solve(board):
    if not board or not board[0]:
        return
    row=len(board)
    col=len(board[0])
    def dfs(board,i,j):
        board[i][j]='#'
        for di,dj in [[0,1],[0,-1],[1,0],[-1,0]]:
            nexti,nextj=i+di,j+dj
            if 0<=nexti<row and 0<=nextj<col and board[nexti][nextj]=='O':
                dfs(board,nexti,nextj)
        return board
    
    for j in range(col):
        if board[0][j]=='O':
            board=dfs(board,0,j)
        if board[col-1][j]=='O':
            board=dfs(board,row-1,j)
            
    for i in range(row):
        if board[i][0]=='O':
            board=dfs(board,i,0)
        if board[i][row-1]=='O':
            board=dfs(board,i,col-1)
    for i in range(row):
        for j in range(col):
            if board[i][j]=='#':
                board[i][j]='O'
            elif board[i][j]=='O':
                board[i][j]='X'
    return board
board=[["X"]]
print(solve(board))
79、单词搜素

要注意回溯

def exist(board,word):
    word=list(word)
    m=len(board)
    n=len(board[0])
    mark=[[ 0 for _ in range(n)] for _ in range(m)]
    def dfs(idx,i,j):
        #idx下一个要查找的词
        if idx==len(word):
            return True
        
        for di,dj in [[0,1],[0,-1],[1,0],[-1,0]]:
            nexti,nextj=i+di,j+dj
            if 0<=nexti<m and 0<=nextj<n and board[nexti][nextj]==word[idx]:
                if mark[nexti][nextj]==1:
                    continue
                mark[nexti][nextj]=1
                if dfs(idx+1,nexti,nextj)==True:
                    return True
                else:
                    mark[nexti][nextj]=0
        return False
    for r in range(m):
            for c in range(n):
                if board[r][c]==word[0]:
                    mark[r][c]=1
                    if dfs(1,r,c):
                      return True
                    else:
                        mark[r][c]=0
    return False

board=[["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]
word='ABCB'
print(exist(board,word))
6.6、字符串中的回溯问题
17、电话号码的字母组合
def letterCombinations(digits):
    if not digits:return []
    phone = {'2':['a','b','c'],
                 '3':['d','e','f'],
                 '4':['g','h','i'],
                 '5':['j','k','l'],
                 '6':['m','n','o'],
                 '7':['p','q','r','s'],
                 '8':['t','u','v'],
                 '9':['w','x','y','z']}
  
    res=[]
    def dfs(i,digit):
        if 0==len(digit):
            res.append(i)
            return 
        for letter in phone[digit[0]]:
            dfs(i+letter,digit[1:])
    dfs('',digits)
    return res
digits='23'
print(letterCombinations(digits))
784、字母大小写全排列
def letterCase(s):
    #两种选择:转换和不转换
    def dfs(path,i):
        if i==len(s):
            res.append(path)
            return
        if s[i].isdigit():
            dfs(path+s[i],i+1)
        else:
            if s[i].islower():
                dfs(path+s[i].upper(),i+1)
            else:
                dfs(path+s[i].lower(),i+1)
            dfs(path+s[i],i+1)
    res=[]
    dfs('',0)
    return res
s='a1b2'
print(letterCase(s))
22、括号生成
#当前左右括号都有大于0个可以使用的时候,才能分支
#左分支:左括号>0
#右分支:左括号《右分支
#当左右括号==0时截至
def generate(n):
    left=n
    right=n
    res=[]
    def dfs(path,left,right):
        if left==0 and right==0:
            res.append(path)
            return
        if right>left:
            dfs(path+')',left,right-1)
        if left>0:
            dfs(path+'(',left-1,right)
    dfs('',left,right)
    return res
n=3
print(generate(n))
6.7、游戏问题
51.N皇后

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UX6avcSV-1650247907472)(C:\Users\Tian\AppData\Roaming\Typora\typora-user-images\image-20220311090132764.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WnfiWG7g-1650247907472)(C:\Users\Tian\AppData\Roaming\Typora\typora-user-images\image-20220311090103762.png)]

def solveNQueens(n):
    res=[]
    def dfs(m,path):
        if len(path)==n:
            res.append(path)
            return
        for i in range(n):
            #i是行下标,m是列下标
            if lie[i]==0 and zheng[i-m+n-1]==0 and fu[i+m-1]==0:
                #同一列没有,第i-m+n-1个正斜线没有,第i+m-1个负斜线没有
                lie[i],zheng[i-m+n-1],fu[i+m-1]=1,1,1
                cpath=['.'*i+'Q'+'.'*(n-i-1)]
                dfs(m+1,path+cpath)
                lie[i],zheng[i-m+n-1],fu[i+m-1]=0,0,0
        
    lie = [0 for j in range(n)]
    zheng = [0 for j1 in range(2 * n - 1)]
    fu = [0 for j2 in range(2 * n - 1)]
    dfs(0,[])
    return res
print(solveNQueens(1))
37、解数独
def solve(board):
    row=[set(range(1,10)) for _ in range(9)]
    col=[set(range(1,10)) for _ in range(9)]
    block=[set(range(1,10)) for _ in range(9)]
    empty=[]
    for i in range(9):
       for j in range(9):
           if board[i][j]!='.':
               val=int(board[i][j])
               row[i].remove(val)
               col[j].remove(val)
               block[(i//3)*3+j//3].remove(val)
           else:
               empty.append((i,j))
    def backtrack(iter):
        if iter==len(empty):
            return True
        i,j=empty[iter]
        b=(i//3)*3+j//3
        for val in row[i] &col[j]&block[b]:
            row[i].remove(val)
            col[j].remove(val)
            block[b].remove(val)
            board[i][j] = str(val)
            if backtrack(iter+1):
                return True
            row[i].add(val)  # 回溯
            col[j].add(val)
            block[b].add(val)
    backtrack(0)
    return board
board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
print(solve(board))
257、二叉树的所有路径
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:

        def construct_paths(root, path):
            if not root:return 
            
            path += str(root.val)
            if not root.left and not root.right: #叶子结点
                paths.append(path)
            else:
                path += '->'
                construct_paths(root.left, path)
                construct_paths(root.right, path)
        paths = []
        construct_paths(root, '')
        return paths
310、最小高度树
def findminheighttrees(edges):
    if n==2:
        return [0,1]
    if n==1:
        return [0]
    adjs=defaultdict(list)#创建一个错误的话会返回默认值的字典
    for x in edges:#图的邻接表表示法,基本上是模板
        adjs[x[0]].append(x[1])
        adjs[x[1]].append(x[0])
    #BFS队列:初始队列放入初始元素,size=1为叶子,入队
    queue=deque()
    for key,value in adjs.items():
        if len(value)==1:
            queue.append(key)
    while(queue):
        size=len(queue)
        n=n-size
        for _ in range(size):
            #能遍历到v时,说明v只有一条邻边
            v=queue.popleft()
            v_adj=adjs[v].pop()
            adjs[v_adj].remove(v)
            if len(adjs[v_adj])==1:
                queue.append(v_adj)
        if n==1:
            return [queue.popleft()]
        if n==2:
            return [queue.popleft(),queue.popleft()]

7、DP动态规划

目标-》最终状态:使用动态搜索

自下而上,先解决子问题再解决父问题

目标-》输出所有路径:带有状态记录的优先搜索

自上而下,从父问题搜索到子问题,若重复搜索到同一子问题则进行状态记录,防止重复记录

4.1、一维

70、爬楼梯
def climbstairs(n):
    if n==0 or n==1:
        return 1
    return self.climbStairs(n-1)+self.climbStairs(n-2)
def climbstairs(n):
    def dfs(i,memo):
        if i==0 or i==1:
            return 1
        if memo[i]==-1:
            memo[i]=dfs(i-1,memo)+dfs(i-2,memo)
        return memo[i]
    return dfs(n,[-1]*(n+1))
    
def climbStairs(n):
    dp=[0]*(n+1)
    dp[0]=dp[1]=1
    for i in range(2,n+1):
        dp[i]=dp[i-1]+dp[i-2]
    return dp[-1]
def climbStairs(n):
    a=b=1
    for i in range(2,n+1):
        a,b=b,a+b
    return b
198、打家劫舍
def rob(nums):
    size=len(nums)
    dp=[0]*(size)
    if size==0:
        return 0
    if size==1:
        return nums[0]
    dp[0]=nums[0]
    dp[1]=max(nums[1],dp[0])
    for i in range(2,size):
        dp[i]=max(dp[i-1],nums[i]+dp[i-2])
    return dp[-1]
nums=[1,2,3,1]
print(rob(nums))      
    
def rob(nums):
    size=len(nums)
    if size==0:
        return 0
    if size==1:
        return nums[0]
    first,second=nums[0],max(nums[1],nums[0])
    for i in range(2,size):
        first,second=second,max(first+nums[i],second)
    return second

nums=[1,2,3,1]
print(rob(nums))   

4.2、二维

64、最小路径和
def minPathsum(grid):
#单元格(i,j)只能从左方或者上方走到,只需考虑左边界和上边界
    for i in range(len(grid)):
        for j in range(len(grid[0])):
            if i==0 and j==0:
                continue
            elif i==0:
                grid[i][j]=grid[i][j-1]+grid[i][j]
            elif j==0:
                grid[i][j]=grid[i-1][j]+grid[i][j]
            else:
                grid[i][j]=min(grid[i-1][j],grid[i][j-1])+grid[i][j]
    return grid[-1][-1]
grid = [[1,3,1],[1,5,1],[4,2,1]]
print(minPathsum(grid))
542、01矩阵
def updatematrix(mat):
    m,n=len(mat),len(mat[0])
    dp = [[10**9] * n for _ in range(m)]
    for i in range(m):
         for j in range(n):
             if mat[i][j]==0:
                 dp[i][j]=0
    for i in range(m):
         for j in range(n):      
                 if j>0:
                     dp[i][j]=min(dp[i][j],dp[i][j-1]+1)
                 if i>0:
                     dp[i][j]=min(dp[i][j],dp[i-1][j]+1)
    for i in range(m-1,-1,-1):
         for j in range(n-1,-1,-1):
                     if j<n-1:
                         dp[i][j]=min(dp[i][j],dp[i][j+1]+1)
                     if i<m-1:
                         dp[i][j]=min(dp[i][j],dp[i+1][j]+1)
    return dp

mat=[[0,0,0],[0,1,0],[1,1,1]]
print(updatematrix(mat))
#多源点广度优先搜索
def updatematrix1(mat):
    m,n=len(mat),len(mat[0])
    dist= [[10**9] * n for _ in range(m)]
    zeroes_pos=[(i,j) for i in range(m) for j in range(n) if mat[i][j]==0]
    #将所有的0添加进入初始队列中
    q=collections.deque(zeroes_pos)
    seen=set(zeroes_pos)
    while q:
        i,j=q.popleft()
        for ni,nj in [(i-1,j),(i + 1, j), (i, j - 1), (i, j + 1)]:
            if 0<=ni<m and 0<=nj<n and (ni,nj) not in seen:
                dist[ni][nj]=dist[i][j]+1
                q.append((ni,nj))
                seen.add((ni,nj))
    return dist
221、最大正方形
def maximal(matrix):
    #如果该位置是0,则dp(i,j)=0,因为当前位置不可能在由1组成的正方形中
    #如果该位置的值是1,则dp(i,j)的值由其上方、左方和左上方的三个相邻位置的dp决定
    maxside=0
    m,n=len(matrix),len(matrix[0])
    dp=[[0]*n for _ in range(m)]
    for i in range(m):
        for j in range(n):
             if matrix[i][j]=='1':
                if i==0 or j==0:
                    dp[i][j]=1
                else:
                    dp[i][j]=min(dp[i-1][j],dp[i-1][j-1],dp[i][j-1])+1
                maxside=max(maxside,dp[i][j])
    return maxside*maxside
matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
print(maximal(matrix))

4.3、分割类型

279、完全平方数
def numsquares(n):
    #初始化dp[0,1,2,..,n]长度为n+1,最多次数就是全由1构成的
    dp=[i for i in range(n+1)]
    for i in range(2,n+1):
        for j in range(1,int(i**0.5)+1):
            #遍历所有平方数小于i的数j,始终保存所有可能情况中的最小值
            dp[i]=min(dp[i],dp[i-j*j]+1)
    return dp[-1]
print(numsquares(12))
**91、解码方法
#f[i]为前i个字符的解码方案数
'''
f[i]=f[i-1] 1<=a<=9
f[i]=f[i-2] 10<=b<=26
f[i]=f[i-1]+f[i-2] 1<=a<=9,10<=b<=26
'''
#由于题目存在前导零,而前导零属于无效item,追加空格既可以避免讨论前导零,也能使下标从1开始
def numdecoding(s):
    n=len(s)
    s=' '+s
    f=[0]*(n+1)
    f[0]=1
    for i in range(1,n+1):
        #a代表当前位置单独形成item
        #b代表当前位置与前一位置共同形成item
        a = ord(s[i]) - ord('0')
        b = ( ord(s[i - 1]) - ord('0') ) * 10 + ord(s[i]) - ord('0')
        if 1<=a<=9:
            f[i]=f[i-1]
        if 10<=b<=26:
            f[i]+=f[i-2]
    return f[n]
print(numdecoding('226'))
**139、单词拆分
def wordBreak(s,wordDict):
    #dp[i]为前i个字符在wordDict中
    n=len(s)
    dp=[False]*(n+1)
    dp[0]=True
    for i in range(n):
        for j in range(i+1,n+1):
            #dp[i]=True表示s的前i位可以表示
            #s[i,...,j)出现在wordDict中,表示s的前j位可以表示
            if dp[i] and s[i:j] in wordDict:
                dp[j]=True
    return dp[-1]

4.4、子序列

300、最长递增子序列
def lengthof(nums):
    n=len(nums)
    dp=[1]*(n+1)
    #dp[i]表示以i结尾的子序列的长度
    #对于每一个位置,如果其之前的某个位置j所对于的数字小于位置i所对应的数字
    for i in range(0,n):
        for j in range(0,i):
            if  nums[i]>nums[j]:#nums[i]可以接在nums[j]之后
              dp[i]=max(dp[j]+1,dp[i])
    return max(dp)
nums=[7,7,7,7,7,7,7]
print(lengthof(nums))
1143、最长公共子序列
def longestCommon(text1,text2):
    m=len(text1)
    n=len(text2)
    #dp[i][j]表示到第一个字符串位置i为止,到第二个字符串位置j为止,最长的公共子序列长度
    dp=[[0]*(n+1) for _ in range(m+1)]
    for i in range(1,m+1):
        for j in range(1,n+1):
            if text1[i-1]==text2[j-1]:
                dp[i][j]=dp[i-1][j-1]+1
            else:
                dp[i][j]=max(dp[i-1][j],dp[i][j-1])
    return dp[m][n]
text1='abcde'
text2='ace'
print(longestCommon(text1,text2))

4.5、背包问题

**416、分割等和子集
def canpartition(nums):
    if sum(nums)%2!=0:
        return False
    allsum=sum(nums)//2
    if max(nums)>allsum:
        return False
    dp=[[False]*(allsum+1) for _ in range(len(nums))]
    dp[0][nums[0]]=True
    for i in range(1,len(nums)):
        for j in range(1,allsum+1):
            if j>=nums[i]:
                dp[i][j]=dp[i-1][j]|dp[i-1][j-nums[i]]
            else:
                dp[i][j]=dp[i-1][j]
    return dp[-1][-1]
nums= [1,1,1,1,1]
print(canpartition(nums))
**474、一和零
#多维的0-1背包问题
#dp[i][j]最多有i个0和j个1的strs的最大子集的大小
def findmaxform(strs,m,n):
    dp=[[0]*(n+1) for _ in range(m+1)]
    for strr in strs:
        count0=strr.count('0')
        count1=strr.count('1')
        for i in range(m,count0-1,-1):
            for j in range(n,count1-1,-1):
                dp[i][j]=max(dp[i][j],dp[i-count0][j-count1]+1)
    return dp[m][n]
strs=["10", "0001", "111001", "1", "0"]
print(findmaxform(strs,5,3))
**322、零钱兑换
def coinChange(coins,amount):
    dp=[amount+2]*(amount+1)
    dp[0]=0
    for i in range(1,amount+1):
        for coin in coins:
            if i>=coin:
                dp[i]=min(dp[i],dp[i-coin]+1)
    return dp[amount] if dp[amount]!=amount+2 else -1
coins=[2]
print(coinChange(coins,11))

4.6、字符串编辑

**650、只有两个键的键盘
def minsteps(n):
    #dp[i]表示延展到长度i的最少操作次数
    #对于每一个位置j,如果j可以被i整除,dp[i]=dp[j]+dp[i/j]
    dp=[float('inf')]*(n+1)
    dp[1]=0
    for i in range(2,n+1):
        for j in range(1,i):
            if i%j==0:
                dp[i]=min(dp[i],dp[j]+i//j)
    return dp[-1]
print(minsteps(3))

4.7、股票交易

**121、买卖股票的最佳时机
def maxprofit(prices):
    minp=prices[0]
    dp=[0]*len(prices)
    dp[0]=0
    for i in range(1,len(prices)):
        dp[i]=max(prices[i]-minp,dp[i-1])
        if minp>prices[i]:
            minp=prices[i]
    return dp[-1]
prices=[7,6,4,3,1]
print(maxprofit(prices))
**122、最佳买卖股票时机含冷冻期
def maxProfit(prices):
    if not prices:
        return 0
    n=len(prices)
    #dp[i][0]第i天不持有股票,且当天没卖出
    #dp[i][1]第i天不持有股票,当天卖出了
    #dp[i][2]第i天持有股票,此时的最大利润
    dp=[[0,0,0] for i in range(n)]
    dp[0]=[0,0,-prices[0]]
    for i in range(1,n):
        #"0"状态下,昨天没有股票/昨天有股票,但是卖出了
        dp[i][0]=max(dp[i-1][0],dp[i-1][1])
        #“1”状态下
        dp[i][1]=dp[i-1][2]+prices[i]
        #"2"状态下,有股票可能是当前买入的,且前一天不能是状态1;前一天就持股dp[i-1][2]
        dp[i][2]=max(dp[i-1][0]-prices[i],dp[i-1][2])
    return max(dp[-1])
prices=[1,2,3,0,2]
print(maxProfit(prices))
**714、买卖股票的最佳时机含手续费
def maxprofit(prices,fee):
    '''
    dp[i][0]表示第i天,持有股票-当天买入或者前i天买入
    dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i])    
    dp[i][1]表示第i天没有股票--之前卖出或者当天卖出
    dp[i][1]=max(dp[i-1][0]-fee+prices[i],dp[i-1][1])
    '''
    n=len(prices)
    if n==1:
        return 0
    dp=[[0,0] for i in range(n)]
    dp[0]=[-prices[0],0]
    for i in range(1,n):
        dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]) 
        dp[i][1]=max(dp[i-1][0]-fee+prices[i],dp[i-1][1])
    return dp[-1][-1]
prices=[1,3,7,5,10,3]
print(maxprofit(prices,3))

#使用变量进行状态转移
class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        n = len(prices)
        sell, buy = 0, -prices[0]
        for i in range(1, n):
            sell, buy = max(sell, buy + prices[i] - fee), max(buy, sell - prices[i])
        return sell

练习

**213、打家劫舍II
#2、 打家劫舍II
#环形排列意味着第一个房子和最后一个房子只能选择一个偷窃
#偷窃第一个房子nums[:n-2]
#偷窃最后一个房子nums[1:]
def rob(nums):
    #dp[i]表示第i个房屋的最高金额
    '''
    如果偷dp[i]=dp[i-2]+nums[i]
    如果不偷dp[i]=dp[i-1]
    '''
    n=len(nums)
    dp1=[0]*(n)
    dp2=[0]*(n)
    if n==1:
    	return nums[0]
    #偷第一间房子,当前房子收益nums[i-1]
    dp1[0],dp1[1]=0,nums[0]
    #偷最后一间房子,当前房子收益nums[i]
    dp2[0],dp2[1]=0,nums[1]
    for i in range(2,n):
        dp1[i]=max(dp1[i-2]+nums[i-1],dp1[i-1])
        dp2[i]=max(dp2[i-2]+nums[i],dp2[i-1])
    return max(dp1+dp2)

nums=[4,1,2,7,5,3,1]
print(rob(nums))
53、最大子数组和
def maxsub(nums):
    n=len(nums)
    dp=[0]*n
    dp[0]=nums[0]
    if n==1:
        return nums[0]
    for i in range(1,n):
        #dp[i]之和dp[i-1]有关
        if dp[i-1]>=0:
            dp[i]=dp[i-1]+nums[i]
        else:
            dp[i]=nums[i]
    return max(dp)
nums= [-2,1,-3,4,-1,2,1,-5,4]
print(maxsub(nums))
**343、整数拆分

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jY6Bmjdj-1650247907473)(C:\Users\Tian\AppData\Roaming\Typora\typora-user-images\image-20220402140320549.png)]

#4、整数拆分
'''
1、当n<=3时,返回(1,n-1)
2、当n>3时,b=n%3 a=n//3
            b=0,n^a
            b=1,n^(a-1)*4
            b=2,3^a*2
'''
def intergerbreak(n):
    if n<=3:
        return 1*(n-1)
    a,b=n//3,n%3
    if b==0:
        return 3**a
    elif b==1:
        return 3**(a-1)*4
    else:
        return 3**a*2
print(intergerbreak(10))
def intergerbreak(n):
    dp=[0]*(n+1)
    for i in range(2,n+1):
        for j in range(i):
            dp[i]=max(dp[i],j*(i-j),j*dp[i-j])
    return dp[n]
print(intergerbreak(10))
**583、两个字符串的删除操作
def mindistance(word1,word2):
    #dp[i][j]记录第一个字符串的i位置之前的字符串,第二个字符串的j位置
    m=len(word1)
    n=len(word2)
    dp=[[0]*(n+1) for i in range(m+1)]
    for i in range(1, m + 1):
            dp[i][0] = i
    for j in range(1, n + 1):
            dp[0][j] = j
    for i in range(1,m+1):
        for j in range(1,n+1):
            if word1[i-1]==word2[j-1]:
                dp[i][j]=dp[i-1][j-1]
            else:
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1
    return dp[m][n]
print(mindistance('sea','eat'))
**646、最长数对链
def findLongest(pairs):
    n=len(pairs)
    dp=[1]*n
    pairs.sort(key=lambda a:a[0])
    for i in range(n):
        for j in range(i):
            if pairs[i][0]>pairs[j][1]:
                dp[i]=max(dp[i],dp[j]+1)
    return max(dp)
pairs=[[1,2], [2,3], [3,4]]
print(findLongest(pairs))
**376、摆动序列
def wiggle(nums):
        n=len(nums)
        if n==1:
            return 1
        down=1
        up=1
        for i in range(1,n):
            if nums[i]>nums[i-1]:
                up=down+1
            elif nums[i]<nums[i-1]:
                down=up+1
        return max(down,up)
nums=[0,0]
print(wiggle(nums))
**494、目标和
def findtarget(nums,target):
    if (sum(nums)+target)%2!=0:
        return 0
    total=(target+sum(nums))//2
    if total<0:
        return 0
    n=len(nums)
    #dp[i][j]表示在数组前i个数中选取元素,最终和差为j的方案数
    dp=[[0]*(total+1) for _ in range(n)]
    dp[0][nums[0]]=1
    dp[0][0]+=1
    for i in range(1,n):
        for j in range(total+1):#迭代正数和
            if j>=nums[i]:#装或不装nums[i]
                dp[i][j]=dp[i-1][j-nums[i]]+dp[i-1][j]
            else:
                dp[i][j]=dp[i-1][j]
    return dp[-1][-1]
            
nums=[1,1,1,1,1]
print(findtarget(nums,3))
#正数和=(sum(nums)+target)/2
def find2(nums,target):
    if (sum(nums)+target)!=0 or sum(nums)<target:
        return 0
    ztotal=(target+sum(nums))//2
    if total<0:
        return 0
    dp=[0 for _ in range(total+1)]
    for j in range(total+1):
        if nums[0]==j:
            dp[j]=1
    dp[0]+=1#装满0容量背包的一种方法就是装0个物体
    for num in nums[1:]:
        for j in range(total,num-1,-1):
            dp[j]=dp[j]+dp[j-num]
    return dp[-1]
714、买卖股票的最佳时机含手续费
def maxprofit(prices,fee):
    '''
    dp[i][0]表示第i天,持有股票-当天买入或者前i天买入
    dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i])    
    dp[i][1]表示第i天没有股票--之前卖出或者当天卖出
    dp[i][1]=max(dp[i-1][0]-fee+prices[i],dp[i-1][1])
    '''
    n=len(prices)
    if n==1:
        return 0
    dp=[[0,0] for i in range(n)]
    dp[0]=[-prices[0],0]
    for i in range(1,n):
        dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]) 
        dp[i][1]=max(dp[i-1][0]-fee+prices[i],dp[i-1][1])
    return dp[-1][-1]
prices=[1,3,7,5,10,3]
print(maxprofit(prices,3))

#使用变量进行状态转移
class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        n = len(prices)
        sell, buy = 0, -prices[0]
        for i in range(1, n):
            sell, buy = max(sell, buy + prices[i] - fee), max(buy, sell - prices[i])
        return sell

8、数学问题

#1、公倍数与公因数

204、计数质数
#1、埃拉托斯特尼筛法
def count_primes_py(n):
    #最小的质数是2
    if n<2:
        return 0
    isPrime=[1]*n
    isPrime[0]=isPrime[1]=0
    for i in range(2,int(n**0.5)+1):
        if isPrime[i]:
            isPrime[i*i:n:i]=[0]*len(isPrime[i*i:n:i])
    return sum(isPrime)
print(count_primes_py(10))
504、七进制数
def converttobase(num):
    if num==0:
        return '0'
    fu=num<0
    if fu:
        num=-num
    ans=''
    while num:
        a,b=num//7,num%7
        ans=str(b)+ans
        num=a
    return '-'+ans if fu else ans 
num=-7
print(converttobase(num))
172、阶乘后的零
#阶乘后的零大部分来源于2*5,但是2的数量往往多与5的数量,在这里我们统计5的数量
def trailing(n):
    return 0 if n==0 else n//5+trailing(n//5)
print(trailing(12))
415、字符串相加
def addstrings(num1,num2):
        res = ""
        i, j, carry = len(num1) - 1, len(num2) - 1, 0
        while i >= 0 or j >= 0:
            n1 = int(num1[i]) if i >= 0 else 0
            n2 = int(num2[j]) if j >= 0 else 0
            tmp = n1 + n2 + carry
            carry = tmp // 10
            res = str(tmp % 10) + res
            i, j = i - 1, j - 1
        return "1" + res if carry else res
print(addstrings('11','123'))
326、3的幂
def isPowerOfThree(n):
        while n and n % 3 == 0:
            n //= 3
        return n == 1
384、打乱数组
#knuth洗牌算法:通过随机交换位置来实现随机打乱
'''
等概率选择每个位置应该填哪个数。
具体来说,我们先在0 ~ n-1中随机选一个坐标,将它作为第一个,和第一个交换位置; (每个数被选到的概率是 1/n)
剩下的n-1个数里,继续随机一个1 ~ n-1的坐标,将它作为第二个,和第二个交换位置;(每个数被选到的概率为第一次没被选到且第二次被选到 [(n-1)/n]*[1/(n-1)]
。。。
以此类推。
每个数填到每个位置是等概率的,都是1/n
'''
class Solution:
    def __init__(self,nums:list[int]):
        self.nums=nums
    def reset(self)->List[int]:
        return self.nums
    def shuffle(self)->List[int]:
        self.temp=list(self.nums)
        for i in range(len(self.nums)):
            idx=random.randint(i,len(self.nums)-1)
            self.temp[i],self.temp[idx]=self.temp[idx],self.temp[i]
        return self.temp
168、Excel表列名称

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6XLT6NPj-1650247907474)(C:\Users\Tian\AppData\Roaming\Typora\typora-user-images\image-20220331093508029.png)]

def converttitle(columnNumber):
    ans=''
    while columnNumber>0:
        a0=(columnNumber-1)%26+1
        ans=chr(a0-1+ord('A'))+ans
        columnNumber=(columnNumber-a0)//26
    return ans
print(converttitle(701))
67、二进制求和
def addBinary(a,b):
    return bin(int(a,2)+int(b,2))[2:]
print(addBinary('11','1'))
238、除自身以外数组的成绩
def product(nums):
    n=len(nums)
    a=[1]*n
    b=[1]*n
    for i in range(1,n):
        a[i]=a[i-1]*nums[i-1]
    for i in range(n-2,-1,-1):
        b[i]=b[i+1]*nums[i+1]
    for i in range(n):
        a[i]*=b[i]
    return a
nums=[-1,1,0,-3,3]
print(product(nums))
462、最少移动次数使数组元素相等
#排序选择中位数需要移动的位数最少
def minMoves(nums):
    nums.sort()
    n=len(nums)
    middle=nums[n//2]
    move=0
    for num in nums:
        move+=abs(num-middle)
    return move
print(minMoves([1,2,3]))
470、用rand7实现rand10
#randN可以等概率的生成[1,N]范围的随机数
#(randX()-1)*y+randY()可以等概率生成[1,X*Y]范围的随机数
def rand10(self):
        """
        :rtype: int
        """
        while True:
             a = rand7();
             b = rand7()
             num = (a-1)*7 + b
             if num <= 40:
                 return num % 10 + 1 
202、快乐数
'''
1、找到快乐数
2、没有快乐数,形成环路,造成死循环
创建一个满指针,一次走一步,在创建一个快指针,一次走两步
'''
def isHappy(n):
    def get_next(number):
        total_sum=0
        while number>0:
            number,digit=divmod(number,10)
            total_sum+=digit**2
        return total_sum
    slow_runner=n
    fast_runner=get_next(n)
    while fast_runner!=1 and slow_runner!=fast_runner:
        slow_runner=get_next(slow_runner)
        fast_runner=get_next(get_next(fast_runner))
    return fast_runner==1
print(isHappy(19))    
169、多数元素–Boyer_morre投票算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z8fMTmLM-1650247907474)(C:\Users\Tian\AppData\Roaming\Typora\typora-user-images\image-20220331161705637.png)]

def majorityelement(nums):
    count=0
    candidate=None
    for num in nums:
        if count==0:
            candidate=num
        count+=(1 if num==candidate else -1)
    return candidate
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值