7月力扣,刷就完事了

前言

头一次刷力扣,卑微的很,觉得自己会的太少了,不过要一直刷下去,提高自我代码水平,hhhh,有什么好的思路或建议,欢迎大家评论

路径总和(7.8)

力扣菜鸟112题,不多BB上图
在这里插入图片描述
我的思路:
用深度优先遍历,传的参数有树的下一个节点和当前的sum值,如果没有左或右节点则判断是否加起来等于sum,不等于则返回sum 等于返回true
分析:
实现起来难的很,首先力扣函数给定布尔型,不好调参数回传,如果非要用得另写一个函数,加法得判断是否和是sum,不是则返回,不如用减法,减法的优点在于不用回传,因为sum已经给定,所以减法判断是否为0即可,传的参数不用返回上一级,若不为0时则返回False,未终止时继续,

答案解析

1.递归:深度优先

实现的代码:

    def hasPathsum(self,root : TreeNode,sum :int):
        if not root:
            return False
        if not root.left and not root.right:
            return root.val==sum
        return self.hasPathsum (root.left,sum-root.val) or self.hasPathsum(root.right,sum-root.val)

需要判断根节点是否为空,再判断是否有叶子节点,没有的话判断sum的值是否减小到与此节点值相同,如果有叶子节点,传sum到叶子结点中。递归还是又快又简单,空间复杂度变成O(h),h代表树的高度。

2.队列:广度优先

队列采用广度优先算法,

class Solution:
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if not root:
            return False
        que_node = collections.deque([root])
        que_val = collections.deque([root.val])
        while que_node:
            now = que_node.popleft()
            temp = que_val.popleft()
            if not now.left and not now.right:
                if temp == sum:
                    return True
                continue
            if now.left:
                que_node.append(now.left)
                que_val.append(now.left.val + temp)
            if now.right:
                que_node.append(now.right)
                que_val.append(now.right.val + temp)
        return False

队列使用方法:见链接
使用两个队列,一个放节点,另一个放值,然后操作。使用append函数依次叠加,有叶子节点就继续加,没有才判断

3.遇到的问题

首先是类的建立和调用,出现如下问题:
在这里插入图片描述
这个问题之前在写密码学作业就碰到过,今天终于搞懂了,原来是我类实体化参数没写对:

#这是我先写的错误的类实体化
j = Solution.hasPathSum(a, 22)
    print(j)
#正确的应该是这样
j = Solution().hasPathSum(a, 22)

类实体化一定要加括号,否则类并没有实体化,所以调不了参数
在做题时,我在想,力扣给出的初始化是图中这样的,但是怎么把这些转化为代码进行初始化
在这里插入图片描述
可能大佬们都觉得太简单了,所以我简单地实体化了一下:

	a=TreeNode(5)	 ,a.left = b
    b=TreeNode(4)	 ,a.right=f
    c=TreeNode(11)	 ,b.left=c
    d = TreeNode(7)  ,c.left=d	
    e = TreeNode(2)  ,c.right=e	
    f = TreeNode(8)  ,f.left=g 
    g = TreeNode(13) ,f.right=h
    h = TreeNode(4)  ,h.right=i 
    i= TreeNode(1)   

这也太丑了,而且和力扣的输入根本对不上,所以我再次思考,观察他的输入,是层序遍历,所以猜测可能是使用队列进行初始化,所以需要一个队列来建立初始化的树,具体代码如下

class tree():
    def __init__(self, root=None):
        self.root = root
    def build(self, num):
        node = TreeNode(num)
        if not self.root: # 判断根节点是否为空
            self.root = node
        else:
            queue = [self.root]
            while queue: #添加
                now = queue.pop(0)
                if not now.left :
                    now.left = node
                    return
                elif not now.right:
                    now.right = node
                    return
                else:
                    queue.append(now.left)
                    queue.append(now.right)

if __name__=='__main__':                   
tree = tree()
    a = [5, 4, 8, 11, 0, 13, 4, 7, 2, 0, 0, 0, 1]
    for i in range(len(a)):
        tree.build(a[i])
    j=Solution().hasPathSum(tree.root,22)

这次执行出来就很给力了,a即是力扣的输入类型,把null换成0,要不然append无法把字符和数字合并,如果生成一个接口,则可以把null换成0,成功完成。

4.收获

收获了很多,还是得多练习,熟悉算法思想,加法减法互相变换,对于树的理解还不够深刻,没有熟练掌握各种遍历方法。不知道啥时候收获我可以说我贼熟练,哈哈哈。

list相关题和其他题(7.8-7.11)

这几天其实刷了一些,但是都是简单题,最后来个总结,详细写自己收获大的

给定顺序创建数组

老规矩,见图,不多BB
在这里插入图片描述
赶赶单单

class Solution:
    def createTargetArray(self, nums: List[int], index: List[int]) -> List[int]:
        target=[]
        for i in range(len(index)):
            target.insert(index[i],nums[i])
        return target

这里列出list的相关操作,感觉很多函数需要熟练掌握

  • 基本操作:
  • len():获取长度,这个函数啊可以用在很多方面, 但需要注意,之前做过的一个题
  • 加法,乘法,in,max ,min
a=7,bina=bin(a)#111
len(bina)=5#加上符号位
a.bit_length=3#只有数转化的位
[1,2,3]+[4,5,6]=[1,2,3,4,5,6]
[1]*4=[1,1,1,1]
3 in [1,2,3,4] =True
  • name[n:m:s] s:步长 隔多少个元素取一次,n和m缺省表示从头/尾,s+左到右,s-右到左
  • list.append(元素/列表)
    功能:在列表中末尾添加新的元素【在原本的列表中追加元素】
    注意:append()中的值可以是列表也可以是普通元素
    Append()总是添加一个元素,这个元素可以是元素或列表,添加列表的话则升维
