Leetcode-review
题单为leetcode经典150
数组 / 字符串
68. 文本左右对齐
68. 文本左右对齐 - 力扣(LeetCode)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33class Solution:
def fullJustify(self, words: List[str], maxWidth: int) -> List[str]:
ans=[]
word_list=[]
cur_len = 0
for word in words:
if cur_len + len(word) + len(word_list) > maxWidth:
spaces = maxWidth - cur_len
gaps = len(word_list) - 1
if gaps == 0:
line = word_list[0] + ' ' * spaces
else:
average,left = divmod(spaces,gaps)
line = ''
for i in range(gaps):
line += word_list[i]
line += average* ' '
if i<left:
line +=' '
line += word_list[-1]
ans.append(line)
#initialize
word_list = []
cur_len = 0
word_list.append(word)
cur_len += len(word)
#最后一行
line = ' '.join(word_list)
spaces = maxWidth - len(line)
line += ' ' * spaces
ans.append(line)
return ans
一开始先写了处理溢出的情况,不过中间还需要先存储那些可以的word再合成一个line
思路就是一行一行读一行一行造,里面有一个问题是在这里1
2
3
4
5for i in range(gaps):
line += word_list[i]
line += average* ' '
if i<left:
line +=' '
这个i<left我想了很久
left是余数,i是中间有的空格-1,这边要的效果是把余数在前面的空里面填满
那假设有3个要填的,有四个空格,要填的i是0,1,2,此时left=3
5个要填的,有七个空格,要填的i是0,1,2,3,4,此时left=5
那很明显可以用i<left来限制
然后就造吧
392. 判断子序列
1 | class Solution: |
迭代器神力,同时判断是不是在里面
同时使用all( )
1312. 让字符串成为回文串的最少插入次数
1 | class Solution: |
套用了另外一道简单题目lcs,第一次涉及dp
如何求lcs
1 | def longestCommonSubsequence(text1: str, text2: str) -> int: |
m = "abccdca"
n = "adcaab" lcs = “aca”`
情况一:两个字符串的最后一个字符相等
如果 text1[i-1] == text2[j-1]
- 这意味着我们找到了一个公共字符!这个字符必然可以作为它们公共子序列的一部分。
- 那么,text1 的前 i 个字符和 text2 的前 j 个字符的LCS,就等于它们各自去掉最后一个字符后的LCS长度再加1。
- 用 dp 数组来表示就是:
dp[i][j] = dp[i-1][j-1] + 1
举例: 求 “abc” 和 “adc” 的LCS。因为最后一个字符 ‘c’ 相等,问题就转化为求 “ab” 和 “ad” 的LCS长度,然后加1。
情况二:两个字符串的最后一个字符不相等
如果 text1[i-1] != text2[j-1]
- 这意味着这两个不同的字符不可能同时出现在LCS的末尾。
- 那么,我们就要在两种可能性中取一个较大值:
- text1 的前 i 个字符和 text2 的前 j-1 个字符的LCS长度(相当于把 text2 的最后一个字符扔掉,看看结果)。这个值就是
dp[i][j-1]。 - text1 的前 i-1 个字符和 text2 的前 j 个字符的LCS长度(相当于把 text1 的最后一个字符扔掉,看看结果)。这个值就是
dp[i-1][j]。
- text1 的前 i 个字符和 text2 的前 j-1 个字符的LCS长度(相当于把 text2 的最后一个字符扔掉,看看结果)。这个值就是
- 我们取这两者中的最大值,因为它代表了更长的公共子序列。
- 用 dp 数组来表示就是:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
这题看题解去吧,俺也懵懵的
3354. 使数组元素等于零 - 力扣(LeetCode)
1 | class Solution: |
思路非常简单,发现题目根本没必要使用动态的想法
双指针
167. 两数之和 II - 输入有序数组 - 力扣(LeetCode)
1 | class Solution: |
这是第一次ac代码,执行用时分布 5813ms 击败5.20%
没错高贵的O(n^2),那可得好好优化一下了
在这应该使用双指针来快速查找,避免多次遍历1
2
3
4
5
6
7
8
9
10
11
12class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
left=0
right=len(numbers)-1
while left < right:
cur_sum=numbers[left]+numbers[right]
if cur_sum==target:
return [left+1,right+1]
elif cur_sum < target:
left+=1
else:
right-=1
该方法时间复杂度为O(n) 执行用时分布 4ms 击败50.86%
奇怪,为什么一样的算法他们能跑到0ms的,是我没充钱吗?
11. 盛最多水的容器 - 力扣(LeetCode)
贪心双指针一遍过1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Solution:
def maxArea(self, height: List[int]) -> int:
max_volume=0
left=0
right=len(height)-1
while(left!=right):
x=right-left
y=min(height[left],height[right])
v=x*y
if v>max_volume:
max_volume=v
if height[left]>=height[right]:
right-=1
else:
left+=1
return max_volume
15. 三数之和 - 力扣(LeetCode)
这是第一版🔟山,最后tle了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if len(nums) < 3:
return []
sorted_nums = sorted(nums)
count = 0
for num in sorted_nums:
if num < 0:
count += 1
nums1 = sorted_nums[:count]
nums2 = sorted_nums[count:]
if not nums1 or not nums2:
if sorted_nums.count(0) >= 3:
return [[0, 0, 0]]
return []
ans = []
seen = []
for i in nums1:
for j in nums2:
k = -i - j
if k not in sorted_nums:
continue
if [i, j, k].count(i) > sorted_nums.count(i):
continue
if [i, j, k].count(j) > sorted_nums.count(j):
continue
if [i, j, k].count(k) > sorted_nums.count(k):
continue
triple = sorted([i, j, k])
if triple not in seen:
seen.append(triple)
ans.append(triple)
if sorted_nums.count(0) >= 3:
ans.append([0, 0, 0])
return ans
这是高贵的O(n^3)算法,暴力的后果还是被卡了啊,同时内存也多到爆炸,于是想到三指针1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
ans=[]
nums.sort()
n=len(nums)
for i in range(n-2):
if i > 0 and nums[i]==nums[i-1]:
continue
left=i+1
right=n-1
while left < right:
s = nums[i]+nums[left]+nums[right]
if s==0:
ans.append([nums[i],nums[left],nums[right]])
while left < right and nums[left]==nums[left+1]:
left+=1
while left < right and nums[right]==nums[right-1]:
right-=1
left+=1
right-=1
elif s<0:
left+=1
else:
right-=1
return ans
滑动窗口
209. 长度最小的子数组 - 力扣(LeetCode)
滑动窗口初尝试,好像不难,和双指针很像了1
2
3
4
5
6
7
8
9
10
11
12
13
14class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
n=len(nums)
ans=n+1
s=0
left=0
for right,num in enumerate(nums):
s+=num
while s>=target:
ans=min(ans,right-left+1)
s-=nums[left]
left+=1
if ans==n+1:return 0
return ans
3. 无重复字符的最长子串 - 力扣(LeetCode)
滑动窗口+hashmap1
2
3
4
5
6
7
8
9
10
11
12
13
14class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
start = 0
seen = set()
maxlen = 0
for end in range(len(s)):
while s[end] in seen:
seen.remove(s[start])
start += 1
seen.add(s[end])
maxlen = max(maxlen, end - start + 1)
return maxlen
和LCR 167. 招式拆解 I - 力扣(LeetCode)是一样的,因此一起过了
30. 串联所有单词的子串 - 力扣(LeetCode)
一开始思路不好,神秘的O(n!xn)算法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Solution:
def generate_permutations(self,words:str) -> List[str]:
if len(words) == 1:
return words
res = []
for i in range(len(words)):
first = words[i]
rest = words[:i] + words[i+1:]
for sub in self.generate_permutations(rest):
res.append(first + sub)
return res
def findSubstring(self, s: str, words: List[str]) -> List[int]:
sub_words = set(self.generate_permutations(words))
l=len(s)
window = len(words[0]) * len(words)
if window > len(s): return []
ans=[]
for start in range(0, len(s) - window + 1):
if s[start : start + window] in sub_words:
ans.append(start)
return ans
124 / 182 个通过的测试用例
提交于 2025.10.30 21:35
s ="fffffffffffffffffffffffffffffffff"
words =["a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"]
很明显会因为生成子串的过程太长导致tle
所以换一个思路,把words用键值对一一对应1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:
return []
word_len = len(words[0])
total_len = word_len * len(words)
word_map = {w: words.count(w) for w in words}
ans = []
n = len(s)
for i in range(0, n - total_len + 1):
seen = {}
j = 0
while j < len(words):
word_start = i + j * word_len
word_end = word_start + word_len
word = s[word_start:word_end]
if word not in word_map:
break
seen[word] = seen.get(word, 0) + 1
if seen[word] > word_map[word]:
break
j += 1
if j == len(words):
ans.append(i)
return ans
30. 串联所有单词的子串 - 力扣(LeetCode)
这测试点诗人啊
181 / 182 个通过的测试用例
ac代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:
return []
word_len = len(words[0])
total_len = word_len * len(words)
word_map = {w: words.count(w) for w in words}
ans = []
n = len(s)
for offset in range(word_len):
left = offset
seen = {}
count = 0
for right in range(offset, n - word_len + 1, word_len):
word = s[right:right+word_len]
if word in word_map:
seen[word] = seen.get(word, 0) + 1
count += 1
while seen[word] > word_map[word]:
left_word = s[left:left+word_len]
seen[left_word] -= 1
left += word_len
count -= 1
if count == len(words):
ans.append(left)
else:
seen.clear()
count = 0
left = right + word_len
return ans
最快的方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
n = len(s)
step = len(words[0])
total_len = step * len(words)
res = []
for i in range(step):
map_word_cnt = {}
for word in words:
if word not in map_word_cnt:
map_word_cnt[word] = 1
else:
map_word_cnt[word] += 1
match_cnt = len(words)
right = i
while right + step - 1 < n:
left = right + step - total_len
left_out = left - step
if left_out >= 0:
w_left_out = s[left_out:left_out+step]
if w_left_out in map_word_cnt:
map_word_cnt[w_left_out] += 1
if map_word_cnt[w_left_out] > 0:
match_cnt += 1
w_right = s[right:right+step]
if w_right in map_word_cnt:
map_word_cnt[w_right] -= 1
if map_word_cnt[w_right] >= 0:
match_cnt -= 1
if left >= 0 and match_cnt == 0:
res.append(left)
right += step
return res
76. 最小覆盖子串 - 力扣(LeetCode)
滑动窗口 哈希表
如果min_len=len(s)会导致维护出错
使用哈希表还是不熟练
.items()忘了
还有一个注意点是切片,切片左闭右开 [left , right + 1) 老记错
right-left+1 是长度
滑动窗口从窗口为零开始,向右扩张,同时维护window_counts ,一边向右添加一边保持左边没有额外的字符
就这样遍历,复杂度O(N) 600ms 击败44.06%1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42class Solution:
def count(self, t: str) -> dict:
t_count = {}
for char in t:
if char in t_count:
t_count[char] += 1
else:
t_count[char] = 1
return t_count
def check(self, current_dict: dict, t_count: dict) -> bool:
for char, required_count in t_count.items():
if current_dict.get(char, 0) < required_count:
return False
return True
def minWindow(self, s: str, t: str) -> str:
t_count = self.count(t)
window_counts = {}
left = 0
min_len = len(s) + 1
result = ""
for right in range(len(s)):
char_in = s[right]
window_counts[char_in] = window_counts.get(char_in, 0) + 1
while self.check(window_counts, t_count):
current_len = right - left + 1
if current_len < min_len:
min_len = current_len
result = s[left : right + 1]
char_out = s[left]
window_counts[char_out] -= 1
if window_counts[char_out] == 0:
del window_counts[char_out]
left += 1
return result
这次想法里面的问题所在是这块1
2
3
4
5def check(self, current_dict: dict, t_count: dict) -> bool:
for char, required_count in t_count.items():
if current_dict.get(char, 0) < required_count:
return False
return True
这个循环的次数取决于 t 中有多少个 不重复 的字符
但是如果字典有27个字符的时候就很明显不合理了
而且check了非常非常多次,也浪费了很多的时间
看看这个代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Solution:
def minWindow(self, s: str, t: str) -> str:
need=defaultdict(int)
for c in t:
need[c]+=1
needCnt=len(t)
i=0
res=(0,float('inf'))
for j,c in enumerate(s):
if need[c]>0:
needCnt-=1
need[c]-=1
if needCnt==0:
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
needCnt+=1
i+=1
return ''if res[1]>len(s) else s[res[0]:res[1]+1]
在这边发现了defaultdict,这才知道leetcode已经import过了collections
那这块内容需要好好学一学,写到Python-algorithm去
矩阵
36. 有效的数独 - 力扣(LeetCode)
这题只要判断是否合理,不需要解出来数独其实还好
一次遍历就行了
用set来创建集合1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
rows = [set() for _ in range(9)]
cols = [set() for _ in range(9)]
boxes = [set() for _ in range(9)]
for i in range(0,9):
for j in range(0,9):
num = board[i][j]
if num == '.':
continue
if num in rows[i]:
return False
rows[i].add(num)
if num in cols[j]:
return False
cols[j].add(num)
box_index = (i // 3) * 3 + (j // 3)
if num in boxes[box_index]:
return False
boxes[box_index].add(num)
return True
54. 螺旋矩阵 - 力扣(LeetCode)
我的思路是用上下左右限制,有点复杂,维护起来也好累。。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix or not matrix[0]:
return []
ans = []
num_rows = len(matrix)
num_cols = len(matrix[0])
left, right = 0, num_cols - 1
top, bottom = 0, num_rows - 1
while left <= right and top <= bottom:
for col in range(left, right + 1):
ans.append(matrix[top][col])
top += 1
for row in range(top, bottom + 1):
ans.append(matrix[row][right])
right -= 1
if not (left <= right and top <= bottom):
break
for col in range(right, left - 1, -1):
ans.append(matrix[bottom][col])
bottom -= 1
for row in range(bottom, top - 1, -1):
ans.append(matrix[row][left])
left += 1
return ans
但看官方第二种解法是这样的
对于每层,从左上方开始以顺时针的顺序遍历所有元素。假设当前层的左上角位于 (top,left),右下角位于 (bottom,right),按照如下顺序遍历当前层的元素。
从左到右遍历上侧元素,依次为 (top,left) 到 (top,right)。
从上到下遍历右侧元素,依次为 (top+1,right) 到 (bottom,right)。
如果 left<right 且 top<bottom,则从右到左遍历下侧元素,依次为 (bottom,right−1) 到 (bottom,left+1),以及从下到上遍历左侧元素,依次为 (bottom,left) 到 (top+1,left)。
遍历完当前层的元素之后,将 left 和 top 分别增加 1,将 right 和 bottom 分别减少 1,进入下一层继续遍历,直到遍历完所有元素为止。
48. 旋转图像 - 力扣(LeetCode)
想法很多都不好实现。
没写,但是看到了一个逆天答案1
2
3
4class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
matrix[:] = list(map(list, zip(*matrix)))
for row in matrix: row.reverse()
73. 矩阵置零 - 力扣(LeetCode)
思路非常简单,找到0的位置,然后直接改,这题目凭什么是中档题?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
rows = len(matrix)
cols = len(matrix[0])
temp = []
for i in range(rows):
for j in range(cols):
if matrix[i][j] == 0:
temp.append([i, j])
for r, c in temp:
for j in range(cols):
matrix[r][j] = 0
for i in range(rows):
matrix[i][c] = 0
但由于使用了temp存储,导致空间复杂度为O(M+N)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
m, n = len(matrix), len(matrix[0])
flag_col0 = False
for i in range(m):
if matrix[i][0] == 0:
flag_col0 = True
for j in range(1, n):
if matrix[i][j] == 0:
matrix[i][0] = matrix[0][j] = 0
for i in range(m - 1, -1, -1):
for j in range(1, n):
if matrix[i][0] == 0 or matrix[0][j] == 0:
matrix[i][j] = 0
if flag_col0:
matrix[i][0] = 0
这个是leetcode官方给出的空间复杂度为O(1)的算法
如果 matrix[i][j]是 0,它就把这个信息记录在 matrix[i][0]和 matrix[0][j] 上,将它们也设置为 0。
这样一来,matrix[i][0]就成了第 i 行是否需要置零的标记位。
同理,matrix[0][j]就成了第 j 列是否需要置零的标记位。
很巧妙的直接使用原矩阵在不破坏的情况下标记并且置零,不额外开空间
289. 生命游戏 - 力扣(LeetCode)
充满小巧思了
第一个小巧思是使用元组来传neighbor,然后python会自动解包,写起来好看
第二个小巧思是要判断当前状态:状态 1 和 2 都代表“原来是活的”,状态 0 和 3 都代表“原来是死的”
这样区分避免出现在修改过程中误判的情况,最后第二次遍历把状态再修正
和官方第二个题解差不多,但是官方只用到21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class Solution:
def gameOfLife(self, board: List[List[int]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
m, n = len(board), len(board[0])
neighbors = [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (1, -1), (-1, 1), (-1, -1)]
for i in range(m):
for j in range(n):
alive = 0
for dx, dy in neighbors:
r, c = i + dx, j + dy
if 0 <= r < m and 0 <= c < n:
if board[r][c] == 1 or board[r][c] == 2:
alive += 1
if board[i][j] == 1 and (alive < 2 or alive > 3):
board[i][j] = 2
elif board[i][j] == 0 and alive == 3:
board[i][j] = 3
for i in range(m):
for j in range(n):
if board[i][j] == 2:
board[i][j] = 0
elif board[i][j] == 3:
board[i][j] = 1
哈希表
383. 赎金信 - 力扣(LeetCode)
用Counter自动计数
然后直接在本体上操作就行1
2
3
4
5
6
7
8
9
10
11
12
13class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
if len(ransomNote) > len(magazine):
return False
magazine_counts = Counter(magazine)
for char in ransomNote:
if magazine_counts[char] > 0:
magazine_counts[char] -= 1
else:
return False
return True
这样写也可以1
2
3
4
5
6
7
8
9
10
11
12class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
if len(ransomNote)>len(magazine) or len(set(magazine)) < len(set(ransomNote)):
return False
count = {}
for i in magazine:
count[i] = count.get(i,0)+1
for i in ransomNote:
if count.get(i, 0) == 0:
return False
count[i] -= 1
return True
205. 同构字符串 - 力扣(LeetCode)
1 | class Solution: |
但是zip()用法太别致了吧1
2
3class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
return len(set(s)) == len(set(t)) == len(set(zip(s, t)))
被吓哭了
也许应该复习一下set()和zip()
set(t)创建的是t中所有不重复字符的集合
zip(s, t) 会生成一系列的配对元组(tuple):
(‘p’, ‘t’), (‘a’, ‘i’), (‘p’, ‘t’), (‘e’, ‘l’), (‘r’, ‘e’)
这些配对就代表了 s 到 t 的映射关系。例如,第一个 ‘p’ 映射到 ‘t’,‘a’ 映射到 ‘i’,第二个 ‘p’ 也映射到 ‘t’。
因此映射和set()长度相同的情况下就可以得出结论
290. 单词规律 - 力扣(LeetCode)
和上一题同一个道理1
2
3
4
5class Solution:
def wordPattern(self, pattern: str, s: str) -> bool:
s2list=s.split(" ")
p2list=list(pattern)
return len(s2list)==len(p2list) and len(set(zip(s2list,p2list)))==len(set(s2list))==len(set(p2list))
思路:s2list和p2list
首先两个长度要相等
然后要保证set出来的长度也相同
eg:
“abba”“wow mom mom wow”
set(list(“abba”))={“a”,“b”}
set(list(“wow mom mom wow”.split(" ")))={“wow”,“mom”}
set(zip(s2list,p2list))={(“a”,“wow”),(“b”,“mom”)}
242. 有效的字母异位词 - 力扣(LeetCode)
1 | class Solution: |
其实"str"可以直接到Counter里面去使用
直接return Counter(t)==Counter(s)就结束了
或者也可以使用sorted(),return sorted(t)==sorted(s)
49. 字母异位词分组 - 力扣(LeetCode)
1 | class Solution: |
复杂度是O(Nklogk),有点高了1
2
3
4
5
6
7
8
9
10
11
12
13class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
if len(strs) == 1:
return [strs]
ans= {}
for ss in strs:
s = str(sorted(ss))
if s not in ans :
ans[s] = [ss]
else :
ans[s].append(ss)
return list(ans.values())
这样一个道理1
2
3
4
5
6
7
8
9class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
mp = collections.defaultdict(list)
for str in strs:
key=''.join(sorted(str))
mp[key].append(str)
return list(mp.values())
1. 两数之和 - 力扣(LeetCode)
哈希1
2
3
4
5
6
7
8
9class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
num_map = {}
for i, num in enumerate(nums):
complement = target - num
if complement in num_map:
return [num_map[complement], i]
num_map[num] = i
return []Q
202. 快乐数 - 力扣(LeetCode)
第一版
371 / 420 个通过的测试用例1
2
3
4
5
6
7
8
9
10
11
12
13class Solution:
def isHappy(self, n: int) -> bool:
while n/10>1:
if n==1:
return True
sum=0
n2str=str(n)
for num in n2str:
sum+=(int(num))**2
n=sum
if n==1:
return True
return False
while那边的判断条件写错了,看到测试用例7,在一开始就返回false了,但是迭代到最后是可以到1的
第二版1
2
3
4
5
6
7
8
9
10
11
12
13
14class Solution:
def isHappy(self, n: int) -> bool:
seen = set()
while n != 1 and n not in seen:
seen.add(n)
total_sum = 0
n2str = str(n)
for digit in n2str:
total_sum += int(digit) ** 2
n = total_sum
return n==1
时间复杂度O(Log(N)),7ms在这个题目里算最慢的一档了1
2
3
4
5
6
7
8
9
10
11
12class Solution:
def isHappy(self, n: int) -> bool:
result = []
while n not in result:
result.append(n)
s = 0
for i in str(n):
s+=int(i)**2
if s == 1:
return True
n = s
return False
4ms
稍微快了一些
219. 存在重复元素 II - 力扣(LeetCode)
思路:用map存,时间复杂度O(N),1
2
3
4
5
6
7
8class Solution:
def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
num_map={}
for i, num in enumerate(nums):
if num in num_map and i - num_map[num] <= k:
return True
num_map[num] = i
return False
官方第二个滑动窗口
思路是维护k+1大小的set1
2
3
4
5
6
7
8
9
10
11class Solution:
def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
s = set()
for i, num in enumerate(nums):
if i > k:
s.remove(nums[i - k - 1])
if num in s:
return True
s.add(num)
return False
128. 最长连续序列 - 力扣(LeetCode)
思路:去重,然后迭代,哈希表1
2
3
4
5
6
7
8
9
10
11
12
13class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
num_set = set(nums)
longest_streak = 0
for num in num_set:
if num - 1 not in num_set:
current_num = num
current_streak = 1
while current_num + 1 in num_set:
current_num += 1
current_streak += 1
longest_streak = max(longest_streak, current_streak)
return longest_streak
数学
9. 回文数 - 力扣(LeetCode)
1 | func isPalindrome(x int) bool { |
66. 加一 - 力扣(LeetCode)
1 | func plusOne(digits []int) []int { |
172. 阶乘后的零 - 力扣(LeetCode)
1 | func trailingZeroes(n int) int { |
69. x 的平方根 - 力扣(LeetCode)
二分查找1
2
3
4
5
6
7
8
9
10
11
12
13
14func mySqrt(x int) int {
l, r := 0, x
ans := -1
for l <= r {
mid := l + (r - l) / 2
if mid * mid <= x {
ans = mid
l = mid + 1
} else {
r = mid -1
}
}
return ans
}
50. Pow(x, n) - 力扣(LeetCode)
初版 TLE 代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15func myPow(x float64, n int) float64 {
var ans float64 = x
if n > 0 {
for i := n - 1; i > 0; i-- {
ans *= x
}
} else if n < 0 {
for i := n; i <= 0; i++ {
ans /= x
}
} else {
ans = 1.0
}
return ans
}
AC
快速幂+递归1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17func myPow(x float64, n int) float64 {
if n >= 0 {
return quickMul(x,n)
}
return 1.0 / quickMul(x, -n)
}
func quickMul(x float64, n int) float64 {
if n == 0 {
return 1
}
y := quickMul(x, n /2)
if n % 2 == 0 {
return y * y
}
return y * y * x
}
149. 直线上最多的点数 - 力扣(LeetCode)
真恶心,谁面试出这题目我就敢光速跑路1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17func maxPoints(points [][]int) (ans int) {
for i, p := range points {
x, y := p[0], p[1]
cnt := map[float64]int{}
for _, q := range points[i+1:] {
dx, dy := q[0]-x, q[1]-y
k := math.MaxFloat64
if dx != 0 {
k = float64(dy) / float64(dx)
}
cnt[k]++
ans = max(ans, cnt[k])
}
}
return ans + 1
}
区间
56. 合并区间 - 力扣(LeetCode)
1 | func merge(intervals [][]int) [][]int { |
57. 插入区间 - 力扣(LeetCode)
1 | func insert(intervals [][]int, newInterval []int) [][]int { |
452. 用最少数量的箭引爆气球 - 力扣(LeetCode)
贪心1
2
3
4
5
6
7
8
9
10
11
12
13
14
15func findMinArrowShots(points [][]int) int {
res := 1
sort.Slice(points, func(i, j int) bool{
return points[i][1] < points[j][1]
})
arr := points[0][1]
for i := 1; i < len(points); i++ {
if points[i][0] > arr {
res += 1
arr = points[i][1]
}
}
return res
}
栈
20. 有效的括号 - 力扣(LeetCode)
栈入门
golang里面没用自带的栈
因此使用切片来模拟栈
就是当动态数组用了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24func isValid(s string) bool {
n := len(s)
if n % 2 == 1 {
return false
}
pairs := map[byte] byte {
')': '(',
']': '[',
'}': '{',
}
stack := []byte{}
for i := 0; i < n; i++ {
if pairs[s[i]] > 0 {
if len(stack) == 0 || stack[len(stack)-1] != pairs[s[i]] {
return false
}
stack = stack[:len(stack)-1]
} else {
stack = append(stack, s[i])
}
}
return len(stack) == 0
}
71. 简化路径 - 力扣(LeetCode)
栈继续尝试1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19func simplifyPath(path string) string {
parts := strings.Split(path, "/")
stack := []string{}
for _ , part := range parts {
if part == "" || part == "." {
continue
}
if part == ".." {
if len(stack) > 0 {
stack = stack[:len(stack)-1]
}
} else {
stack = append(stack, part)
}
}
return "/" + strings.Join(stack, "/")
}
155. 最小栈 - 力扣(LeetCode)
1 | type MinStack struct { |
150. 逆波兰表达式求值 - 力扣(LeetCode)
1 | func evalRPN(tokens []string) int { |
看了一下官方解法确实好很多,不看我都忘记switch了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23func evalRPN(tokens []string) int {
stack := []int{}
for _, token := range tokens {
val, err := strconv.Atoi(token)
if err == nil {
stack = append(stack, val)
} else {
num1, num2 := stack[len(stack)-2], stack[len(stack)-1]
stack = stack[:len(stack)-2]
switch token {
case "+":
stack = append(stack, num1+num2)
case "-":
stack = append(stack, num1-num2)
case "*":
stack = append(stack, num1*num2)
default:
stack = append(stack, num1/num2)
}
}
}
return stack[0]
}
224. 基本计算器 - 力扣(LeetCode)
不想写栈了,好烦
这题没想出来,hard 还是你 hard1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35func calculate(s string) int {
stack := []int{}
var ans int = 0
var num int = 0
var op int = 1
stack = append(stack, op)
for _, st := range s {
if st == ' ' {
continue
}
// 判断数字字符
if st >= '0' && st <= '9' {
// 类型转换:rune -> int
num = num*10 + int(st-'0')
} else {
// 遇到运算符或括号,先把之前的数字结算到 ans
ans += op * num
num = 0
if st == '+' {
op = stack[len(stack)-1]
} else if st == '-' {
op = -stack[len(stack)-1]
} else if st == '(' {
// ( 号:将当前的 op 压入栈,作为括号内新的环境符号
stack = append(stack, op)
} else if st == ')' {
// ) 号:出栈,回到上一层环境
stack = stack[:len(stack)-1]
}
}
}
return ans + op*num
}
链表
141. 环形链表 - 力扣(LeetCode)
第一种,哈希表去存储路过的所有结点1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func hasCycle(head *ListNode) bool {
seen := map[*ListNode]struct{}{}
for head != nil {
if _, ok := seen[head]; ok {
return true
}
seen[head] = struct{}{}
head = head.Next
}
return false
}
但是这边用法好多,需要慢慢理解
head *ListNode很明显就是指针map[*ListNode]struct{}{}:Go内部没有Set()集合,要用Map映射来模拟集合map[keyType]valueType所以这边key是*ListNode,value是struct{}这样一个空结构体,0字节弄好这样一个
map之后要初始化nil==Nonemap查找 (comma-ok idiom)
if _ , ok := map[fuck]; ok { }seen[head] = struct{}{}这边还是用
.来选结构体内部内容
题目问有没有空间为O(1)的算法,这也就说明不能去用哈希表存了,理论上也在只能用指针那种,那当然就是了
快慢指针
「Floyd 判圈算法」1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17func hasCycle(head *ListNode) bool {
if head == nil || head.Next == nil {
return false
}
slow := head
fast := head
for fast != nil && fast.Next != nil {
slow = slow.Next
fast = fast.Next.Next
if slow == fast {
return true
}
}
return false
}
好吧官方题解给的更精简一点1
2
3
4
5
6
7
8
9
10
11
12
13
14func hasCycle(head *ListNode) bool {
if head == nil || head.Next == nil {
return false
}
slow, fast := head, head.Next
for fast != slow {
if fast == nil || fast.Next == nil {
return false
}
slow = slow.Next
fast = fast.Next.Next
}
return true
}
然后起点有那么点不一样罢了,反正最后套圈就能套回来
2. 两数相加 - 力扣(LeetCode)
梦开始的地方到梦结束的地方一站式服务喵
这是我们leetcode第二题1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
var tail *ListNode
head := tail
carry := 0
for l1 != nil || l2 != nil {
n1, n2 := 0, 0
if l1 != nil {
n1 = l1.Val
l1 = l1.Next
}
if l2 != nil {
n2 = l2.Val
l2 = l2.Next
}
sum := n1 + n2 + carry
sum, carry = sum % 10, sum / 10
if head == nil {
head = &ListNode{Val: sum}
tail = head
} else {
tail.Next = &ListNode{Val: sum}
tail = tail.Next
}
}
if carry > 0 {
tail.Next = &ListNode{Val: carry}
}
return head
}
先来理思路吧,创建了一个新的链表tail,head是它的头结点
加法变成carry*10+sum,然后其实就结束了,不过我没用过这里的链表,这边这个链表的创建确实把我难住了
先创建头结点,头节点是一个地址+Val所以直接对ListNode{Val: sum}取地址存起来就行了
tail.Next = &ListNode{Val: sum}
三件事,创建ListNode,ListNode的Val设成sum,tail.Next指向该地址
Q:我有一个问题,如果ListNode{Val: sum}里面东西一样的时候,他们地址还一样吗?
A:不一样,它会在堆(heap)上再开一个空间
当然啦,还可以有更好的写法
虚拟头节点1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
dummy := &ListNode{Val: 0}
tail := dummy
carry := 0
for l1 != nil || l2 != nil || carry > 0 {
n1, n2 := 0, 0
if l1 != nil {
n1 = l1.Val
l1 = l1.Next
}
if l2 != nil {
n2 = l2.Val
l2 = l2.Next
}
sum := n1 + n2 + carry
sum, carry = sum % 10, sum / 10
newNode := &ListNode{Val: sum}
tail.Next = newNode
tail = tail.Next
}
return dummy.Next
}
可以看到我们把很多个判断条件省略了,不需要考虑太多可读性也很好zwz
但是还是想吐槽,链表写起来没用理解起来方便,太需要大脑和操作了
21. 合并两个有序链表 - 力扣(LeetCode)
1 | /** |
过了,但是可以做得更好,里面的101很明显是看数据给到100就这样用的,顺便优化一下写法和算法
不能忘记了链表的特色,一串直接用了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
dummy := &ListNode{Val: 0}
tail := dummy
for list1 != nil && list2 != nil {
if list1.Val <= list2.Val {
tail.Next = list1
list1 = list1.Next
} else {
tail.Next = list2
list2 = list2.Next
}
tail = tail.Next
}
if list1 != nil {
tail.Next = list1
} else {
tail.Next = list2
}
return dummy.Next
}
138. 随机链表的复制 - 力扣(LeetCode)
看哭了,但是其实意思就是深拷贝
那要怎么办呢,有random甚至会导致遍历的时候出现环的情况,所以完全不能这样
那怎么办呢,我抄你的码
抄都抄不明白
那就只能好好看看了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28/**
* Definition for a Node.
* type Node struct {
* Val int
* Next *Node
* Random *Node
* }
*/
var cachedNode map[*Node]*Node
func deepCopy(node *Node) *Node {
if node == nil {
return nil
}
if n, ok := cachedNode[node]; ok {
return n
}
newNode := &Node{Val: node.Val}
cachedNode[node] = newNode
newNode.Next = deepCopy(node.Next)
newNode.Random = deepCopy(node.Random)
return newNode
}
func copyRandomList(head *Node) *Node {
cachedNode = map[*Node]*Node{}
return deepCopy(head)
}
这是官方的第一种解法,官方说是回溯 + 哈希表
但不如说是DFS 深度优先搜索+ 哈希表
var cachedNode map[*Node]*Node全局变量
其实就是map[原链表地址]新创建的对应链表的地址1
2
3
4func copyRandomList(head *Node) *Node {
cachedNode = map[*Node]*Node{}
return deepCopy(head)
}
初始化cachedNode,递归1
2
3if node == nil {
return nil
}
空就别复制了1
2
3if n, ok := cachedNode[node]; ok {
return n
}
创建过对应关系的两个节点就直接用,不能再创1
2newNode := &Node{Val: node.Val}
cachedNode[node] = newNode
创建对应关系
当然还有别的方法
// ToDo
206. 反转链表 - 力扣(LeetCode)
你好1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseList(head *ListNode) *ListNode {
if head == nil {
return nil
}
if head.Next == nil {
return head
}
next := head.Next
var prev *ListNode = nil
for next != nil {
next = head.Next
head.Next = prev
prev = head
head = next
}
return prev
}
写的有点好笑,return head return半天大脑没褶皱了,不过进步的地方是至少能写出来了😢
可以稍微优化一下,前面判断条件实际上可以在下面优化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseList(head *ListNode) *ListNode {
var prev *ListNode = nil
curr := head // 去用curr来存当前的地方就不会因为head而乱七八糟的了
for curr != nil { // curr没遍历完的时候
next := curr.Next // 下一个目的地 ,如果curr.Next == nil的时候也考虑好了
curr.Next = prev // 反向
prev = curr // 准备移动,所以同步往前一步prev和curr
curr = next
}
return prev
}
92. 反转链表 II - 力扣(LeetCode)
哈哈我不适合做链表,做玉玉了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseBetween(head *ListNode, left int, right int) *ListNode {
dummy := &ListNode{Val: 0, Next: head}
preLeft := dummy
for i := 0; i < left - 1; i++ {
preLeft = preLeft.Next
}
curr := preLeft.Next
leftPtr := curr
var prev *ListNode = nil
for i := 0; i < right - left + 1; i++ {
next := curr.Next
curr.Next = prev
prev = curr
curr = next
}
preLeft.Next = prev
leftPtr.Next = curr
return dummy.Next
}
25. K 个一组翻转链表 - 力扣(LeetCode)
第一次ac代码
执行用时分布 3ms 击败1.36%1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseKGroup(head *ListNode, k int) *ListNode {
dummy := &ListNode{Val: 0, Next: head}
curr := head
n := 0
for curr != nil {
n++
curr = curr.Next
}
count := n / k
pre := dummy
curr = head
for i := 0; i < count; i++ {
groupTail := curr
var prev *ListNode = nil
for j := 0; j < k ; j++ {
next := curr.Next
curr.Next = prev
prev = curr
curr = next
}
pre.Next = prev
groupTail.Next = curr
pre = groupTail
}
return dummy.Next
}