206. Reverse Linked List
- ver1是迭代版,但是一开始写while的判断条件写成cur.next,最后return的是cur,最后输出只有反转后的头节点,实际上最后一个节点还没有连上去,最后一个节点进不了循环,前面还是断开的orz。
- ver2的递归版本实际上思想和迭代版是一样的,而且直接可以输出翻转后的节点;
- ver3的递归版本则是deep到当前根节点再逐步往回,这里会很奇怪的点在于实际上它又回去了,最后要输出翻转链表后的头节点,怎么去保存翻转后的节点呢?别人写的题解好棒!!动图非常清楚!
- 例子(1,2,3,4,5)实际上终止的时候cur = 5,但是此时head=4!!然后他去翻转之后return了cur 就是说下一步cur还是5!这就起到了保存反转后的头节点的作用了!
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def reverseList(self, head: ListNode):
# if not head or not head.next:
# return head
prev, cur = None, head
while cur:
tmp = cur.next
cur.next = prev
prev = cur
cur = tmp
return prev
def reverseList2(self, head):
def dfs(prev, cur):
if not cur:
return prev
tmp = cur.next
cur.next = prev
return dfs(cur, tmp)
return dfs(None, head)
def reverseList3(self, head):
if not head or not head.next:
return head
cur = self.reverseList3(head.next)
head.next.next = head
head.next = None
return cur
21. Merge Two Sorted Lists
- ver2实际上把ver1每次加一个节点后断开的操作省了;
- ver3是令我头疼的递归orz…尝试说明一下
- 问题分解为比较当前两个链表的头节点的大小,假设list1的当前头节点值比较大,则把下一个要连接的就是list1.next和list2链表的最大值,嗯就是一样的问题了;
- 还有就是返回的问题("总是学不会~再聪明一点"orz)【再补充吧 讲不来orz】
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def mergeTwo(self, list1, list2):
dummy = ListNode(0)
cur = dummy
while list1 and list2:
if list1.val < list2.val:
cur.next = list1
list1 = list1.next
cur = cur.next
cur.next = None
else:
cur.next = list2
list2 = list2.next
cur = cur.next
cur.next = None
if list1:
cur.next = list1
if list2:
cur.next = list2
return dummy.next
def mergeTwoLists2(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(0)
cur = dummy
while list1 and list2:
if list1.val < list2.val:
cur.next = list1
list1 = list1.next
else:
cur.next = list2
list2 = list2.next
cur = cur.next
if list1:
cur.next = list1
if list2:
cur.next = list2
return dummy.next
def mergeTwoLists3(self, list1, list2):
if not list1 or not list2:
return list1 or list2
if list1.val < list2.val:
list1.next = self.mergeTwoLists3(list1.next, list2)
return list1
else:
list2.next = self.mergeTwoLists3(list1, list2.next)
return list2
19. Remove Nth Node From End of List
- ver1快慢指针k
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int):
dummy = ListNode(0, head)
left, right = dummy, dummy
for _ in range(n):
right = right.next
while right.next:
left = left.next
right = right.next
left.next = left.next.next
return dummy.next
143. Reorder List
- 我的v1初试,非常直观的想法就是left后指向left链表的最后一个元素,插到left后,left更新成left.next.next,继续找,知道left和最后一个节点重合;
- 很不幸的是是的!超时了!!!!
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def reorderList(self, head: ListNode) -> None:
# 说是找到最后一个实际上是最后一个节点的前一个
def last(head):
if not head.next:
return head
right = head
while right.next.next:
right = right.next
return right
left = head
right = last(left)
t = 0
while left != right:
t += 1
tmp1 = left.next
tmp2 = right.next
right.next = None
left.next = tmp2
tmp2.next = tmp1
left = left.tmp1
right = last(left)
- 既然超时,我们想想有没有别的出路?
- 问题拆分:实际上相当于把原链表对半砍断,第二个链表翻转,在两个链表按顺序merge。
- 对半砍: 快慢指针,注意保留的是第一个list的尾部,即第二个list开头的前一个节点因为想把它断开[low.next=None]
- 链表翻转:对第二个链表(迭代)
- merge:第一个用完了就直接连上第二个(其实只会出现第二个剩1or2的情况)
- 问题拆分:实际上相当于把原链表对半砍断,第二个链表翻转,在两个链表按顺序merge。
好绝!用tmp成瘾了么2333
def reorderList2(self, head: ListNode) -> None:
# 找到中点(low为中点的前一个节点)
low, fast = head, head.next
while fast and fast.next:
low = low.next
fast = fast.next.next
# 断开
l2 = low.next
low.next = None
l1 = head
# 翻转链表
def reverse(head):
prev, cur = None, head
while cur:
tmp = cur.next
cur.next = prev
prev = cur
cur = tmp
return prev
# 合并链表
def merge(l1, l2):
while l1.next:
tmp1, tmp2 = l1.next, l2.next
l1.next = l2
l2.next = tmp1
l1, l2 = tmp1, tmp2
l1.next = l2
return l1
# 不用return你就自己做吧
merge(l1, reverse(l2))
138. Copy List with Random Pointer
很好我又没看懂题目让我干嘛…
去看了下nc的思路,哦是创建一个和他一模一样的链表;
- 如果一次创建就连起来(next和random)实际上是不可操作的,random指向的可能是你没有创建的节点;
- 考虑两次遍历,用hash存储{old:new}
- 第一次遍历:创建节点,hash存储;
- 第二次遍历:next,random指向创建好的新的node(通过old的next,random + hash找到)
class Node:
def __init__(self, x, next=None, random=None):
self.val = int(x)
self.next = next
self.random = random
class Solution:
def copyRandomList(self, head):
odd2copy = {None: None}
cur = head
while cur:
node = Node(cur.val)
odd2copy[cur] = node
cur = cur.next
cur = head
while cur:
node = odd2copy[cur]
node.next = odd2copy[cur.next]
node.random = odd2copy[cur.random]
cur = cur.next
return odd2copy[head]
2. Add Two Numbers
- ver1:考虑两种情况
- l1,l2位数不齐的,不足就补零(好像把原list给改了orz)
- 进位:位数上直接考虑进去
(l1.val+l2.val+up)%10
;超出长度的进位再加一个1就好了;
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def addTwoNumbers(self, l1, l2):
dummy = ListNode(-1)
cur = dummy
up = 0
while l1 or l2:
if not l1:
l1 = ListNode(0)
if not l2:
l2 = ListNode(0)
tmp = l1.val + l2.val + up
node = ListNode(tmp % 10)
cur.next = node
cur = cur.next
l1, l2 = l1.next, l2.next
up = 1 if tmp >= 10 else 0
if up == 1:
node = ListNode(1)
cur.next = node
return dummy.next
- 上面提到ver1当两个list的位数不齐时修改了原list(补零),下面来一版没有补零的操作+合并了超出位的情况;((๑•̀ㅂ•́)و✧)
def addTwoNumbers2(self, l1, l2):
dummy = ListNode(-1)
cur = dummy
up = 0
while l1 or l2 or up == 1:
val1 = l1.val if l1 else 0
val2 = l2.val if l2 else 0
tmp = val1 + val2 + up
node = ListNode(tmp % 10)
up = tmp // 10
cur.next = node
cur = cur.next
l1 = l1.next if l1 else None
l2 = l2.next if l2 else None
return dummy.next
141. Linked List Cycle
class Solution:
def hasCycle(self, head):
low, fast = head, head
while fast and fast.next:
low = low.next
fast = fast.next.next
if low == fast:
return True
return False
287. Find the Duplicate Number
*这道题有丢丢特殊竟然是Linked List类下的,主要还是他不能不修改 数组 nums 且只用常量级 O(1) 的额外空间
∈
\in
∈不做就很难有思路的问题;
- 首先要明确他的值是[1,n],长度是n+1,那么你的第一个位置的index=0这个位置实际上后面不用担心链表会回来,而且值为[1,n]确保后面的都能被访问到,所以把index0作为链表的头是没有问题的;
- 下面就是链表的判断是否有环+环的交点的问题了 ∈ \in ∈Floyd判圈算法;[挖🕳待补,fast low的case]
def findDuplicate2(self, nums):
fast, low = 0, 0
while True:
fast = nums[nums[fast]]
low = nums[low]
if low == fast:
break
fast = 0
while True:
fast = nums[fast]
low = nums[low]
if low == fast:
return low
25. Reverse Nodes in k-Group
虽然写的很丑,但是竟然效果还不错!!!耶耶耶!hard耶!!!泪目了好吗!
- 记录要reverse的list的前一个节点l1,以及后面的list的头l2;断开!reverse之后接起来(连l2时候把遍历到reverse的尾部)!
- 处理不够的用t来计数,不足的直接break掉;
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
dummy = ListNode(-1, head)
cur = dummy
while cur.next:
l1 = cur
t = 0
for _ in range(k):
if not cur.next:
break
cur = cur.next
t += 1
if t < k:
break
tmp = l1.next
l2 = cur.next
l1.next = None
cur.next = None
rev = self.reverse(tmp)
l1.next = rev
while rev.next:
rev = rev.next
rev.next = l2
cur = rev
return dummy.next
def reverse(self, head):
prev, cur = None, head
while cur:
tmp = cur.next
cur.next = prev
prev = cur
cur = tmp
return prev
23. Merge K Sorted Lists
- 参考的neetcode~
- 主要的思想是归并(本质是Divide and Conquer)
- 主要思路:两两合并直到只剩下一个;(复用merge 2 sorted lists模块)
- 时间复杂度: O ( n l o g k ) O(nlogk) O(nlogk)
- 学习的点:
- 考虑边界;
- range的step的设置,之前都用的默认233;
- l2边界的设置,考虑奇数个的情况!
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]):
if not lists or len(lists) == 0:
return None
while len(lists) > 1:
mergedLists = []
for i in range(0, len(lists), 2):
l1 = lists[i]
l2 = lists[i + 1] if (i + 1) < len(lists) else None
mergedLists.append(self.merge2Lists(l1, l2))
lists = mergedLists
return lists[0]
def merge2Lists(self, l1, l2):
dummy = ListNode()
cur = dummy
while l1 and l2:
if l1.val < l2.val:
cur.next = l1
l1 = l1.next
else:
cur.next = l2
l2 = l2.next
cur = cur.next
if l1:
cur.next = l1
if l2:
cur.next = l2
return dummy.next