list=[1,2,3,4]
a=[1,2,3]
list.append(5)#list=[1,2,3,4,5]
list.append(a)#list=[1,2,3,4,5,[1,2,3]]
  • list.extend(列表)
    功能:在列表的末尾一次性追加另外一个列表中的多个值
    注意:extend()中的值只能是列表/元组,不能是元素
  • list.insert(下标值, 元素/列表)
    功能:在下标处插入元素,不覆盖原本的数据,原数据向后顺延
    注意:插入的数据可以是元素也可以为列表,原理同append
  • list.pop(下标值)
    功能:移除列表中指定下标处的元素(默认移除最后一个元素),并返回移除的数据
  • list.remove(元素)
    功能:移除列表中的某个元素第一个匹配结果
  • list.clear()
    功能:清除列表中所有的数据
  • list.index(object[, start][, stop])
    功能:从指定的范围的列表中找出某个值第一匹配的索引值 ;若不指定范围,则默认是整个列表
    注意:若在列表中找不到这个元素,则会报错。
    ps:此函数应该是采取遍历的方法来找元素,而不是通过哈希等方式。
  • list.count(元素)
    功能:查看元素在列表中出现的次数
  • list.reverse()
    功能:将列表中的元素倒叙,操作原列表,不返回新的列表
  • list.sort(reverse=False)
    功能:将list中的元素进行升序排列(默认false),当reverse为True的时候,降序排列。
    请注意:这个函数采用的排序方式为TimeSort,这是一种结合了合并排序(merge sort)和插入排序(insertion sort)而得出的排序算法,时间复杂度最小为O(n),具体参见大佬的博客
  • list(元组):功能:将元组转为列表 即list((1,2,3,4))
  • del list[n] 删除指定下标对应的元素

其他的没啥了,list是python经常使用的一种数据结构,需要熟练掌握。熟练掌握从了解开始,不多BB

数字问题

这俩题类似,如图
在这里插入图片描述
在这里插入图片描述
看题,不用多说,两个循环,就可以解决

class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:
        b=0
        for i in range (len(nums)):
            for j in range(i+1,len(nums)):
                if nums[j]<nums[i]:
                    b=b+1
            nums[i]=b
            b=0
        return nums

但是事情有这么简单吗??
在这里插入图片描述
这下舒服了,复杂度太高了,所以优化。自己想到的优化方法:1.用更好地排序方法2.哈希查找
所以换了一套自带函数完成

class Solution:
    def countSmaller(self, nums) :
        a=nums.copy()
        a.sort()
        for i in range(len(nums)):
            nums[i]=a.index(nums[i])
            a.pop(nums[i])
        return nums

这里的问题在于a=nums,如果直接写a=nums,则会错误,这就涉及到list的复制

  • 映射 a=nums就叫映射,如果nums变,则a也会变,共用同一个内存空间
  • 浅复制 a=nums.copy()这是浅复制,注意:只适用于一维列表,对于一维列表重新开辟了一块内存空间,但若出现二维列表的情况下,因为二维列表存放在一维列表中存放的是列表的地址,因此,若出现二维列表相当于间接的引用了同一块内存区域(即二维列表还是共用内存)
  • 深复制 a= nums.deepcopy() 完全内存拷贝,相当于将list1中的所有列表元素重新复制了一份,对于多维的也重新开辟了新的内存空间

a.sort不用多说了,上边有,复杂度杠杠好!
还有一个有趣的python模块:bisect,具体可以自己查,也是创建表并排序,其优点在于查找和插入
我写的这个代码关键在for循环和pop函数,这两个复杂度为O(n),sort函数虽好,但是没用
所以使用自己编的函数来完成降复杂度
主要的排序函数常用的有

方法时间复杂度(平均)时间复杂度(最差)时间复杂度(最好)空间复杂度稳定性
冒泡排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)稳定
选择排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)不稳定
插入排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)稳定
希尔排序 O ( n 1 , 3 ) O(n^{1,3}) O(n1,3) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)不稳定
快速排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n 2 ) O(n^2) O(n2) O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn)不稳定
归并排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn) O ( n ) O(n) O(n)稳定
堆排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn) O ( 1 ) O(1) O(1)不稳定

接下来写一下这几个算法深入理解一下

冒泡排序

思路:从前向后,从小到大,对比相邻的两个数,把小的放在前边,最大的一定到了最后,然后循环这个,倒数第二大的放到倒数第二,循环。中间加一个布尔型参数判断是否经过了交换,若没交换,则直接返回数组(这步是时间复杂度O(n)的关键)

def mopao(a):
	for i in range(len(a)):#次数
		c=False
		b=0
		for j in range(len(a)-i-1):
			c=False
			if a[j+1]<a[j]:
				a[j],a[j+1]=a[j+1],a[j]
				c=True
		if c==False:
			return a

选择排序

最简单的,找到最大放最后,然后回去重新找,再放到最后。不多BB,不会的是派大星,上代码!

def xuanze(a):
    for i in range(len(a)):
        b = i
        for j in range(i,len(a)):
            if a[j] < a[b]:
                b = j
        a[i],a[b]=a[b],a[i]
    return a

手撕还是差点意思,这么简单地代码手撕忘了+return 调试了才发现,下次要认真点

插入排序

插入排序是递归的一种算法,从第一个元素开始,和前边的比较,小的话互换位置,换到比前一个大为止(前边的已经排好),然后循环,直到最后一个元素,

