目录
一、22. 括号生成
1.dfs-枚举填左括号还是右括号
我想到了用dfs,但是边界写错了。来自灵神题解(. - 力扣(LeetCode))。
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
# dfs
# 枚举填左括号还是右括号
m = 2 * n
ans = []
path = ['' for _ in range(m)]
def dfs(i: int, n_left: int) -> None:
if i == m:
ans.append(''.join(path))
return
if n_left < n: # 可以填左括号
path[i] = '('
dfs(i + 1, n_left + 1)
if i - n_left < n_left: # 可以填右括号
# 右括号 < 左括号
path[i] = ')'
dfs(i + 1, n_left)
dfs(0, 0)
return ans
2.dfs-枚举下一个左括号的位置
来自灵神题解。
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
# dfs
# 枚举下一个左括号的位置
ans = []
path = [] # 储存左括号的下标
# balance = 左括号个数 - 右括号个数
def dfs(i: int, balance: int) -> None:
if len(path) == n: # 左括号个数为n
s = [')' for _ in range(2 * n)]
for j in path:
s[j] = '('
ans.append(''.join(s))
return
for n_right in range(balance + 1):
# n_right在i后面添加的右括号个数
path.append(i + n_right)
# 填的右括号的后面一位即为左括号的位置
dfs(i + n_right + 1, balance + 1 - n_right)
path.pop() # 注意pop,回溯
dfs(0, 0)
return ans
二、26. 删除有序数组中的重复项
1.模拟
当时没读懂题,原来是只考虑前k个数。
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
# 模拟
n = len(nums)
if n < 1: return 0
ans = [nums[0]]
cnt = 1
pre = nums[0]
for x in nums[1:]:
if x != pre:
ans.append(x)
cnt += 1
pre = x
nums[:] = ans
return cnt
2.双指针
来自题解(. - 力扣(LeetCode))。
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
# 双指针
'''
注意这里只考虑前k个数,nums 的其余元素与 nums 的大小不重要
'''
n = len(nums)
if n == 0: return 0
left, right = 0, 1
while right < n:
if nums[left] != nums[right]:
left += 1
# if left < right: nums[left] = nums[right]
nums[left] = nums[right]
right += 1
return left + 1
三、27. 移除元素
1.双指针
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
# 双指针
n = len(nums)
left, right = -1, 0
while right < n:
if nums[right] != val:
left += 1
nums[left] = nums[right]
right += 1
return left + 1
2.双指针优化
思路来自官方题解(. - 力扣(LeetCode)),代码自写。return处只能使用right + 1,当最后位置left==right时,经过内层whle,right和left之间还有一个数;当数组全为val时,right=-1,left = 1,综上right+1处即为答案。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
# 双指针优化
# 可改变元素顺序,首尾双指针
left, right = 0, len(nums) - 1
while left <= right:
# 闭区间
if nums[left] == val:
while nums[left] == val and left <= right:
nums[left] = nums[right]
right -= 1
left += 1
return right + 1
官方题解写法:
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
# 双指针优化
# 可改变元素顺序,首尾双指针
left, right = 0, len(nums) - 1
while left <= right:
# 闭区间
if nums[left] == val:
nums[left] = nums[right]
right -= 1
else:
left += 1
return left # 这里使用left和right+1均可
四、28. 找出字符串中第一个匹配项的下标
1.直接暴力匹配
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
# 直接暴力匹配
# 数据量不大
n, m = len(haystack), len(needle)
for i in range(0, n - m + 1):
k, j = i, 0
while j < m and haystack[k] == needle[j]:
k += 1
j += 1
if j == m: return i
return -1
2.kmp算法
class Solution:
def build_next(self, patt: str) -> List[int]:
# 构建next数组
n = len(patt)
if n == 0: return []
res_next = [0] * n
prefix_len = 0
i = 1 # 第一个肯定为0,从1开始
while i < n:
if patt[i] == patt[prefix_len]:
prefix_len += 1 # 先加一
res_next[i] = prefix_len
i += 1
elif prefix_len == 0:
i += 1
else:
# 找到前一位的next值,跳转
prefix_len = res_next[prefix_len - 1]
return res_next
def strStr(self, haystack: str, needle: str) -> int:
# kmp算法
nxt = self.build_next(needle)
i, n = 0, len(haystack) # 主串
j, m = 0, len(needle) # 子串
while i < n:
if haystack[i] == needle[j]:
i += 1
j += 1
elif j > 0:
j = nxt[j - 1]
else:
i += 1
# 注意返回起始位置,此时的i在匹配串末尾
if j == m: return i - j
return -1
五、842. 排列数字 - AcWing题库
dfs
n = int(input())
flag = [0 for _ in range(n + 1)] # 记录是否被选,0没选,1选
flag[0] = 1 # 不包括数字0
path = [0 for _ in range(n)]
def dfs(i: int) -> None:
if i == n:
# 到达边界
for x in path:
print(x, end = ' ')
print()
return
for j, x in enumerate(flag):
if not x:
path[i] = j # 下标代表元素
flag[j] = 1
dfs(i + 1)
flag[j] = 0
dfs(0)
六、AcWing 843. n-皇后问题 - AcWing
dfs
不会,来自题解(AcWing 843. n-皇后问题--图解+代码注释 - AcWing)。注意其中斜线的表示方法。
n = int(input())
map = [['.' for _ in range(n)] for _ in range(n)]
col = [0 for _ in range(n)] # 列标记
dg = [0 for _ in range(n * 2)] # 左上到右下对角线,注意是2n
udg = [0 for _ in range(n * 2)] # 左下到右上对角线
def dfs(i: int) -> None:
# i表示行数
if i == n:
# 输出map
for i in range(n):
for j in range(n):
print(map[i][j], end = '')
print() # 输出换行符
print() # 输出间隔空行
return
for j in range(n):
if not col[j] and not dg[n - i + j] and not udg[i + j]:
# 可以放置
col[j] = dg[n - i + j] = udg[i + j] = 1
map[i][j] = 'Q'
dfs(i + 1)
# 回
col[j] = dg[n - i + j] = udg[i + j] = 0
map[i][j] = '.'
dfs(0)
七、844. 走迷宫 - AcWing题库
bfs
不会,来自题解(AcWing 844.走迷宫 - AcWing --- AcWing 844. 走迷宫 - AcWing)。
# 最少移动次数->bfs
from collections import deque
def dfs() -> int:
dx = [-1, 1, 0, 0]
dy = [0, 0, 1, -1]
q = deque([])
q.append([0, 0]) # 起点入队
mark[0][0] = 0
while q:
cur = q.popleft()
for i in range(4):
# 四个方向
x = cur[0] + dx[i]
y = cur[1] + dy[i]
if 0 <= x < n and 0 <= y < m and not maze[x][y] and mark[x][y] == -1:
# 可走且没被走过
mark[x][y] = mark[cur[0]][cur[1]] + 1
q.append([x, y]) # 新节点入队
return mark[-1][-1]
if __name__ == '__main__':
n, m = map(int, input().split())
maze = [list(map(int, input().split())) for _ in range(n)]
mark = [[-1 for _ in range(m)] for _ in range(n)] # 记录到达(i, j)点的最少移动次数
print(dfs())
八、845. 八数码 - AcWing题库
bfs
不会,来自题解(AcWing 845型。八数码 - AcWing --- AcWing 845. 八数码 - AcWing)。这道题的关键是:
1.使用字符串存储状态
2.使用字典来存储状态之间的距离
3.每一次更新状态后需要还原
# from sys import *
from collections import *
def dfs(start: str) -> int:
# 使用字符串存储状态
END = '12345678x'
q = deque([])
# mark = {}
mark = defaultdict(lambda:0)
q.append(start)
mark[start] = 0
dx = [0, 0, 1, -1]
dy = [-1, 1, 0, 0]
while q:
cur = q.popleft()
if cur == END: return mark[cur]
# 寻找一维位置
for idx, x in enumerate(cur):
if x == 'x':
break
x, y = idx // 3, idx % 3
for i in range(4):
a, b = x + dx[i], y + dy[i]
temp_state = list(cur) # 还原状态
if 0 <= a < 3 and 0 <= b < 3:
temp_state[a * 3 + b], temp_state[idx] = temp_state[idx], temp_state[a * 3 + b]
s = ''.join(temp_state)
if s not in mark:
mark[s] = mark[cur] + 1
q.append(s)
return -1
if __name__ == '__main__':
start = ''.join(input().split())
# start = stdin.readline().replace(" ", "").replace("\n", "")
print(dfs(start))
完
感谢你看到这里!一起加油吧!