七月力扣
前言
头一次刷力扣,卑微的很,觉得自己会的太少了,不过要一直刷下去,提高自我代码水平,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道题了,这个月收获满满,感觉自己也就对基础操作更加熟悉了,对于一些简单地题还是总会想一些老办法,没有把学到的方法好好运用。今后要继续加油,哈哈