def charu(a):
    for j in range(1, len(a)):
        key = a[j]
        i = j - 1
        while i >= 0 and a[i] > key:
            a[i + 1] = a[i]
            i = i - 1
        a[i + 1] = key
    return a

希尔排序

希尔排序和希尔密码啥相同点都没有,这根本就是两个名字不同的人(hill 和shell)发明的不同东西,很蛋疼,希尔排序的核心是分段,有一个分段list,list最简单的是len(a)//2,比如有8个值,第0个和第4个先比,第1个和第5个比,然后下一轮变成0和2比,1和3比,2和4比,这样排序可以基本有序,直到最后间隔为1,有序。可以看出,当

def shellSort(a):
    b = len(a) // 2
    while b > 0:
        for i in range(b, len(a)):
            temp = a[i]
            j = i
            while j >= b and a[j - b] > temp:
                a[j] = a[j - b]
                j -= b
            a[j] = temp
        b = b // 2
    return a

快速排序

快排的关键是找一个值,比这个数小的全放前边,比这个数大的全放后边,然后再在这分好的两边重新来这一套,用递归写,上代码

def partition(arr, low, high):
    i = (low - 1)  # 最小元素索引
    pivot = arr[high]
    for j in range(low, high):
        if arr[j] <= pivot:# 当前元素小于或等于 pivot
            i = i + 1
            arr[i], arr[j] = arr[j], arr[i]
    arr[i + 1], arr[high] = arr[high], arr[i + 1]
    return (i + 1)

def quickSort(arr, low, high):
    if low < high:
        pi = partition(arr, low, high)
        quickSort(arr, low, pi - 1)
        quickSort(arr, pi + 1, high)

归并排序

归并排序也是递归,和快速排序一样,是分治法的体现,快速排序是先分两段,前段全是比基准小的,归并是先分段,分到不能分为止,再向回靠拢,二叉树后序遍历一样,

def merge(arr, l, m, r): 
    n1 = m - l + 1
    n2 = r- m   
    # 创建临时数组
    L = [0] * (n1)
    R = [0] * (n2) 
    # 拷贝数据到临时数组 arrays L[] 和 R[] 
    for i in range(0 , n1): 
        L[i] = arr[l + i]   
    for j in range(0 , n2): 
        R[j] = arr[m + 1 + j]   
    # 归并临时数组到 arr[l..r] 
    i = 0     # 初始化第一个子数组的索引
    j = 0     # 初始化第二个子数组的索引
    k = l     # 初始归并子数组的索引 
    while i < n1 and j < n2 : 
        if L[i] <= R[j]: 
            arr[k] = L[i] 
            i += 1
        else: 
            arr[k] = R[j] 
            j += 1
        k += 1  
    # 拷贝 L[] 的保留元素
    while i < n1: 
        arr[k] = L[i] 
        i += 1
        k += 1  
    # 拷贝 R[] 的保留元素
    while j < n2: 
        arr[k] = R[j] 
        j += 1
        k += 1  
def mergeSort(arr,l,r): 
    if l < r:           
        m = int((l+(r-1))/2)         
        mergeSort(arr, l, m) 
        mergeSort(arr, m+1, r) 
        merge(arr, l, m, r) 

堆排序

堆排序关键是每个叶子结点都小于父节点,然后把最大的放在上边之后,将这个最大的值与末尾元素交换,接着重复这些操作

def heapify(arr, n, i): 
    largest = i  
    l = 2 * i + 1     # left = 2*i + 1 
    r = 2 * i + 2     # right = 2*i + 2   
    if l < n and arr[i] < arr[l]: 
        largest = l   
    if r < n and arr[largest] < arr[r]: 
        largest = r   
    if largest != i: 
        arr[i],arr[largest] = arr[largest],arr[i]  # 交换  
        heapify(arr, n, largest)   
def heapSort(arr): 
    n = len(arr)   
    # Build a maxheap. 
    for i in range(n, -1, -1): 
        heapify(arr, n, i) 
    # 一个个交换元素
    for i in range(n-1, 0, -1): 
        arr[i], arr[0] = arr[0], arr[i]   # 交换
        heapify(arr, i, 0) 

排序害得好好练练,熟练掌握 和计算复杂度。
最后附上上色的小画,加油

在这里插入图片描述

7.12-7.16+7.18(PS:17号做项目活,另说)

小题

在这里插入图片描述

class Solution1:
    def maxProduct(self, nums) :
        a=max(nums)
        nums.pop(nums.index(a))
        c=max(nums)
        return (a-1)*(c-1)

没啥可说的 按部就班,主要是看数组的操作,pop可以移除某一数字,index()里边是值,找到第一个匹配的这个值,如果给定位置的话可以用delete删除
在这里插入图片描述

class Solution:
    def canMakeArithmeticProgression(self, arr) :
        a=arr
        a.sort()
        b=a[1]-a[0]
        for i in range(1,len(a)):
            if a[i]!=a[i-1]+b:
                return False
        return True

简简单单,但学会了一点,要学会用数学方法来判断,学的数学应用到这里
在这里插入图片描述
在这里插入图片描述
解这个题思路:数组A依次向后数,遍历B,如果有相同的就停止,将这个值放到新的数组,然后A和B都pop这个值
大佬使用了哈希表,现在记录一下哈希表的使用方式:

