本文目录
24. 两两交换链表中的节点
代码随想录:24. 两两交换链表中的节点
Leetcode:24. 两两交换链表中的节点
直接做题
自己推导,不用虚拟头结点,基本OK,但发现[1, 2, 3, 4]输出后变成了[2, 1, 3],看了代码随想录,感觉思路不一样,还是自己捣鼓了一下,发现是没有将1索引到4。最终结果如下:
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return head
pre = head
cur = head.next
new_head = cur
while True:
temp = cur.next
pre.next = temp
cur.next = pre
if temp and temp.next:
pre.next = temp.next # 解决1索引到4
pre = temp
cur = temp.next
else:
return new_head
看文章
时间复杂度: O(n)
空间复杂度: O(1)
虚拟结点
相当于把我的初始边界条件判断,用虚拟节点解决了。但我感觉反而比较难理解,具体见“个人注释”。
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
dummy_head = ListNode(next=head)
current = dummy_head
# 必须有cur的下一个和下下个才能交换,否则说明已经交换结束了
while current.next and current.next.next:
temp = current.next # 防止节点修改
temp1 = current.next.next.next
current.next = current.next.next
current.next.next = temp # “个人注释”:因为上一行,这里其实cur已经被改变了
temp.next = temp1
current = current.next.next
return dummy_head.next
时间复杂度:O(n)
空间复杂度:O(1)
递归
比起上面,这个反而更好理解,它在处理头结点的思路和我一样。
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
if head is None or head.next is None:
return head
# 待翻转的两个node分别是pre和cur
pre = head
cur = head.next
next = head.next.next
cur.next = pre # 交换
pre.next = self.swapPairs(next) # 将以next为head的后续链表两两交换
return cur
19.删除链表的倒数第N个节点
代码随想录:19.删除链表的倒数第N个节点
Leetcode:19.删除链表的倒数第N个节点
做题
考虑快慢节点,但是没想出来。看了代码随想录,fast比slow多走n+1步,且设置虚拟头结点。但代码随想录中的python代码没有考虑fast.next为none的情况,不知道为什么ac了。我看完修改的代码如下:
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy_head = ListNode(0, next=head)
slow = fast = dummy_head
for i in range(n+1):
fast = fast.next
if not fast and i < n:
return dummy_head.next
while fast:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy_head.next
时间复杂度: O(n)
空间复杂度: O(1)
面试题 02.07. 链表相交
代码随想录:面试题 02.07. 链表相交
Leetcode:面试题 02.07. 链表相交
直接做题
没想出怎么用时间复杂度O(n)、仅用O(1)内存的方案。
看文章
思路如下:
- 先分别求A、B的长度,然后使lenA < lenB(不满足则互换)。
- 将指针移动至lenB - lenA的位置上,使两个链表尾部对齐。
- 指针同时移动,如果指针位置的val相等,则返回val,否则继续移动,直至结束。
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA, lenB = 0, 0
cur = headA
while cur: # 求链表A的长度
cur = cur.next
lenA += 1
cur = headB
while cur: # 求链表B的长度
cur = cur.next
lenB += 1
curA, curB = headA, headB
if lenA > lenB: # 让curB为最长链表的头,lenB为其长度
curA, curB = curB, curA
lenA, lenB = lenB, lenA
for _ in range(lenB - lenA): # 让curA和curB在同一起点上(末尾位置对齐)
curB = curB.next
while curA: # 遍历curA 和 curB,遇到相同则直接返回
if curA == curB:
return curA
else:
curA = curA.next
curB = curB.next
return None
时间复杂度:O(n + m)
空间复杂度:O(1)
思路是能看明白,但我对时间复杂度的理解有误,实际上应该是遍历了小于等于2n+2m次,但时间复杂度确实为O(n + m)。
142.环形链表II
代码随想录:142.环形链表II
Leetcode:142.环形链表II
做题
快慢指针,快指针每次走两步,慢指针每次走一步,可以找到环。但环的位置不会找。
看文章
环的位置:两个指针分别从起点、相遇处出发,再次相遇时,即可通过前者找到环的位置。
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# If there is a cycle, the slow and fast pointers will eventually meet
if slow == fast:
# Move one of the pointers back to the start of the list
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
# If there is no cycle, return None
return None
时间复杂度: O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n
空间复杂度: O(1)
以往忽略的知识点小结
- 多用虚拟节点
- 找到倒数第n个点,可以通过fast比slow先走n+1步
- 反复k次遍历一个长度为n的链表,只要k << n,一般就视其时间复杂度为O(n)
- 快慢指针,也可以是步幅快
- 环形链表,需要推导规律,找环为追及问题,找环的位置就从起点、追及点同步往前走
个人体会
完成时间:3h。
心得:思路越来越丰富,还是要慢慢积累、循序渐进,有时间的话要静下心多想想。