链表|链表 LC.19/83/92/61/143/21/160/141/147/138/142

LC.19 Remove Nth Node Form End of List
Description Given a linked list, remove the n-th node from the end of list and return its head.
Example:

Given linked list: 1->2->3->4->5, and n = 2.After removing the second node from the end, the linked list becomes 1->2->3->5.

Note:
Given n will always be valid.
Follow up:
Could you do this in one pass?
Solution
  • one-pass (trivial) 实际上有两个指针,所以是2-pass
# Definition for singly-linked list. # class ListNode: #def __init__(self, x): #self.val = x #self.next = Noneclass Solution: def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode: count = 1 target = head temp = head while count < n: temp = temp.next count += 1 if temp.next == None: return target.next else: temp = temp.next while temp.next != None: target = target.next temp = temp.next target_d = target.next target.next = target_d.nextreturn head

  • recursive
    # Definition for singly-linked list. # class ListNode: #def __init__(self, x): #self.val = x #self.next = Noneclass Solution: def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode: rec = self.helper(head, n) if rec == n: return head.next return headdef helper(self, head: ListNode, n: int) -> int: if head.next == None: return 1 rec = self.helper(head.next, n) if rec == n: head.next = head.next.next return rec + 1

    递归做法,实际上这道题本质上必须遍历整个链表直到最后一个node,所以用递归的方法查看之后还剩多少个元素,如果正好是n的话,那么当前的结点(倒数第n+1个)的下一个结点(倒数第n个)就需要被删除。和另外一个方法比起来并不会增大memory的占用。runtime还更低(我猜是因为少了一个移动的指针)
LC.83 Remove Duplicates from Sorted List
Description Given a sorted linked list, delete all duplicates such that each element appear only once.
Example 1:
Input: 1->1->2 Output: 1->2

Example 2:
Input: 1->1->2->3->3 Output: 1->2->3

Solution
  • straight solution (两个指针)
  • # Definition for singly-linked list. # class ListNode: #def __init__(self, x): #self.val = x #self.next = Noneclass Solution: def deleteDuplicates(self, head: ListNode) -> ListNode: node_a = head node_b = head if head == None: return head while node_b.next != None: if node_b.val != node_b.next.val: node_a.next = node_b.next node_a = node_a.next node_b = node_b.next if not node_a == node_b: node_a.next = node_b.next return head

    或者一个指针:
    class Solution: def deleteDuplicates(self, head: ListNode) -> ListNode: temp = head while temp != None and temp.next != None: if temp.val == temp.next.val: temp.next = temp.next.next else: temp = temp.next return head

  • 递归做法
    class Solution: def deleteDuplicates(self, head: ListNode) -> ListNode: if head == None or head.next == None: return head if head.val == head.next.val: return self.deleteDuplicates(head.next) else: head.next = self.deleteDuplicates(head.next) return head

LC. 92 Reversed Linked List
Reverse a linked list from position m to n. Do it in one-pass.
Note: 1 ≤ mn ≤ length of list.
Example:
Input: 1->2->3->4->5->NULL, m = 2, n = 4 Output: 1->4->3->2->5->NULL

Solution
  • 【链表|链表 LC.19/83/92/61/143/21/160/141/147/138/142】直接从前往后走,只要还在reverse的范围内,看见一个拎出来一个,添加到旁边的翻转新链表的表头。所有需要翻转的都翻转完之后,把新的链表插在原来的链表里面
    class Solution(object): def reverseBetween(self, head, m, n): """ :type head: ListNode :type m: int :type n: int :rtype: ListNode """ holder = ListNode(-1) holder.next = head head = holder count = 1 while count < m: head = head.next count += 1 new = None while count < n+1: temp = head.next.next head.next.next = new new = head.next if count == m: tail = new head.next = temp count += 1 temp = head.next head.next = new tail.next = temp return holder.next

    在表的最前面加一个holder可以方便计数,并且corner case少了很多!!第17-20行是翻转表头的操作
  • 递归做法
    class Solution(object): def reverseBetween(self, head, m, n): """ :type head: ListNode :type m: int :type n: int :rtype: ListNode """ self.successor = None if m == 1: return self.reverseHelper(head, n-m+1) head.next = self.reverseBetween(head.next, m-1, n-1) return headdef reverseHelper(self, head, m): if m == 1: self.successor = head.next return head new = self.reverseHelper(head.next, m-1) head.next.next = head head.next = self.successor return new

    是LC.206的变形,递归的时候加入一个变量k,reverse当前链表的前k个元素。递归现在还写得乱七八糟的!!