import collections
class Solution:
    def intersect(self, nums1, nums2):
        if len(nums1) > len(nums2):
            return self.intersect(nums2, nums1)  # 将小的放前边,大的放后边
        m = collections.Counter()  # 初始化计数器容器
        for num in nums1:
            m[num] += 1  # 计算大的数组里边每个值出现次数
        intersection = list()
        for num in nums2:
            count = m.get(num, 0)
            if count > 0:  # 字典的get方法,若不在则默认值为0
                intersection.append(num)
                m[num] -= 1 #num键值减一
                if m[num] == 0:#num键值等于0则删掉这个键
                    m.pop(num)
        return intersection

哈希表并不是由哈希值构成的表,主键只需要使用不会冲突的数据就可以,然后就可以构成一个查找表,哈希排序是主键+位置,这个哈希表是主键+出现次数,然后再对比两个哈希表中的主键,如果一样,则对比次数,找到小的次数,
字典一般用于哈希查找,哈希查找并不需要转成哈希值,只需要制造主键和内容即可,具体见博客
若是排好序,可以对比两个数组,如果相同则输出,若不同则A数组向前搜索是否有相同的项,若没有,B数组向前一个,重复。
:= Python3.8新出的功能

还有更简单的一个方法,还是使用collection类,直接使用Counter类,来对两个数组分好类,然后取交集即可

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        ans = []
        counter_1 = collections.Counter(nums1)
        counter_2 = collections.Counter(nums2)
        for element, count in (counter_1 & counter_2).items():
            ans += [element]*count #主键出现的次数
        return ans  

在这里插入图片描述
返回倒数第几个就先走几个,然后两个一起走,直到先走的为空。

class Solution:
    def kthToLast(self, head: ListNode, k: int) -> int:
        c=head
        for i in range(k):
            c=c.next
        while c:
            head=head.next
            c=c.next
        return(head.val)

在这里插入图片描述
这种题可以使用笨办法,从头往后找,看第二个是否在第一个中有,直到找到一个不在第一个中存在的值,即是终点。
根据上边的思路,是不是可以视作从集合中找到不存在另一个集合中的数,所以代码如下:

class Solution(object):
    def destCity(self, paths):
        allCity = set() #所有城市的集合	
        beginCity = set() #所有出发城市的集合
        for path in paths:
            allCity.add(path[0])
            allCity.add(path[1])
            beginCity.add(path[0])
        return (allCity - beginCity).pop()

集合操作使用set()函数,set即集合,其中不包含相同元素,使用add(key)往集合中添加元素,重复的元素自动过滤,通过remove(key)方法可以删除元素,set还可以像数学上那样求交集和并集,还可以进行加减乘除操作

动态规划

在这里插入图片描述
拿到题进行分析,类似下一步要看上一步结果的题要用动态规划。动态规划一般正着看不好解就倒着看,找到状态转移方程。这个题如果正着做,可以每一轮都找最小的值,加到下一行,然后继续。倒着看就是从下往上数,把最后一行的小的数加到上一行,然后逐行向上加。
刚开始没读懂题,以为是贪心算法遍历整个数组长度,但这题意味着当在第二行选择3后,第三行只能在6和5里边选,刚开始我写的是这样:(PS:我发现我做动态规划题总没读懂…)

class Solution:
    def minimumTotal(self, triangle):
        a=0
        for i in range(len(triangle)):
            a= min(triangle[i][0:i+1])+a
        return a

修改之后是这样

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        for i in range(1,len(triangle)):
            triangle[i][0]=triangle[i-1][0]+triangle[i][0]
            triangle[i][i]=triangle[i-1][i-1]+triangle[i][i]
            for j in range(1,i):
                triangle[i][j]=min(triangle[i-1][j],triangle[i-1][j-1])+triangle[i][j]
        return min(triangle[len(triangle)-1])

大佬的是这样,运行起来更快:

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        n = len(triangle)
        f = [[0] * n for _ in range(n)]
        f[0][0] = triangle[0][0]
        for i in range(1, n):
            f[i][0] = f[i - 1][0] + triangle[i][0]
            for j in range(1, i):
                f[i][j] = min(f[i - 1][j - 1], f[i - 1][j]) + triangle[i][j]
            f[i][i] = f[i - 1][i - 1] + triangle[i][i]
        return min(f[n - 1])

删除括号

在这里插入图片描述
这个是删除最外层,所以先识别’(‘个数,识别一个加一个,大于1的话就输出,再识别’)'个数,识别一个减一个,然后小于1的时候停止输出,原理就这,上代码:

class Solution:
    def removeOuterParentheses(self, S):
        str1=''
        q=0
        for i in S:
            if i=='(':
                q=q+1
                if q>1:str1=str1+'('
            else:
                q=q-1
                if q>0:str1=str1+')'
        return str1

大佬用的栈,一个道理,写不出花来,上代码!

class Solution:
    def removeOuterParentheses(self, S: str) -> str:
        stack = []
        result = ''
        for i in S:
            if i == '(':
                stack.append(i)
                if len(stack) > 1:
                    result += '('
            else:
                stack.pop()
                if len(stack) != 0:
                    result += ')'
        return result

替换空格

在这里插入图片描述
这题简简单单,两个思路,一个是把字符串遍历,不是空格的放进新的字符串,空格变成%20
第二个思路是直接使用replace函数,

class Solution:
    def replaceSpace(self, s: str) -> str:
        return(s.replace(' ', '%20'))

注意一点:字符串为不可变类型,每加一个字符就会成为一个新的字符串,太耗内存
此方法用列表来完成对新字符串的储存,最后再用join函数将列表转化为字符串。(从而避免产生多个字符串浪费内存)

class Solution:
    def replaceSpace(self, s: str) -> str:
        s1 = []                       
        for c in s:                  
            if c == ' ':            
                s1.append('%20')        
            else:                       
                s1.append(c)
        return ''.join(s1)     

