链表 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 = None

class 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.next
        
        return head
        
  • recursive

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
            rec = self.helper(head, n)
            if rec == n:
                return head.next
            return head
        
        def 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 = None
    
    class 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

  • 直接从前往后走,只要还在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 head
                
        def 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 % count
            
            while 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
            return
        
        def 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 None
            
            a = 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 = temp
        
    return 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 = new
            
            new_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.next
                
            head = 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 = None
    
            return 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一圈

推荐阅读更多精彩内容