LC. 61 Rotate List
Given a linked list, rotate the list to the right by k places, where k is non-negative.
Example 1:
Input: 1->2->3->4->5->NULL, k = 2 Output: 4->5->1->2->3->NULL Explanation: rotate 1 steps to the right: 5->1->2->3->4->NULL rotate 2 steps to the right: 4->5->1->2->3->NULL

Example 2:
Input: 0->1->2->NULL, k = 4 Output: 2->0->1->NULL Explanation: rotate 1 steps to the right: 2->0->1->NULL rotate 2 steps to the right: 1->2->0->NULL rotate 3 steps to the right: 0->1->2->NULL rotate 4 steps to the right: 2->0->1->NULL

Solution
  • 直接解:
    先数一下一共有多少个元素,再用总数减去模k的值求left rotate多少个。找到新的头元素,把tail和原来的头元素串起来就好了
    class Solution(object): def rotateRight(self, head, k): """ :type head: ListNode :type k: int :rtype: ListNode """ if not head: return head count = 1 key = head while key.next: count += 1 key = key.next key.next = head k = count - k % countwhile k > 1: head = head.next k -= 1 new = head.next head.next = None return new

  • 还可以用间隔k的两个指针,一旦走到头就重新回到head的方法,不需要显式地求list的长度。但这样复杂度显然变高了,模掉的那堆都得计算一遍
LC. 143 Reorder List
Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→LnL1→Ln-1→L2→Ln-2→…
You may not modify the values in the list's nodes, only nodes itself may be changed.
Example 1:
Given 1->2->3->4, reorder it to 1->4->2->3.

Example 2:
Given 1->2->3->4->5, reorder it to 1->5->2->4->3.

