LeetCode上知识卡片之 中级算法的刷题记录
数组和字符串
三数之和
把数组排序后,遍历数组,每次固定当前元素,然后用双指针再后续数组里面搜索与它相加为0的元素组合。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n=len(nums)
res=[]
nums.sort()
for i in range(n):
if nums[i] > 0:
#因为排序了,遍历到大于0的数不可能有结果
return res
#重复元素只要第一个
if(i>0 and nums[i]==nums[i-1]):
continue
l = i + 1
r = n - 1
while l<r:
if nums[i] + nums[l] +nums[r] == 0:
res.append( [nums[i] , nums[l] , nums[r]])
#两个while过滤掉重复元素
while(l<r and nums[l]==nums[l+1]):
l += 1
while(l<r and nums[r]==nums[r-1]):
r -= 1
l -= 1
r -= 1
elif(nums[i]+nums[l]+nums[r]>0):
r-=1
else:
l+=1
return res
矩阵置零
要求原地变换,所以用行列的第一个元素作为标记,来表示该行该列是否要变为0
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
#记录第一列
is_col = False
R = len(matrix)
C = len(matrix[0])
for i in range(R):
#只要第一列出现0,标记为True
if matrix[i][0] == 0:
is_col = True
for j in range(1, C):
if matrix[i][j] == 0:
matrix[0][j] = 0
matrix[i][0] = 0
#根据行列开头标志改变元素
for i in range(1, R):
for j in range(1, C):
if not matrix[i][0] or not matrix[0][j]:
matrix[i][j] = 0
#单独处理第一列和第一行
if matrix[0][0] == 0:
for j in range(C):
matrix[0][j] = 0
if is_col:
for i in range(R):
matrix[i][0] = 0
字母异位词分组
本质上是要我们维护一个字典,字母异位词要对应着同一个key,我们可以把字符串排序后当做key,可以把字母计数结果当作key。
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
ans = collections.defaultdict(list)
#默认字典,索引不在key中,就新建一个空list对应
for s in strs:
count = [0] * 26
for c in s:
count[ord(c) - ord('a')] += 1
ans[tuple(count)].append(s)
return list(ans.values())
无重复字符的最长子串
很明显要使用滑动窗口的思想,一边滑动一边判断就可。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
l = r = out = 0
#窗口内字符
pattern = set()
while r < len(s):
if s[r] not in pattern:
pattern.add(s[r])
out = max(out,r-l+1)
r += 1
else:
pattern.remove(s[l])
l += 1
return out
最长回文子串
回文子串可以分解为对应的子问题,所以采用动态规划思想。要注意的是两个循环的写法,要保证子问题的求解顺序,不能先出现依赖还没求解子问题的高阶问题。
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
dp = [[False] * n for _ in range(n)]
ans = ""
# 枚举子串的长度 l+1
for l in range(n):
#子串起始位置
for i in range(n):
j = i + l
if j >= len(s):
break
if l == 0:
dp[i][j] = True
elif l == 1:
dp[i][j] = (s[i] == s[j])
else:
dp[i][j] = (dp[i + 1][j - 1] and s[i] == s[j])
if dp[i][j] and l + 1 > len(ans):
ans = s[i:j+1]
return ans
递增的三元子序列
问题只要求判断存在与否就可,我们可以用两个变量存储当前的最小值和次小值,如果新遍历到的值大于次小值,那么已经构成了一个三元组,可以返回True;如果不大于,则根据具体值更新最小次小值即可。
class Solution:
def increasingTriplet(self, nums: List[int]) -> bool:
n1 = float('inf')
n2 = float('inf')
for num in nums:
if num <= n1:
n1 = num
elif num <= n2:
n2 = num
else:
return True
return False
链表
两数相加
模拟字符串数字加法即可
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
res = p = ListNode()
carry = 0
while l1 or l2 or carry:
#优化边界判断
x = l1.val if l1 else 0
y = l2.val if l2 else 0
p.next = ListNode((x+y+carry)%10)
p = p.next
carry = (x+y+carry)//10
if l1:
l1 = l1.next
if l2:
l2 = l2.next
return res.next
奇偶链表
用两个指针分别记录奇链表尾和偶链表头,再用一个指针遍历原链表(顺便充当偶链表尾)即可
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def oddEvenList(self, head: ListNode) -> ListNode:
if not head :
return head
p1 = head
p2 = p = head.next
#P1,P2分别为奇偶链表尾
while p2 and p2.next:
p1.next = p2.next
p1 = p1.next
p2.next = p2.next.next
p2 = p2.next
p1.next = p
return head
相交链表
把两个链表分别前后顺序拼接起来,如果是相交的,遍历的时候一定会到相同的节点(走过相同的路)。
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
p1 = headA
p2 = headB
while p1 != p2:
p1 = p1.next if p1 else headB
p2 = p2.next if p2 else headA
#如果两个链表不相交的话,p1和p2都是空值
return p1
树和图
中序遍历二叉树
递归的解法很容易写出
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
res = []
def help(root,res):
if not root:
return
help(root.left,res)
res.append(root.val)
help(root.right,res)
help(root,res)
return res
迭代需要考虑访问顺序
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
res = []
s = []
x = root
while s or x:
while x:
s.append(x)
x = x.left
#左子树全部进栈了
#接下来弹出的就是根节点
x = s.pop()
res