2.两数相加
思路:比较简单的一般题,与字符串相同的模拟加法即可。
具体代码如下:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
# 类似这样的需要生成新的listnode的题目使用哑结点会方便处理一些,
# 就是在前面多加一个任意结点,最后返回.next就可以
# 其实ans和head可以理解为指向同一个链表的指针,所以针对head的改动也可以体现在ans上
# ans用来输出最后的listnode,head用来处理遍历生成新的listnode
ans = head = ListNode(0)
digit = 0 # 标记进位情况
# 当l1与l2没有遍历完的时候,依次遍历相加,并将和加入到结果中
while l1 and l2:
num = l1.val + l2.val + digit # 计算l1和l2在该位数上的和
head.next = ListNode(num % 10) # 将两者的和取个位数添加到结果head和ans中
digit = num // 10 # 标记进位情况,有进位则为1,无进位为0
head, l1, l2 = head.next, l1.next, l2.next # 所有链表后移达到遍历效果
# 当l1较长时执行该循环
while l1:
num = l1.val + digit
head.next = ListNode(num % 10)
digit = num // 10
head, l1 = head.next, l1.next
# 当l2较长时执行该循环
while l2:
num = l2.val + digit
head.next = ListNode(num % 10)
digit = num // 10
head, l2 = head.next, l2.next
# l1和l2均遍历完毕,查看最高位是否还有进位,若有则加入
if digit:
head.next = ListNode(digit)
# 返回哑结点的next
return ans.nex
时间复杂度O(max(m,n))
,空间复杂度O(1)
。
4.寻找两个正序数组的中位数
二分法中比较难的题目了,难点在二分中分界线的划分和左右边界的处理。
思路:需要在两个数组中找出两个分界线,分界线左侧的数字总和为(m+n) // 2
(偶数时)或者(m+n)//2 +1
(奇数时),两者统一起来就是(m+n+1)//2
.最终目的是找到使得两个分界线左侧的数字均小于右侧的数字(如果有的话)。详细边界见具体代码:
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
# 让前者为短的,保证二分中的mid和mid-1不越界,(
# 如果这里不作处理的话,二分那里就要分情况讨论边界)
if len(nums1) > len(nums2):
nums1, nums2 = nums2, nums1
n1, n2 = len(nums1), len(nums2)
# +1是为了处理两数组长度之和奇偶不同的情况,half表示中位数左侧的数字个数
half = (n1 + n2 + 1) // 2
"""这里二分需要特殊理解一下,因为分界线是一个虚的(并不存在的界限,
所以需要用真实的index表示),mid代表界限的右侧坐标,mid-1代表界限坐标的数字,
因此right的初值也不一样了,为n1而非n1-1。
"""
left, right = 0, n1
while left < right :
mid1 = left + ((right - left) >> 1)
mid2 = half - mid1
if nums1[mid1] < nums2[mid2 - 1]: # nums1右侧小于nums2左侧,所以mid要右移
left = mid1 + 1
else:
right = mid1
# mid1和mid2分别表示两数组中分界线右侧的元素坐标
mid1 = left
mid2 = half - mid1
# 总个数为奇数的中位数,等于左侧数字的max
# 总个数为偶数的中位数,等于左侧的max和右侧min的均值
# 这句正则表达式看不太懂的话看下面注释掉的代码,两者等价
max_left = max(nums1[mid1-1] if mid1 > 0 else float("-inf"), nums2[mid2-1] if mid2 > 0 else float("-inf") )
# if mid1 > 0:
# left1 = nums1[mid1 - 1]
# else:
# left1 = float('-inf')
# if mid2 > 0:
# left2 = nums2[mid2 - 1]
# else:
# left2 = float('-inf')
# max_left = max(left1, left2)
if (n1 + n2) % 2 == 1:
return max_left
min_right = min(nums1[mid1] if mid1 < n1 else float("inf"), nums2[mid2] if mid2 < n2 else float("inf"))
return (max_left + min_right) / 2
时间复杂度O(logmin(m,n)))
,空间复杂度O(1)
。
5.最长回文子串
主要是最开始可能不太了解中心拓展法这个方法,了解了后就非常简单了。
其实本题最优解并非该方法,而是Manacher
算法(但是非常复杂一般不要求掌握)。
解法一:中心扩展法具体代码如下:
class Solution:
def longestPalindrome(self, s: str) -> str:
def expendFromCenter(index1, index2):
# index1,index2分别表示左右两个指针。
while index1 >= 0 and index2 < n and s[index1] == s[index2]:
index1 -=1
index2 += 1
return index1 + 1, index2 - 1
n = len(s)
start, end = 0, 0
for i in range(1, n):
left1, right1 = expendFromCenter(i, i) # 长度为奇数的情况
left2, right2 = expendFromCenter(i - 1, i) # 长度为偶数的情况
# 如果长度为奇数时更长,更新
if right1 - left1 > end - start:
start, end = left1, right1
# 如果长度为偶数时更长,更新
if right2 - left2 > end - start:
start, end = left2, right2
return s[start: end + 1]
时间复杂度O(n²)
,空间复杂度O(1)
。
解法二:dp解法
官方解法超时:下面给出我的解法,其实此解法思路来自于最长回文子序列
,感兴趣的同学可以参考一下。
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
dp = [[False] * n for _ in range(n)] # 初始化dp数组
max_len = -1
ans = ''
for i in range(n - 1, -1, -1): # i逆向遍历,因为计算i时的dp需要用到i+1的状态
dp[i][i] = True
for j in range(i + 1, n): # j正向遍历
if j - i == 1: # 长度为2时判断是否为
dp[i][j] = s[i] == s[j]
elif j - i > 1 and s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1]
if dp[i][j] and j - i > max_len: # 记录此时的最长子串
max_len = j - i
ans = s[i: j + 1]
if len(s) > 0 and not ans: # 特殊处理一下最长子串为1的情况,因为j是从i+1开始遍历的
ans = s[0]
return ans
时间复杂度O(n²)
,空间复杂度O(n²)
。