Solutions
  • 这题还蛮容易错的,主要是reverse list的时候不能反转原链表,这样正序的链表就没了;首先找到链表中点,然后反转后一半的链表,前一半的链表a和后一半的反转链表b交替插在一块儿就好了
    class Solution(object): def reorderList(self, head): """ :type head: ListNode :rtype: None Do not return anything, modify head in-place instead. """ if not head or not head.next: return a = b = head move_a = False while b.next: b = b.next if move_a == True: a = a.next move_a = False else: move_a = True list_b = self.reverseList(a.next) a.next = None list_a = head while list_b: a_temp = list_a.next b_temp = list_b.next list_a.next = list_b list_b.next = a_temp list_a = a_temp list_b = b_temp returndef reverseList(self, head): if not head.next: return head new = self.reverseList(head.next) head.next.next = head head.next = None return new

    优化:
    第7-17行可以优化成如下,复杂度降了好多,我也不确定为什么,大概是更改变量的次数变少了。。
    while b.next.next: b = b.next.next a = a.next if not b.next: break

  • 感觉这道没必要用递归,因为每次都得找最尾端的元素,性价比很低 (?
LC. 21. Merge Two Sorted Lists
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
Example:
Input: 1->2->4, 1->3->4 Output: 1->1->2->3->4->4、

Solutions
  • 直接解(超级丑)
  • class Solution(object): def mergeTwoLists(self, l1, l2): """ :type l1: ListNode :type l2: ListNode :rtype: ListNode """ if not l1: return l2 if not l2: return l1 if l1.val <= l2.val: head = l1 l1 = l1.next else: head = l2 l2 = l2.next key = head while l1 and l2: if l1.val <=l2.val: key.next = l1 key = key.next l1 = l1.next else: key.next = l2 key = key.next l2 = l2.next if l1 or l2: key.next = l1 if l1 else l2 return head

  • 递归(似乎叫动态规划了这里)
    class Solution(object): def mergeTwoLists(self, l1, l2): """ :type l1: ListNode :type l2: ListNode :rtype: ListNode """ if not l1: return l2 if not l2: return l1 if l1.val <= l2.val: l1.next = self.mergeTwoLists(l1.next, l2) return l1 else: l2.next = self.mergeTwoLists(l1, l2.next) return l2

LC. 160. Intersection of Two Linked Lists
Write a program to find the node at which the intersection of two singly linked lists begins.
Notes:
If the two linked lists have no intersection at all, return null.
The linked lists must retain their original structure after the function returns.
You may assume there are no cycles anywhere in the entire linked structure.
Your code should preferably run in O(n) time and use only O(1) memory.
Solutions
  • 一个超简洁的解法:
    class Solution(object): def getIntersectionNode(self, headA, headB): """ :type head1, head1: ListNode :rtype: ListNode """ if not headA or not headB: return Nonea = headA b = headB while a != b: a = headB if a == None else a.next b = headA if b == None else b.next return a

    • 思路是a+b = b+a,如果遍历两遍,他们会同时到达终点,自动对齐
    • 第12行很重要,这和两个链表intersect的方式有关,如果只是一个node的val相同,这个语句是false的。必须后面是同一个链才是true
    • 第12行到14行的另外一种错误写法:
      if a.next == None: a.next = headB if b.next == None: b.next = headA a = a.next b = b.next

      原因是第2/4行会导致链表的终端不指向None,会形成一个环形链表,这样后到达的指针就出不来了!
LC. 141. Linked List Cycle
Given a linked list, determine if it has a cycle in it.
To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to. If pos is -1, then there is no cycle in the linked list.
Example 1:
Input: head = [3,2,0,-4], pos = 1
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the second node.
Solutions
  • 一个作弊解法:(which 我觉得肯定有毛病)
    class Solution(object): def hasCycle(self, head): """ :type head: ListNode :rtype: bool """ marker = ListNode(-1) if not head: return False while head.next and not head.next == marker: temp = head.next head.next = marker head = temp if not head.next: return False return True

    走过的点就标记为走过,通过把它指向一个marker点。但这样链表就被损坏了
  • 龟兔赛跑的解法:一个指针以步长1移动,另一个以步长2移动。如果有环的话,fast指针一定会追上另外一个
    class Solution(object): def hasCycle(self, head): """ :type head: ListNode :rtype: bool """ try: slow = head fast = head.next while not slow == fast: slow = slow.next fast = fast.next.next return True except: return False

    这里用try/except的写法写会更快,不必每次显式地检查是否有指针到达终点
class Solution(object):
def insertionSortList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head:
return head
helper = ListNode(0)
pre = helper
while head: temp = head.next if pre.next and head.val < pre.next.val: pre = helper while pre.next and head.val >= pre.next.val: pre = pre.next head.next = pre.next pre.next = head head = tempreturn helper.next

LC. 138 Copy List with Random Pointer
A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
Return a deep copy of the list.
Solution
  • 交替索引
    class Solution(object): def copyRandomList(self, head): """ :type head: Node :rtype: Node """ if not head: return None helper = Node(0, head, None) while head.next: new = Node(head.val, head.next, head.random) head.next = new head = head.next.next new = Node(head.val, None, head.random) head.next = newnew_head = helper.next.next while new_head.next: new_head.random = None if not new_head.random else new_head.random.next new_head = new_head.next.next new_head.random = None if not new_head.random else new_head.random.nexthead = helper.next helper.random = head.next new = Node(0, None, None) while head.next.next: new.next = head.next head.next = head.next.next new = new.next head = head.next new.next = head.next head.next = Nonereturn helper.random

  • keep an hash table for each node in the list: 原链表的每一个node索引到它的copy上,然后再次从头到位遍历原来的链表,写入新结点们的next和random
    class Solution(object): def copyRandomList(self, head): """ :type head: Node :rtype: Node """ if not head: return None copy = dict() m = n = head while m: copy[m] = Node(m.val, m.next, m.random) m = m.next while n: copy[n].next = copy.get(n.next) copy[n].random = copy.get(n.random) n = n.next return copy[head]

    需要注意的是,在第15/16行都用的是dict.get(key)而不是直接dict[key], 这是为了当key等于None的时候,返回default value None而不是报错。如果事先定义copy[None]=None的话,这两处也可以直接用dict[key]
    这种方法还有一个很trick的只遍历一次的写法,使用collections.defaultdict来访问(及创建)存在(或不存在)的结点
    class Solution(object): def copyRandomList(self, head): """ :type head: Node :rtype: Node """ copy = collections.defaultdict(lambda: Node(0, None, None)) m = head copy[None] = None while m: copy[m].val = m.val copy[m].next = copy[m.next] copy[m].random = copy[m.random] m = m.next return copy(head)

    注意这里的12/13行是直接调用的方式,且在第9行定义了key=None的情况。这是因为现在的default是RandomNode(0)而不是None了
LC. 142 Linked List Cycle II
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to. If pos is -1, then there is no cycle in the linked list.
Solution
  • hash table that records the visited nodes
    class Solution(object): def detectCycle(self, head): """ :type head: ListNode :rtype: ListNode """ info = dict() m = head while m: if not info.get(m): info[m] = True else: return m m = m.next return None

  • hare-tortoise method
    class Solution(object): def detectCycle(self, head): """ :type head: ListNode :rtype: ListNode """ fast = slow = helper = head try: fast = fast.next.next slow = slow.next while not fast == slow: fast = fast.next.next slow = slow.next while not helper == slow: helper = helper.next slow = slow.next return helper except: return None

    这里最重要的就是fast步长=2,slow=1。。。这样保证了fast恰好超slow一圈

    推荐阅读