虽然简单,但是学到了一些,比如转义字符都有哪些:

\(在行尾时)	续行符
\\	反斜杠符号
\'	单引号
\"	双引号
\a	响铃
\b	退格(Backspace)
\e	转义
\000	空
\n	换行
\v	纵向制表符
\t	横向制表符
\r	回车
\f	换页
\oyy	八进制数,yy代表的字符,例如:\o12代表换行
\xyy	十六进制数,yy代表的字符,例如:\x0a代表换行
\other	其它的字符以普通格式输出
字符串前加r b u f代表原先字符串,byte类型字符串,Unicode类型,和加大括号
首先声明,退格符、换行符、回车符、制表符这都是对光标的操作,其本身不会对已有内容进行修改和擦除等操作。
退格符,相当于光标向前移动,如果后续没有内容或者内容不足会导致残痕效果。即如果退格符后没有内容则无影响,如果有则在光标处进行替换。这就是说,退格符退的是光标,并不会对现有内容进行擦除。这个和我们键盘上的退格按键是不同的。

replace 函数:str.replace(old, new[, max])
old -- 将被替换的子字符串。
new -- 新字符串,用于替换old子字符串。
max -- 可选字符串, 替换不超过 max

在这里插入图片描述

class Solution:
    def hammingDistance(self, x: int, y: int) -> int:
        a=bin(x^y)
        c=collections.Counter(a)
        return c['1']

汉明距没啥可说的,一般用来校验

7.18打卡题

在这里插入图片描述
思路:笨办法:先对字符串3前边匹配,找到和12匹配,的一个,然后匹配另一个,接着循环,知道遍历完字符串3
新的思路:将字符串1和2的第一位取出来,和3的1和len(s1)位取出来对比,然后2和len(s1)+1 一直后推
优化:先用3来看,看完第一位匹配的是s1还是s2 然后3的1位到s1位和s1+s2位到s1+s1+s2位是否相同,(可以一位一位对比,减小复杂度)
再次优化:切片,化为二维数组,还是先匹配s1或s2,然后切片,例如先匹配的s1,然后按照s1长度切片,再按照s2切片,再s1切片,然后放到二维数组中,用Counter计算出现次数,只有两项,再判断次数,若次数一致或后识别的比前识别的少1,则再识别顺序,看是否是一个接一个
自己想的思路,看起来挺靠谱,开始写:

class Solution:
    def isInterleave(self, s1: str, s2: str, s3: str) :
        if len(s2)>len(s1):
            s1,s2=s2,s1#长的放在s1
        s1_len=len(s1)
        s2_len=len(s2)
        s3_len=len(s3)
        tem=s3_len%(s1_len+s2_len)
        if tem==0:
            tem=s3_len//(s1_len+s2_len)
        elif tem==s1_len or tem==s2_len:
            tem=s3_len//(s1_len+s2_len)+1
        else:
            return False
        temstr=[]
        if s1==s3[0:s1_len]: #将识别的字符放在s1
            pass #不执行任何操作
        elif s2==s3[0:s2_len]:
            s1,s2,s1_len,s2_len=s2,s1,s2_len,s1_len
        else:
            return False
        for i in range(tem):
            if s3[i*(s1_len+s2_len):s1_len+i*(s1_len+s2_len)] == s1 and s3[s1_len+i*(s1_len+s2_len):s1_len+s2_len+i*(s1_len+s2_len)]==s2:
                pass
            else:
                return False
        return True

做完了一看 白吉尔扯,我佛了原来题读错了,交错组成可以是这样:
“aabcc”
“dbbca”
“aadbbcbcac”
这样也行?的确行,裂开
俺以为的交错组成:S1 S2 S1 S2…
所以大改 RNM了,白在上边分析半天了,大佬的代码如下

class Solution(object):
    def isInterleave(self, s1, s2, s3):
        if len(s1)+len(s2) != len(s3):
            return False
        if len(s1) == len(s2) == len(s3) == 0:
            return True
        col = len(s2) + 1
        row = len(s1) + 1
        dp = [[False]*col for i in range(row)]
        dp[0][0] = True
        for i in range(row):
            for j in range(col):
                if i > 0:
                    dp[i][j] |= ( dp[i-1][j] and s1[i-1] == s3[i+j-1] )
                if j > 0:
                    dp[i][j] |= ( dp[i][j-1] and s2[j-1] == s3[i+j-1] )
        return dp[-1][-1]

原理就是像蜘蛛纸牌一样,从后往前数,分别于s1,s2匹配,匹配不到的话就卡死。
学到了很多:pass,if elif,还有python运算符:

运算符:
=	简单的赋值运算符	c = a + b 将 a + b 的运算结果赋值为 c
+=	加法赋值运算符	c += a 等效于 c = c + a
-=	减法赋值运算符	c -= a 等效于 c = c - a
*=	乘法赋值运算符	c *= a 等效于 c = c * a
/=	除法赋值运算符	c /= a 等效于 c = c / a
%=	取模赋值运算符	c %= a 等效于 c = c % a
**=	幂赋值运算符	c **= a 等效于 c = c ** a
//=	取整除赋值运算符	c //= a 等效于 c = c // a
:=	海象运算符,可在表达式内部为变量赋值。Python3.8 版本新增运算符。
is	is 是判断两个标识符是不是引用自一个对象	x is y, 类似 id(x) == id(y) , 如果引用的是同一个对象则返回 True,否则返回 False
is not	is not 是判断两个标识符是不是引用自不同对象
a|=2等价于a=a|2(按位或)
a>>=2等价于a=a>>2(右移3位)
a<<=2等价于a=a<<2(左移3位)

