总之就是每天搞一道题。
有时两题,有时零题。挑一些自我反省来写。
我常常因为自己太菜而一道题写半天.jpg
4.2
题目链接
2. 两数相加
很喜欢的解法(哑结点)
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
dummyHead = ListNode(0)
curr, carry = dummyHead, 0
while l1 or l2:
sum = 0
if l1:
sum += l1.val
l1 = l1.next
if l2:
sum += l2.val
l2 = l2.next
sum += carry
carry = sum // 10
curr.next = ListNode(sum % 10)
curr = curr.next
if carry > 0:
curr.next = ListNode(1)
return dummyHead.next;
自己第一反应的解法
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
b=ListNode(0)
c=b
while l1 or l2:
c.val+=sum(list(map(lambda x:0 if x==None else x.val,[l1,l2])))
l1,l2=list(map(lambda x:x if x==None else x.next,[l1,l2]))
c.next=[ListNode(c.val//10),None][c.val<10 and l1==l2==None]
c.val%=10
c=c.next
return b
感想
- 发现自己的解法太过耗脑就不要一直莽,想想别的出路。就好像这道题,做一个哑节点会让逻辑和操作都清晰很多。
-
lambda
真的很酷炫,也很容易沉迷……但别瞎用。这东西对于提升运行效率并无助益,平时跑起来感觉不到什么,但其实会很慢。慢到想死。 - 多看数据结构,多看数据结构,多看数据结构。
4.3
题目链接
8. 字符串转换整数 (atoi)
很喜欢的解法(正则)
class Solution:
def myAtoi(self, str: str) -> int:
return max(min(int(*re.findall('^[\+\-]?\d+', str.lstrip())), 2**31 - 1), -2**31)
自己第一反应的解法
class Solution:
def myAtoi(self, str: str) -> int:
r=''
l='0123456789'
for i in str:
if (i==' ' and r!='') or (i not in l+'-+ '):
break
if (i in '-+') and (len(r)!=0 or ('-' in r or '+' in r)):
if len(r)==1:return 0
else:break
if i in l+'-+':
r+=i
if r in '-+':
return 0
return max(min(int(r),2**31-1),-2**31)
感想
- 正则可以省很多事,能上正则的时候就尽量上正则。
- 不要觉得一个技巧很酷就一直用,要多多依赖自带函数。就好像在这道题里
str.lstrip()
比遍历判断空格更好用,min(r,2**31-1)
比[r,2**31-1][r>=2**31-1]
更清晰。虽然用列表来写判断很有趣,但不要遇事不决上列表(……)本道理同样适用于lambda x
。 - 造测试样例很重要(……)尽可能把所有可能的情况都包含上比较好,想的不够多又相信自己的脑子就会出很多WA和RE(。
4.17
隔很多天又去A了一下每日一题(……)
要么太难要么太简单,今天正好难度适中动了动脑子(……)
题目链接
35.跳跃游戏
很喜欢的解法
class Solution:
def canJump(self, nums) :
max_i = 0 #初始化当前能到达最远的位置
for i, jump in enumerate(nums): #i为当前位置,jump是当前位置的跳数
if max_i>=i and i+jump>max_i: #如果当前位置能到达,并且当前位置+跳数>最远位置
max_i = i+jump #更新最远能到达位置
return max_i>=i
自己第一反应的解法
class Solution:
def canJump(self, nums: List[int]) -> bool:
maxc=nums[0]
for i in range(len(nums)):
if i>maxc:break
if (i+nums[i])>=(len(nums)-1):return True
maxc=max(maxc,nums[i]+i)
return False
感想
-
enumerate()
是不是比range(len())
要快啊!好不安,总之度了一下,大家说尽量用enumerate
取代掉range
,那就这样叭( - 想了想其实初始值直接
maxc=0
就好了(……)不过这样子赋值也没什么影响,大概。
4.18
题目链接
11. 盛最多水的容器
自己暴力开冲超时以后的修改解法(双指针)
class Solution:
def maxArea(self, height: List[int]) -> int:
maxm=0
i,j=0,len(height)-1
while i!=j:
maxm=max(maxm,(j-i)*min(height[i],height[j]))
if height[i]
感想
- 关于解法的说明:尝试过程中矩形底边必定不断变小,而矩形的高又是
min(height[i],height[j])
,所以只能通过改变min值来增加矩形面积,亦即改变这两个值中的较小值……也就是较矮一端的柱子。
视频:【双指针法】的合理性证明 - LeetCode - 后来去看了看题解,在判断那里可以再加一下。如果下一个位置比现在的位置数值还要小,可以直接跳过,免得重复计算面积(……)天才吗(
4.19
题目链接
5388. 重新格式化字符串
很喜欢的解法
class Solution:
def reformat(self, s: str) -> str:
a=re.findall(r'\d',s)
b=re.findall(r'[a-z]',s)
if abs(len(a)-len(b))>1:
return ''
a,b=sorted([a,b],key=len)
return ''.join(map(''.join,itertools.zip_longest(b,a,fillvalue='')))
自己第一反应的解法
class Solution:
def reformat(self, s: str) -> str:
a=re.findall('[a-z]',s)
b=re.findall('[0-9]',s)
if not (-1<=(len(a)-len(b))<=1):
return ""
if len(b)>len(a):a,b=b,a
c=''.join([y for x in zip(a,b) for y in x])
if len(a)!=len(b):c+=a[-1]
return c
感想
- 是我读的函数太少,是我太菜,我根本没掌握
map
的正确用法,下次一定写abs
和sorted
,dbq,dbq
4.20
题目链接
219. 存在重复元素 II
很喜欢的解法(HashSet)
class Solution:
def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
tmp=set()
for i in range(len(nums)):
if nums[i] in tmp:return True
tmp.add(nums[i])
if len(tmp)>k:
tmp.remove(nums[i-k])
return False
自己安定超时的第一反应
class Solution:
def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
for i in range(len(nums)):
for j in range(len(nums)):
if j!=i and nums[j]==nums[i] and -k<=(i-j)<=k:
return True
return False
感想
- 暴力不可取,停止套娃。
- 能搞懂各种数据结构的应用场合是快乐做题的关键一步(?)
题目链接
200. 岛屿数量
看了题解的解法(BFS)
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
def bfs(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
bfs(grid,i,j-1)
bfs(grid,i,j+1)
bfs(grid,i-1,j)
bfs(grid,i+1,j)
count=0
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j]=='1':
bfs(grid,i,j)
count+=1
return count
感想
- 知道“有这么个东西”和“能把概念和实际情景联系上”,差别真的很大(……)我的数据结构学得仿佛白瞎(。总之还是要多练,任重道远(
4.21
题目链接
1248. 统计「优美子数组」
看了题解的解法
class Solution:
def numberOfSubarrays(self, nums: List[int], k: int) -> int:
tmp=[i for i in range(len(nums)) if nums[i]%2!=0]
tmp=[-1]+tmp+[len(nums)]
count=0
for i in range(1,len(tmp)-k):
count+=(tmp[i]-tmp[i-1])*(tmp[i+k]-tmp[i+k-1])
return count
感想
- 把列表边界处理下会快乐很多,就像之前那道题的哑结点。我怎么不长教训的(笑)
4.22
题目链接
199. 二叉树的右视图
很喜欢的解法(BFS)
class Solution:
def rightSideView(self, root: TreeNode) -> List[int]:
if not root:
return []
ans, nodes = [], [root]
while nodes:
ans.append(nodes[-1].val)
nodes = [n for node in nodes for n in [node.left, node.right] if n]
return ans
自己第一反应的解法
# 很憨憨的层序遍历
class Solution:
def rightSideView(self, root: TreeNode) -> List[int]:
if root==None:
return []
tmp,level=[root],[]
result=[]
nowlist=1
nextlist=0
while tmp:
node=tmp.pop(0)
nowlist-=1
if node.left!=None:
tmp+=[node.left]
nextlist+=1
if node.right!=None:
tmp+=[node.right]
nextlist+=1
level.append(node.val)
if nowlist==0:
nowlist,nextlist=nextlist,0
result.append(level[-1])
level=[]
return list(filter(None,result))
感想
- 我会个p的bfs和dfs……dbq我又在拿python写c,我是憨憨,笨蛋是我我是笨蛋。
- 既然有语法糖就去吃(。
4.25
题目链接
46. 全排列
很喜欢的解法
itertools — Functions creating iterators for efficient looping
def permute(self, nums: List[int]) -> List[List[int]]:
return list(itertools.permutations(nums))
参考了题解的解法
#回溯算法
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
result=[]
def backtracks(nums,tmp):
if not nums:
result.append(tmp)
return
for i in range(len(nums)):
backtracks(nums[:i]+nums[i+1:],tmp+[nums[i]])
backtracks(nums,[])
return result
感想
- 值得参考的题解:
从全排列问题开始理解「回溯」算法(深度优先遍历 + 状态重置 + 剪枝) - 多读文档多动脑,停止暴力(。
题目链接
6. Z 字形变换
很喜欢的解法
class Solution:
def convert(self, s: str, numRows: int) -> str:
tmp=['']*len(s)
start,flag=0,-1
for i in s:
tmp[start]+=i
if start==0 or start==numRows-1:flag=-flag
start+=flag
return ''.join(tmp)
自己第一反应的解法
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows==1:
return s
s=list(s)
jump=numRows*2-2
tmp=s[::jump]
for i in range(1,numRows-1):
tmp+=[k for j in zip_longest(s[i::jump],s[jump-i::jump],fillvalue='') for k in j]
tmp+=s[numRows-1::jump]
return ''.join(tmp)
感想
- 多动脑*2。天才 改索引方向 天才
4.28
题目链接
面试题56 - I. 数组中数字出现的次数
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
很喜欢的解法(位运算)
class Solution:
def singleNumbers(self, nums: List[int]) -> List[int]:
tmp=reduce(lambda x,y:x^y,nums)
lowbit=tmp&(-tmp)
result=[0,0]
for i in range(len(nums)):
if nums[i]&lowbit==0:
result[0]^=nums[i]
else:
result[1]^=nums[i]
return result
解法说明
- 参考题解:位运算三步走 详细解释lowbit原理
- 因为“其他数字都出现了两次”,而相同值的异或结果为0,所以对所有数做异或的结果必定是“只出现一次的某两个数异或的结果”,暂称a和b。
之后只要找到a和b(任意一个)不同的一位(即上一步的异或结果为1),就可以通过这一位把a和b区分开。为方便起见,解法里通过lowbit=tmp&-tmp
获取最低位的1
,并把这一位外的数都置0,作为掩码(mask)。
这样一来,和mask
做与运算的结果必定是0
(在那一位上为0)或mask
(在那一位上为1)。最后只需要挨个遍历,通过与运算区分开a和b以后再做异或就ok,出现两次的会自己消掉,所以剩下的就是所求的a和b。
4.29
题目链接
1095. 山脉数组中查找目标值
很喜欢的解法(二分查找)
def binary_search(mountain, target, l, r, key=lambda x: x):
target = key(target)
while l <= r:
mid = (l + r) // 2
cur = key(mountain.get(mid))
if cur == target:
return mid
elif cur < target:
l = mid + 1
else:
r = mid - 1
return -1
class Solution:
def findInMountainArray(self, target: int, mountain_arr: 'MountainArray') -> int:
l, r = 0, mountain_arr.length() - 1
while l < r:
mid = (l + r) // 2
if mountain_arr.get(mid) < mountain_arr.get(mid + 1):
l = mid + 1
else:
r = mid
peak = l
index = binary_search(mountain_arr, target, 0, peak)
if index != -1:
return index
index = binary_search(mountain_arr, target, peak + 1, mountain_arr.length() - 1, lambda x: -x)
return index
感想
- 参考题解:山脉数组中查找目标值
- 这一手lambda用得好强,二分的泛用性也好强,学习了(。
4.30
题目链接
202. 快乐数
很喜欢的解法(快慢指针)
参考英文网站热评第一。这题可以用快慢指针的思想去做,有点类似于检测是否为环形链表那道题。
如果给定的数字最后会一直循环重复,那么快的指针(值)一定会追上慢的指针(值),也就是两者一定会相等。如果没有循环重复,那么最后快慢指针也会相等,且都等于1。
class Solution:
def isHappy(self, n: int) -> bool:
def calc(i):
return sum(map(lambda x:int(x)**2,list(str(i))))
slow=calc(n)
fast=calc(calc(n))
while slow!=fast and fast!=1:
slow=calc(slow)
fast=calc(calc(fast))
return fast==1
看了题解的解法(HashSet)
我们使用 HashSet 而不是向量、列表或数组的原因是因为我们反复检查其中是否存在某数字。检查数字是否在哈希集中需要O(1)的时间,而对于其他数据结构,则需要O(n)的时间。选择正确的数据结构是解决这些问题的关键部分。
class Solution:
def isHappy(self, n: int) -> bool:
def calc(i):
return sum(map(lambda x:int(x)**2,list(str(n))))
tmp=set()
while True:
n=calc(n)
print(n)
if n==1:
return True
if n not in tmp:
tmp.add(n)
else:
return False
感想
- 摘录评论如下:
此题不建议用集合记录每次的计算结果来判断是否进入循环,因为这个集合可能大到无法存储;另外,也不建议使用递归,同理,如果递归层次较深,会直接导致调用栈崩溃。不要因为这个题目给出的整数是int型而投机取巧。 - 菜 我 菜。这道题最大也就243所以不会崩,但是其他场景下就不好说了,所以还是用快慢指针好一点。