1. 两数相加I (2)
题目可以看-原题地址
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807
分析:
(1)注意长度不同的两个数进行相加,且对应位相加的时候是三个数相加,x+y+carry
(2)注意需要进位的情况,答案比原来的数要多一位 如 11 + 99 = 110,因此当任何一个数组没有遍历完或者有进位carry的情况,循环继续。
(3)时间复杂度:O(max(m, n)),假设 m 和 n 分别表示 l1 和 l2 的长度,上面的算法最多重复 O(max(m, n)) 次。
(4)空间复杂度:O(max(m, n)), 新列表的长度最多为 O(max(m, n))+1。
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
dummy = tail = ListNode(0)
s = 0
while l1 or l2 or s:
s += (l1.val if l1 else 0) + (l2.val if l2 else 0)
tail.next = ListNode(s % 10)
tail = tail.next
s //= 10
l1 = l1.next if l1 else None
l2 = l2.next if l2 else None
return dummy.next
2. 两数相加II (445)
题目可以看-原题地址
给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。进阶:
如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。
示例:
输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 8 -> 0 -> 7
分析:
(1)这道题与上一道题的区别:上一道题的高位在链表的尾部,因此可以设定两个指针在链表头部按位遍历再相加。而本题的高位在尾部,要想先让尾部按位相加,可以利用栈的知识,先将两个链表由到尾压入栈中,栈的特性是先入后出,因此可以再出栈时按位相加。
(2)两种特殊情况和上一题就一样了,循环的时候要判断是不是栈是否为空,是否还有进位。另一个就是,按位相加是三个数相加,x+y+carry
(3)如何进行连接,设置dummy节点,新节点的.next指向dummy.next,而dummy.next永远指向头节点。
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
stack1, stack2 = [], []
carry = 0
dummy = ListNode(0)
while l1:
stack1.append(l1.val)
l1 = l1.next
while l2:
stack2.append(l2.val)
l2 = l2.next
while stack1 or stack2 or carry:
if stack1:
val1 = stack1.pop()
else:
val1 = 0
if stack2 :
val2 = stack2.pop()
else:
val2 = 0
value = val1 + val2 + carry
valnode = value % 10
carry = value // 10
node = ListNode(valnode)
node.next = dummy.next
dummy.next = node
return dummy.next
3. 回文链表 (234)
题目可以看-原题地址
请判断一个链表是否为回文链表
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
分析:
本题要空间复杂度为O(1)所以说我们不能开辟新的内存空间。因此我们可以按以下步骤和方法来解体:
(1)截取两段链表:因为是对称链表,因此我们可以截取两段链表。方法:使用快慢指针,快指针是慢指针的2倍速度。
(2)反转链表。方法:三个指针,或者递归都可以。
(3)然后两个链表进行一一比较。
截取链表时要注意,当为奇数链表时,slow指针位于中间点,而偶数链表时,位于中间两个偏右的节点,但是当我们反转链表一一比较时候,只要有一个链表为空时,我们就可以不比较了。所以也不用在意到底是奇数链表还是偶数链表。
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if not head or not head.next:
return True
# 取中位数的上边界,比如[1, 2, 2, 3] 取到是第二个2
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 反转链表
pre = None
cur = slow
while cur:
next_node = cur.next
cur.next = pre
pre = cur
cur = next_node
# 链表对比
p1 = head
p2 = pre
# 若为奇数,中间元素不用比较,所以当有一个链表为空则可以退出循环
while p1 and p2:
if p1.val != p2.val:
return False
p1 = p1.next
p2 = p2.next
return True
4. 分隔链表 (725)
题目可以看-原题地址
给定一个头结点为 root 的链表, 编写一个函数以将链表分隔为 k 个连续的部分。
每部分的长度应该尽可能的相等: 任意两部分的长度差距不能超过 1,也就是说可能有些部分为 null。
这k个部分应该按照在链表中出现的顺序进行输出,并且排在前面的部分的长度应该大于或等于后面的长度。
返回一个符合上述规则的链表的列表。
举例: 1->2->3->4, k = 5 // 5 结果 [ [1], [2], [3], [4], null ]
示例 1:
输入:
root = [1, 2, 3], k = 5
输出: [[1],[2],[3],[],[]]
示例 2:
输入:
root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
输出: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
解释:
输入被分成了几个连续的部分,并且每部分的长度相差不超过1.前面部分的长度大于等于后面部分的长度。
思路:
(1)首先遍历链表长度counts,然后再根据K把其分为K段。
(2)counts // K就是每一段的长度。
(3)根据题意,任意两段的长度不能相差大于1,因此counts % K的余数为m,则代表前面m段,每段的长度要比m段后面的每段长度大多1位。
(4)设置两个循环,外层循环遍历K段,每次把节点放入列表中。
(5)内层循环则是遍历每段的大小,然后移动指针,直到该段达到分配的长度后,就把这段截断,后面的在之后的循环中继续分配,循环的条件是cur不为空,
class Solution:
def splitListToParts(self, root: ListNode, k: int):
total_len = 0
cur = root
while cur:
total_len += 1
cur = cur.next
length = total_len // k # 每段的基础长度
m = total_len % k # 前 l 段需要在基础长度上+1
res = []
cur = root
for i in range(k):
res.append(cur)
size = length + (1 if m > 0 else 0) # 算出每段的长度
if cur: # 这里判断cur是否存在,防止cur.next不存在报错
for j in range(size-1):
cur = cur.next
m -= 1
tmp = cur.next # 把后面一段截掉,后面一段需在后面继续划分
cur.next = None
cur = tmp
return res
class Solution:
def splitListToParts(self, root: ListNode, k: int) -> List[ListNode]:
counts = 0
cur = root
while cur:
cur = cur.next
counts += 1
per_len = counts // k
m = counts % k
cur = root # 指针别忘了移回来,指到头节点
alist = []
for i in range(k):
alist.append(cur)
size = per_len + (1 if m > 0 else 0)
if cur:
for j in range(size - 1):
cur = cur.next
m -= 1
tmp = cur.next
cur.next = None
cur = tmp
return alist
5. 奇偶链表 (328)
题目可以看-原题地址
常规思路:
头节点为奇数,第二个节点为偶数节点,因此我们可以先连接奇数链表,然后在连接偶数连边表,然后让奇数链表的尾部指向偶数链表的头部即可。
class Solution:
def oddEvenList(self, head: ListNode) -> ListNode:
if not head:return head
odd = head
even_head = even = head.next
while odd.next and even.next:
odd.next = odd.next.next
even.next = even.next.next
odd,even = odd.next,even.next
odd.next = even_head
return head