基本这几天就刷了这些,主要在做实验室的一个项目,其中学到了很多,具体内容另开一篇来说,哈哈哈,请听下回分解

7.19-7.31

好久没总结了,哈哈。不多BB,上题:

7.19打卡题

在这里插入图片描述
现在是8月1日,今天再回头看这个题有点想不起来了都,但是后来一想,又想起来了
这个题我刚开始的思路是这样的,先看中间的值(除了最边里的两个元素),不管怎么说,总是取出最小的一个。因为戳最小的那个,最小的就不会被加两次,戳别的的话肯定比这种方法要小。其实可以在开头和末尾都加上一个1,这样的话就不用管开头了
其实这个思路不对,因为没有考虑到后边的值对这个值的影响
其实可以看出来,四个数abcd,对bc来说,到底戳b还是戳c可以转换为下边的方程1/a+1/c>1/b+1/d 如果满足这个方程则戳c,但是问题就在ad上,如果后边的值对这两个值造成影响,使ad变化,这个方程就会有另一个解。
这个方程只是对加和的化简,并不具有连续性,所以不行
所以这块不行,必须用动态规划。
这个动态规划的思路我感觉自己理解还是有点混乱,所以放大佬的解题思路
这个解题思路是找到最后一个戳破的值,计算它的两边开区间,再将开区间设为新的,找到最后戳破的值,直到找到一个3个数的数组,戳中间的值,dp里保存的是区间的最大值
这次我就悟到了,真的很巧妙,哈哈哈

class Solution1:
    def maxCoins(self, nums):
        n = len(nums)
        rec = [[0] * (n + 2) for _ in range(n + 2)]
        val = [1] + nums + [1]

        for i in range(n - 1, -1, -1):
            for j in range(i + 2, n + 2):
                for k in range(i + 1, j):
                    total = val[i] * val[k] * val[j]
                    total += rec[i][k] + rec[k][j]
                    rec[i][j] = max(rec[i][j], total)
        return rec[0][n + 1]

代码虽然少,但是很巧妙,等复习的时候好好再看看。

7.20

在这里插入图片描述
想起来了,我的思路是找把原数组分成[0:targ/2],[targ/2,targ],然后从这里边匹配
但是题解思路是这样,先找到小于trag的部分,然后设置一个high,一个low,然后从mid判断如果targ-num[i]和mid,mid大就减小high,小就增大low,

class Solution:
    def twoSum(self, numbers,target) :
        n = len(numbers)
        for i in range(n):
            if numbers[i]>target:n=i+1
        for i in range(n):
            low, high = i + 1, n - 1
            while low <= high:
                mid = (low + high) // 2
                if numbers[mid] == target - numbers[i]:
                    return [i + 1, mid + 1]
                elif numbers[mid] > target - numbers[i]:
                    high = mid - 1
                else:
                    low = mid + 1
        return [-1, -1]

在这里插入图片描述
这题简单,判断RL,UD次数是否相等就行,可以使用collections.Counter类

import collections
class Solution:
    def judgeCircle(self, moves: str) -> bool:
        a=collections.Counter(moves)
        if a['L']-a['R']==0 and a['U']-a['D']==0:
            return True
        return False
str1="RL"
print(Solution().judgeCircle(str1))

count类用法见链接
在这里插入图片描述
我的思路,分成两半,然后左右互为相反数,奇数个的话中间是0,
题解思路:先遍历,从0到n-1,然后最后一个数是这些数加和的相反数…没谁了 彳亍!

class Solution:
    def sumZero(self, n: int) -> List[int]:
        ans = [x for x in range(n - 1)]
        ans.append(-sum(ans))
        return ans

在这里插入图片描述
这题没啥可说的 按照题的意思加减就行

class Solution:
    def finalPrices(self, prices) :
        sum=[]
        n=len(prices)
        for i in range(n):
            sum.append(prices[i])
            for j in range(i+1,n):
                if prices[j]<=prices[i]:
                    sum[i]-=prices[j]
                    break
        return sum

大佬用的栈

class Solution:
       def finalPrices(self,prices):
            n=len(prices)
            res=[prices[i] for i in range(n)]
            stack=[]
            for i in range(n):
                while stack and prices[stack[-1]]>=prices[i]:
                    res[stack[-1]]-=prices[i]
                    stack.pop()
                stack.append(i)
            return res

在这里插入图片描述
这个题没啥可说的构造哈希表,表的值就是数位的和,

class Solution1:
    def countLargestGroup(self, n) :
        hashMap = collections.Counter()
        for i in range(1, n + 1):
            key = sum([int(x) for x in str(i)])
            hashMap[key] += 1
        maxValue = max(hashMap.values())
        count = sum(1 for v in hashMap.values() if v == maxValue)
        return count

在这里插入图片描述
经典问题,写循环判断

class Solution:
    def numWaterBottles(self, numBottles: int, numExchange: int) -> int:
        res = numBottles
        while numBottles >=numExchange:
            res += numBottles//numExchange
            numBottles = numBottles//numExchange+ numBottles % numExchange       
        return res

或者使用数学的方式

class Solution:
    def numWaterBottles(self, numBottles: int, numExchange: int) -> int:
        '''
        #方法2:数学思想
        Exchange个空瓶 = 1个酒水 + 1个空瓶 ==> 1酒水 = (Ex-1)空瓶  
        ==> 1满酒 = 1酒水+1空瓶 = Ex空瓶
        那最初的酒水的价值= numB * Ex (单位:空瓶)
        由于最后的一个空瓶不能用来兑换,所以可用于兑换的总价值= numB * Ex -1 (单位:空瓶)
        故可得到的酒水= (numB*Ex-1)空瓶 / (Ex-1) 空瓶每酒水 = (numB*Ex-1) / (Ex-1) 酒水
        '''
        return (numBottles*numExchange-1) // (numExchange-1)

