203.移除链表元素
定位到需要删除节点的上一个节点 cur,将其指向下下个节点。
class Solution:def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:dummy_head = ListNode(val = 0, next = head)cur = dummy_headwhile cur and cur.next:if cur.next.val == val:cur.next = cur.next.nextelse:cur = cur.nextreturn dummy_head.next
83.删除排序链表中的重复元素
与上一题是一样的思路。由于删除后,重复元素只出现一次,因此头节点不需要删除,无需 dummy_head。
class Solution:def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:cur = headwhile cur and cur.next:if cur.next.val == cur.val:cur.next = cur.next.nextelse:cur = cur.nextreturn head
82.删除排序链表中的重复元素II
与上一题不同的是,这次我们需要完全删除所有重复元素。因此我们定位到要删除元素的前一个节点。
class Solution:def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:dummy_head = ListNode(val = 0, next = head)cur = dummy_headwhile cur and cur.next and cur.next.next:if cur.next.val == cur.next.next.val:repeat_val = cur.next.valwhile cur.next and cur.next.val == repeat_val:cur.next = cur.next.nextelse:cur = cur.nextreturn dummy_head.next
112.路径总和
- 基本情况处理:
- 当前节点为
None
时,直接返回False
,因为空节点不构成路径。- 检查叶子节点:
- 当前节点既没有左子节点也没有右子节点(即是叶子节点),检查其值是否与剩余的
targetSum
相等。如果相等,说明找到了一条有效路径,返回True
。- 递归求解:
- 如果不是叶子节点,或者叶子节点的值不等于
targetSum
,则递归地在左右子树中寻找是否存在符合条件的路径。- 递归调用时,更新
targetSum
为targetSum - root.val
,表示从当前节点向下寻找路径时,剩余的目标和需要减去当前节点的值。
class Solution:def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:if not root:return Falseif not root.left and not root.right and targetSum == root.val:return Truereturn self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right, targetSum - root.val)
113.路径总和II
和112的区别是,需要记录路径。
class Solution:def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:res, path = [], []def dfs(node, currentSum):if not node:returnpath.append(node.val)if not node.left and not node.right and currentSum == node.val:res.append(path[:])dfs(node.left, currentSum - node.val)dfs(node.right, currentSum - node.val)path.pop()dfs(root, targetSum)return res
437.路径总和III
countPathsFromNode 函数:
- 这是一个递归函数,用于计算从当前节点
node
开始,向下所有可能的路径中,值之和等于currentSum
的路径数量。- 函数首先检查当前节点是否为
None
。如果是,说明已经到达了叶子节点之下的位置,返回 0。- 如果当前节点的值恰好等于
currentSum
,说明从某个上层节点到当前节点的路径和正好是targetSum
,此时count
加一。- 接下来递归地在左右子树中查找减去当前节点值后,路径和为
currentSum - node.val
的路径数量。pathSum 函数:
- 这个函数负责三件事:
- 调用
countPathsFromNode
来找出以当前节点(即根节点)为起点的所有符合条件的路径。- 递归地在左子树中查找符合条件的路径数量。
- 递归地在右子树中查找符合条件的路径数量。
- 通过将这三部分的结果相加,得到了整棵树中符合条件的路径总数。
class Solution:def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int:# 从指定节点开始,计算以当前节点为起点的符合条件的路径数量def countPathsFromNode(node, currentSum):if not node:return 0 # 如果节点为空,直接返回0count = 0# 如果当前节点的值等于剩余所需的sum值,那么找到了一条符合条件的路径if node.val == currentSum:count += 1# 递归计算左子树中的符合条件的路径数量count += countPathsFromNode(node.left, currentSum - node.val)# 递归计算右子树中的符合条件的路径数量count += countPathsFromNode(node.right, currentSum - node.val)return countif not root:return 0 # 如果根节点为空,则返回0# 计算以根节点为起点的路径数量res = countPathsFromNode(root, targetSum)# 递归计算左子树中的路径数量res += self.pathSum(root.left, targetSum)# 递归计算右子树中的路径数量res += self.pathSum(root.right, targetSum)return res
151.反转字符串中的单词
- .split() 去除空格,将单词存为数组
- 双指针调整单词位置
- ' '.join() 重新转为 string,单词间空格隔开
class Solution:def reverseWords(self, s: str) -> str:words = s.split()left, right = 0, len(words) - 1while left < right:words[left], words[right] = words[right], words[left]left += 1right -= 1return ' '.join(words)
我们也可以不使用内置函数 split():
- 去除首尾空格
- 去除单词间多余空格,确保单词由1个空格隔开
- 反转整体
- 反转单词
class Solution:def reverseWords(self, s: str) -> str:# 因为python字符串不可变,将字符串转为列表s_list = []left, right = 0, len(s) - 1# 去除首位空格while left < len(s) and s[left] == ' ':left += 1while right > 0 and s[right] == ' ':right -= 1# 去除单词间多余空格for i in range(left, right + 1):if s[i] != ' ':s_list.append(s[i])else:if s_list[-1] != ' ':s_list.append(s[i])# 定义反转函数def reverse(left, right):while left < right:s_list[left], s_list[right] = s_list[right], s_list[left]left += 1right -= 1# 反转整体left, right = 0, len(s_list) - 1reverse(left, right)# 反转单词n = len(s_list)start = 0for i in range(n):if s_list[i] == ' ':reverse(start, i - 1)start = i + 1if i == n - 1:reverse(start, i)return ''.join(s_list)
186.反转字符串中的单词II
与上一题不同的是,本题的输入为数组,每个字符都是一个元素。采取以下策略:
- 反转整个字符串数组:首先,将整个字符数组
s
完全反转。这一步会使得所有单词的顺序完全颠倒,但单词内部的字符顺序也被反转了。- 反转每个单词:接下来,遍历反转后的数组,并反转每个单词内的字符。单词的界定是由空格分隔的非空格字符序列。
class Solution:def reverseWords(self, s: List[str]) -> None:"""Do not return anything, modify s in-place instead."""# 定义反转函数def reverse(start, end):while start < end:s[start], s[end] = s[end], s[start]start += 1end -= 1# 反转整体reverse(0, len(s) - 1)# 反转每个单词# 初始化首个单词的起始位置start = 0for i in range(len(s)):# 定位每个单词的末尾位置if s[i] == ' ':reverse(start, i - 1)# 更新下一个单词的起始位置start = i + 1# 最后一个单词的末尾位置无法通过空格定位elif i == len(s) - 1:reverse(start, i)
557.反转字符串中的单词III
本题只需反转单词。
class Solution:def reverseWords(self, s: str) -> str:words = s.split()def reverse(word):my_list = list(word)start, end = 0, len(my_list) - 1while start < end:my_list[start], my_list[end] = my_list[end], my_list[start]start += 1end -= 1return ''.join(my_list)# 反转每个单词for i in range(len(words)):words[i] = reverse(words[i])return ' '.join(words)
179.最大数
思路:
- 对于两个数字
a
和b
,比较两种连接方式a+b
和b+a
的结果。- 如果
a+b >
b+a
,则a
应该排在b
之前。反之,则b
应该排在a
前面。
举例 nums[56, 6, 60]
- 我们比较任意两个字符串
x
和y
,看x+y
与y+x
的大小:
- 比较 56 和 6: 566 < 656,所以 6 应该排在 56 前面。
- 比较 6 和 60: 660 > 606,所以 6 应该排在 60 前面。
- 比较 56 和 60: 5660 < 6056,所以 60 应该排在 56 前面。
- 根据这些比较,排序结果是 [6, 60, 56]。连接这些字符串得到最终结果 "66056"。
class Solution:def largestNumber(self, nums: List[int]) -> str:# 自定义排序def custom_compare(a, b):if a + b > b + a:return -1elif a + b < b + a:return 1else:return 0# 题目要求输出字符串str_nums = map(str, nums)sorted_nums = sorted(str_nums, key = cmp_to_key(custom_compare))# 如果首位是0,则说明后续元素全为0,为避免诸如'00'这样的输出。return ''.join(sorted_nums) if sorted_nums[0] != '0' else '0'
22.括号生成
我们可以使用回溯来解决,问题的关键在于如何确保括号的合法性:
左括号的数量始终不超过 n:在添加左括号时,只有当已添加的左括号数量小于 n 时,才可以添加一个左括号。这确保了左括号不会超过它们的总数。
右括号的数量始终不超过左括号:在添加右括号时,只有当已添加的右括号数量小于已添加的左括号数量时,才可以添加一个右括号。
class Solution:def generateParenthesis(self, n: int) -> List[str]:res = []def backtrack(s, left, right):# 递归终止条件if len(s) == 2 * n:res.append(s)returnif left < n:backtrack(s + '(', left + 1, right)if right < left:backtrack(s + ')', left, right + 1)backtrack('', 0, 0)return res