classSolution: defreverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: '''3.非递归法-2''' # 速度最快,消耗内存最少 pre, cur = None, head while cur: rest = cur.next cur.next = pre pre = cur cur = rest return pre
classSolution: defreverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]: if head isNoneor head.nextisNone: return head if left == right: return head
start, node_L, pre = 1, head, None # 先找到node_L,反转起始结点 while start < left: pre = node_L node_L = node_L.next start += 1
# 采用code最简单的那个反转方式 # 这里不用专门找结束位置,因为可以通过控制 i 与 right 的关系来停止 front, cur, i = None, node_L, left while i <= right: res = cur.next cur.next = front front = cur cur = res i+=1 # 反转完成后,这一部分的起始结点就是 front结点了 # 这一部分的尾结点,就是原始这一区间的开头:node_L # 还需要和 区间之后的那一部分 链接起来 node_L.next = res
# 还需要和 区间之前的那一部分 也链接起来 if pre: # pre不为初始值none,说明不是从头结点开始反转的 pre.next = front return head else: # pre 为none,说明是从头结点开始反转的,需要更新头结点 return front
defreverse_linklist(node, k): # 因为该题只在有限区间内进行反转,所以需要用k来控制步长 # 注意反转完成后,node变成这部分的尾部结点 pre, cur = None, node i = 1 while cur and i <=k: res = cur.next cur.next = pre pre = cur cur = res i += 1 return pre
# 如果只有1个结点或者k=1,就不用做反转 if head.nextisNoneor k == 1: return head
pre, cur = dummy, head while cur: # 如果当前结点值和前一个结点值相等,就删除当前结点 if pre.val == cur.val: pre.next = cur.next else: pre = cur cur = cur.next return dummy.next
思路 2 :思路 1 的 code 的想法是,每遇到一个和 pre 重复的结点,就将它删除,这样有点费时间,比如若中间有很多个重复出现的值,就要一个一个的进行删除操作。 如果直接找到重复数字区域的最末尾,从末尾处断开,这样每个区域只用执行一次断开和链接操作,理论上来说会更快一点。
dummy = ListNode(-200, head) pre, cur = dummy, head
while cur and cur.next : # 如果出现了重复,就进入下面的循环 if cur.val == cur.next.val: while cur.nextand cur.val == cur.next.val: cur = cur.next # 直接将中间这段重复数值的区域全部删除 pre.next = cur pre = cur cur = cur.next
classSolution: defdeleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: '''1. 哈希表存储''' hash_s = {} cur = head while cur: if cur.val in hash_s: hash_s[cur.val] +=1 else: hash_s[cur.val] = 0 cur = cur.next
dummy = ListNode(-200, head) pre, cur = dummy, head while cur: # 仍然是遇到满足删除条件,就删除该结点 if hash_s[cur.val] > 0: pre.next = cur.next else: pre = cur cur = cur.next return dummy.next
slow, fast = head, head.next while fast and fast.next: slow = slow.next fast = fast.next.next return slow
根据不同的需求,选用不同的fast指针起始点。
classSolution: defmiddleNode(self, head: Optional[ListNode]) -> Optional[ListNode]: '''由于此题要找的是 中间or偏后的结点,所以选用上述的方案 一 ''' slow, fast = head, head while fast and fast.next: slow = slow.next fast = fast.next.next return slow
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next classSolution: defisPalindrome(self, head: Optional[ListNode]) -> bool: slow, fast = head, head.next while fast and fast.next: slow = slow.next fast= fast.next.next # 循环结束后,slow指向中间结点,或者中间两个中靠左侧的结点 # 将右侧的链表进行逆序 pre, cur = None, slow.next while cur: rest = cur.next cur.next = pre pre = cur cur = rest # 左右两部分进行对比 left, right = head, pre while right: if right.val != left.val: returnFalse right = right.next left = left.next returnTrue ''' 每个循环都只遍历了 一半的链表,所以时间复杂度是O(3N/2)->O(N) 只使用了有限个辅助变量,空间复杂度是O(1) '''
思路:观察示例可以发现,旋转后的链表,其实新的头结点,就是原来的倒数第 k 个结点。所以本质思路和上面两道题是一样的,只不过这里要注意,k 可能会大于链表长度,所以要先遍历一道链表,确定其长度,然后再对k求模。
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next classSolution: defrotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: ifnot head or k == 0: return head # 1.计算链表长度 cnt, cur = 1, head while cur.next: cnt+=1 cur= cur.next # cur 此时指向最后一个结点
# 2.求模 N = k % cnt if N == 0: return head # 3.原链表首尾相连成环 cur.next = head # 向右移动 k 个位置,其实等效于成环之后,原倒数第k个结点做新的头结点 # 所以问题转化为 定位倒数第 k 个结点 fast, slow, pre = head, head, cur i = 1 while i <= cnt: if i > N: pre = pre.next slow = slow.next fast = fast.next i += 1 # 此时slow指向倒数第k个结点,可以作为新的头结点,在此处断开环 pre.next = None return slow
defreverse_linklist(head): pre, cur = None, head while cur: rest = cur.next cur.next = pre pre = cur cur = rest return pre
cur1, cur2 = reverse_linklist(l1), reverse_linklist(l2) # pre = dummy '''可以选择上一题一样的做法,先逆序构造链表,然后在调用逆序函数处理一次; 但是这样时间消耗的有点多; 所以对code做了一点改动,生成链表的时候,直接逆序着生成''' pre = None while add>0or cur1 or cur2: if cur1: a = cur1.val cur1 = cur1.next else: a = 0 if cur2: b = cur2.val cur2 = cur2.next else: b = 0
digit = (a+b+add) % 10 add = (a+b+add-digit) // 10 # pre.next = ListNode(digit) cur = ListNode(digit) cur.next = pre pre = cur
while cur1 and cur2: if cur1.val < cur2.val: pre.next = cur1 pre = pre.next cur1 = cur1.next else: pre.next = cur2 pre = pre.next cur2 = cur2.next if cur1: pre.next = cur1 if cur2: pre.next = cur2 return dummy.next
# 思路2: 既然是两两合并,很明显可以用二分法的思想,分而治之,用递归进行,耗时明显下降 defmerge_k_linklist(lists: List[Optional[ListNode]], Left:int, Right:int) -> Optional[ListNode]: if Left == Right: return lists[Left] M = (Right-Left)//2 + Left
L_node = merge_k_linklist(lists, Left, M) R_node = merge_k_linklist(lists, M+1, Right)
classSolution: defoddEvenList(self, head: Optional[ListNode]) -> Optional[ListNode]: ifnot head ornot head.nextornot head.next.next: # 如果结点数是0、1、2 直接返回head return head # odd, even = head, head.next # 创建两个哑结点,分别作为 奇偶链表的头结点 dummy_odd = ListNode(0,head) dummy_even = ListNode(0,head.next) pre_odd, pre_even, cur = dummy_odd, dummy_even, head i = 1 while cur: # 遍历的时候判断当前是 奇数 还是 偶数 即可 if i % 2 == 1: pre_odd.next = cur pre_odd = cur else: pre_even.next = cur pre_even = cur cur = cur.next i += 1
# Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None
classSolution: defhasCycle(self, head: Optional[ListNode]) -> bool: if head isNoneor head.nextisNone: returnFalse slow, fast = head, head while fast and fast.next: slow = slow.next fast = fast.next.next if fast == slow: returnTrue
因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 2: x + y + n (z + y) = (x + y) 2 , n>=1 两边消掉一个(x+y)得: x + y = n (z + y) 求 x 的表达式的:x = n (z + y) - y = (n-1)(z + y) + z x = (n-1)环 + z 这个等式意味着: 如果另一个指针cur2从头结点开始走,走过x步;必然和从相遇点出发的cur1指针,在环的入口处相遇! 我们不用管cur1在环内究竟转了几圈,只要关注cur2几时能遇到cur1即可。
# Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None
classSolution: defdetectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: if head isNoneor head.nextisNone: returnNone slow, fast = head, head while fast and fast.next: slow = slow.next fast = fast.next.next if fast == slow: # 说明有环 cur1, cur2 = head, slow while cur2 != cur1: cur1 = cur1.next cur2 = cur2.next return cur2 returnNone
# Definition for singly-linked list. classListNode: def__init__(self, x): self.val = x self.next = None
classSolution: '''1.判断是否有环 & 求入环结点的函数''' defdetectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: if head isNoneor head.nextisNone: returnNone slow, fast = head, head while fast and fast.next: slow = slow.next fast = fast.next.next if fast == slow: # 说明有环 cur1, cur2 = head, slow while cur2 != cur1: cur1 = cur1.next cur2 = cur2.next return cur2 returnNone
'''2.求无环链表相交结点的函数''' defgetIntersectionNode_No_Circle(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: # 这里采用的是哈希表方法,代码写起来简洁一点 # 也可以使用另一种方法 curA = headA curB = headB hast_a = dict() # 遍历链表a,将所有结点装入哈希表 while curA isnotNone: hast_a.update({curA:1}) curA=curA.next # 遍历链表b,每次都看看b的结点是否在哈希表中已经存在 while curB isnotNone: if curB in hast_a: return curB curB=curB.next