很明确了 ,数学方式还是简单
在这里插入图片描述
转成集合再转成数组,看长度有没有变

class Solution:
    def isUnique(self, astr: str) -> bool:
        return len(set(astr))==len(astr)

如果不使用额外空间的话,就需要使用位操作,其实和段子中的那个老鼠死了的问题一样

class Solution:
  def isUnique(self, astr: str) -> bool:
    mark = 0
    for char in astr:
      move_bit = ord(char) - ord('a')
      if (mark & (1 << move_bit)) != 0:
        return False
      else:
        mark |= (1 << move_bit)
    return True

7.21

在这里插入图片描述
递归完成,其实相当于遍历,思路很简单,但是不会写,这个要多写几次,然后记住

class Solution:
    def generateTrees(self, n: int) -> List[TreeNode]:
        def generateTrees(start, end):
            if start > end:
                return [None,]            
            allTrees = []
            for i in range(start, end + 1):  # 枚举可行根节点
                # 获得所有可行的左子树集合
                leftTrees = generateTrees(start, i - 1)                
                # 获得所有可行的右子树集合
                rightTrees = generateTrees(i + 1, end)                
                # 从左子树集合中选出一棵左子树,从右子树集合中选出一棵右子树,拼接到根节点上
                for l in leftTrees:
                    for r in rightTrees:
                        currTree = TreeNode(i)
                        currTree.left = l
                        currTree.right = r
                        allTrees.append(currTree)          
            return allTrees    
        return generateTrees(1, n) if n else []

在这里插入图片描述
这个题我的思路是使用极端值,第一个如果是I,代表增的话,那么前一个(也是第一个数)一定是最小,如果第一个是D的话,前一个一定是最大的那个,所以就按照这个顺序填数,最后一个数填剩下的那个

class Solution:
    def diStringMatch(self, S: str) :
        max_num=len(S)
        min_num=0
        b=[]
        for j in S:
            if j=='I':
                b.append(min_num)
                min_num+=1
            elif j=='D':
                b.append((max_num))
                max_num-=1
        return b+[min_num]
S="IDID"
print(Solution().diStringMatch(S))

在这里插入图片描述
首先看题,这个题我的思路是先将最后的条件转换,|arr1[i]-arr2[i]|<=d其实可以化成arr2[i]-d<=arr1<=arr2[i]+d
如果符合这个条件的数,就不是距离要求的数,所以可以制作距离内数的集合,然后看arr1里边的数有没有不在这里的,如果不在则加1(PS:这个思路我在题解里边没有看到过,不知道是是不是因为复杂度高啥的,嘿嘿)

class Solution:
    def findTheDistanceValue(self, arr1, arr2, d):
        b=set()
        c=0
        for t in arr2:
            for i in range(t-d,t+d+1):
                b.add(i)
        for i in range(len(arr1)):
            if arr1[i] not in b:
                c+=1
        return c

7.22

在这里插入图片描述
可以使用二分查找,每次都取中间和末尾,如果中间大于末尾,最小的值一定在后半,如果小于,一定在前半

class Solution:
    def minArray(self, numbers: List[int]) -> int:
        low, high = 0, len(numbers) - 1
        while low < high:
            pivot = low + (high - low) // 2
            if numbers[pivot] < numbers[high]:
                high = pivot 
            elif numbers[pivot] > numbers[high]:
                low = pivot + 1
            else:
                high -= 1
        return numbers[low]

7.23

在这里插入图片描述
这题很明显动态规划,从后往前斜着加,每次取最小的加,直到到头

class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        if not grid or not grid[0]:
            return 0        
        rows, columns = len(grid), len(grid[0])
        dp = [[0] * columns for _ in range(rows)]
        dp[0][0] = grid[0][0]
        for i in range(1, rows):
            dp[i][0] = dp[i - 1][0] + grid[i][0]
        for j in range(1, columns):
            dp[0][j] = dp[0][j - 1] + grid[0][j]
        for i in range(1, rows):
            for j in range(1, columns):
                dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]        
        return dp[rows - 1][columns - 1]

在这里插入图片描述
这句你们是聪明人给我整笑了,这题就不放答案了,不知道的人就可能不是这个…聪明的人了啊,可能呢,有一点点小笨,不过应该不是傻子,哈哈哈

在这里插入图片描述

class Solution:
    def sortArrayByParity(self, A):
        a=[]
        b=[]
        for i in A:
            if i %2 ==0:
                a.append(i)
            else:
                b.append(i)
        return a+b

或者以模二的顺序排列

class Solution(object):
    def sortArrayByParity(self, A):
        A.sort(key = lambda x: x % 2)
        return A

或者双指针

class Solution(object):
    def sortArrayByParity(self, A):
        i, j = 0, len(A) - 1
        while i < j:
            if A[i] % 2 > A[j] % 2:
                A[i], A[j] = A[j], A[i]

            if A[i] % 2 == 0: i += 1
            if A[j] % 2 == 1: j -= 1
        return A

7.24-7.25

啥也没干 周一刷刀塔代币,周二更新也刷了一天 哈哈,虽然感觉不对,但是真的挺爽 嘿嘿

7.26

在这里插入图片描述
又是一个聪明人游戏,这种题一般都要写出最佳方案,找规律就变得十分明显了
这个题其实是谁能到2谁就赢,其实可以分析 每个奇数的因数肯定是奇数,偶数的因数是偶数和奇数,所以奇数减奇数一定是偶数,如果N初始是偶数的话,爱丽丝只需要每次都减1把这个数变成奇数,鲍勃一定会输,如果相反的话,爱丽丝一定会输。

在这里插入图片描述

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root or root == p or root == q: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if not left: return right
        if not right: return left
        return root

在这里插入图片描述
认真分析,首先连续的数可以化为方程((y-x+1)(x+y))/2其中x+y小于等于traget,设i=y-x则原式可化为((i+1)(2x+i))/2=t,然后再次对式子进行操作,转化为x=(t-(i(i+1))/2)/(i+1)其中分子分母都要求是整数,所以再次遍历,就很简单得出结果了

7.27

在这里插入图片描述
经测试 发现
Abc s序列
Acbsdc 可行
Abbsdsdsc 可行
Asdccbbbsadsadac 可行
所以 只要在后边的t字符串中可以找到s的一个序列就可以
思路:从头向后找,找到s的第一个字母,然后再在t的后边依次找,知道找完为止
贪心算法:

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        k=0
        if s=="":
            return True
        for i in t:
            if s[k]==i:
                k+=1
                if k==len(s):
                    return True
        return False

其他的题都很简单,就不写了

7.28

在这里插入图片描述
思路,到底返回,每次取最大的,并且加一

class Solution:
    def maxDepth(self, root) -> int:
        if root is None:
            return 0
        else:
           return max(self.maxDepth(root.left),self.maxDepth(root.right))+1

7.29

今天打卡失败,看来我只能和简单题比划比划了
难题我不会,简单题我重拳出击
在这里插入图片描述
这个题没啥可说的,484很简单?

class Solution:
    def printNumbers(self, n: int) -> List[int]:
        a=[]
        for i in range(1,10**n):
            a.append(i)
        return a

看了题解发现这道题其实考查的是大数的保存,一般来说,一个很大的数需要保存为字符串类型,这样进位就很不方便实现,比如999+1,要对字符串操作很多,大数的加法可以这样来弄:比如999+999,就可以先在每一位加和,变成18 18 18,然后再次判断如果大于10,则下一位加1,就变成了1 9 9 8,这就扯远了,现在回归大数保存。

class Solution:
    def printNumbers(self, n: int) -> [int]:
        def dfs(x):
            if x == n:
                s = ''.join(num[self.start:])
                if s != '0': res.append(int(s))
                if n - self.start == self.nine: self.start -= 1
                return
            for i in range(10):
                if i == 9: self.nine += 1
                num[x] = str(i)
                dfs(x + 1)
            self.nine -= 1
        num, res = ['0'] * n, []
        self.nine = 0
        self.start = n - 1
        dfs(0)
        return res

这个算法写的就是递归,从0开始填进去,最后把最前边的0删除,可以debug来看过程

7.30

在这里插入图片描述
打卡题,这道题很巧妙,是用数学的方法解决,哈哈哈 答案就不放了,不懂的问我,嘿嘿
在这里插入图片描述
在这里插入图片描述
这个题也还行,就是拆分和除,注意考虑除数不能为0,

class Solution:
    def selfDividingNumbers(self, left: int, right: int) :
        def find(n):
            s = n
            while (s != 0):
                t = s % 10
                if t==0 or n % t != 0:
                    break
                s = s // 10
                if s == 0:
                    return True
            return False
        a=[]
        for i in range(left, right+1):
            if find(i):
                a.append(i)
        return a

其实题解的find函数代码更简单

def self_dividing(n):
            for d in str(n):
                if d == '0' or n % int(d) > 0:
                    return False
            return True

在这里插入图片描述
统计字符出现次数,发现是子集则可以,加长度

import collections
class Solution:
    def countCharacters(self, words, chars: str) :
        c=collections.Counter(chars)
        a=0
        for i in words:
            w=0
            j=collections.Counter(i)
            for q in j:
                if q in c and j[q] <= c[q]:
                    w+=1
            if w ==len(j):
                a=a+len(i)
        return a

7.31

在这里插入图片描述
这个题也很简单,见代码就能明白

class Solution:
    def findMagicIndex(self, nums: List[int]) -> int:
        if not nums:
            return -1
        n = len(nums)
        i = 0
        while i < n:
            if nums[i] == i:
                return i
            if nums[i] > i: # 此时我们可以排除索引i到nums[i-1]这一整段
                i = nums[i] # 由于数组可以保持平稳,所以nums[i]这一元素不可排除
            else:
                i += 1         
        return -1

7月结尾

不知不觉刷了100道题了,这个月收获满满,感觉自己也就对基础操作更加熟悉了,对于一些简单地题还是总会想一些老办法,没有把学到的方法好好运用。今后要继续加油,哈哈

要在VS Code上力扣(LeetCode)题目,首先需要进行以下几步操作: 1. 安装VS Code插件:在VS Code中搜索并安装LeetCode插件。这个插件可以提供LeetCode题目的在线编写和提交功能,以及自动测试和调试代码的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [【史上最强代码编辑器VS Code】之VS Code力扣(LeetCode)题目](https://blog.csdn.net/weixin_44553006/article/details/105183522)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [在 vscode 上力扣 Leetcode 可以这样来](https://blog.csdn.net/u012190388/article/details/121277555)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [leetcode答案-algo-study:从零开始力扣(LeetCode),JavaScript语言,算法](https://download.csdn.net/download/weixin_38680764/19920528)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Enginer静态力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值