Leetcode题解-链表

Posted by MaggicQ on November 1, 2017

本文目录

定义

链表(Linked List)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。

由于不必须按顺序存储,链表在插入的时候可以达到 $O(1)$ 的复杂度,比另一种线性表 —— 顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要 $O(n)$ 的时间,而顺序表相应的时间复杂度分别是 $O(log n)$ 和 $O(1)$。

使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

链表有很多种不同的类型:单向链表,双向链表以及循环链表。

其中单链表的定义如下:

class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None

常见题目

合并两个有序链表

题目描述

递归方法:

class Solution:
    def mergeTwoLists(self, l1, l2):
        if l1 is None:
            return l2
        elif l2 is None:
            return l1
        elif l1.val < l2.val:
            l1.next = self.mergeTwoLists(l1.next, l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l1, l2.next)
            return l2

时间复杂度与空间复杂度均为$O(n+m)$。

迭代方法:

class Solution:
    def mergeTwoLists(self, l1, l2):
        # maintain an unchanging reference to node ahead of the return node.
        prehead = ListNode(-1)
        prev = prehead
        while l1 and l2:
            if l1.val <= l2.val:
                prev.next = l1
                l1 = l1.next
            else:
                prev.next = l2
                l2 = l2.next            
            prev = prev.next

        # exactly one of l1 and l2 can be non-null at this point, so connect
        # the non-null list to the end of the merged list.
        prev.next = l1 if l1 is not None else l2
        return prehead.next

时间复杂度为$O(n+m)$,空间复杂度为$O(1)$。

反转链表

题目描述

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        # 迭代
        # if not head:
        #     return None

        # prev, cur = None, head
        # while cur:
        #     next = cur.next
        #     cur.next = prev
        #     prev, cur = cur, next
        # return prev

        # 递归
        if head is None or head.next is None:
            return head
        node = self.reverseList(head.next)
        head.next.next = head
        head.next = None
        return node

如果是反转从位置 mn 的链表:

class Solution:
    def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
        if not head or not head.next or m == n:
            return head

        dummy = ListNode(0)
        dummy.next = head
        prev, cur = dummy, head
        count = 1
        while count <= n:
            tmp = [cur, cur.next]
            if count == m:
                prev_start, start = prev, cur
            elif count > m and count < n:
                cur.next = prev
            elif count == n:
                start.next = cur.next
                prev_start.next = cur
                cur.next = prev
            prev, cur = tmp
            count += 1
        return dummy.next

pre_start指向第m-1节点,start指向第m个节点。

两数相加

题目描述

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy = ListNode(0)
        q = dummy
        carry = 0
        while l1 is not None or l2 is not None:
            val1 = l1.val if l1 is not None else 0
            val2 = l2.val if l2 is not None else 0
            carry, digit = divmod(val1+val2+carry, 10)
            q.next = ListNode(digit)
            q = q.next
            l1 = l1.next if l1 is not None else None
            l2 = l2.next if l2 is not None else None

        if carry > 0:
            q.next = ListNode(carry)
        return dummy.next

链表排序

题目描述:在 $O(nlogn)$的时间间复杂度和常数级空间复杂度下,对链表进行排序。

class Solution(object):
    def merge(self, h1, h2):
        dummy = tail = ListNode(None)
        while h1 and h2:
            if h1.val < h2.val:
                tail.next, tail, h1 = h1, h1, h1.next
            else:
                tail.next, tail, h2 = h2, h2, h2.next
    
        tail.next = h1 or h2
        return dummy.next
    
    def sortList(self, head):
        if not head or not head.next:
            return head
    	
        # slow实际上是链表的中点,这部分代码将链表分割为两部分再进行归并排序
        pre, slow, fast = None, head, head
        while fast and fast.next:
            pre, slow, fast = slow, slow.next, fast.next.next
        pre.next = None

        return self.merge(*map(self.sortList, (head, slow)))

由于使用了递归,这里的空间复杂度实际上是$O(nlogn)$。

$O(1)$空间复杂度的解法比较复杂,可以参考:Bottom-to-up(not-recurring)-with-o(1)-space-complextity-and-o(nlgn)-time-complextity

相交链表

编写一个程序,找到两个单链表相交的起始节点。

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        # 哈希表存储访问过得节点
#         visited = set()
#         while headA:
#             visited.add(headA)
#             headA = headA.next
        
#         while headB:
#             if headB in visited:
#                 return headB
#             headB = headB.next
#         return None
        
    	# 第二种方法:使用两个指针,第一个指针从HeadA开始,
        # 若遍历到终点,则从B的头部继续开始
   		# 同理,第二个指针从headB开始,遍历到终点时,从headA
        # 继续遍历,容易知道,这两个指针会在交点相遇。
        # 如果没有交点,则它们总有一个时刻都等于None
        if not headA or not headB:
            return None
        
        A, B = headA, headB
        while A!=B:
            A = A.next if A else headB   
            B = B.next if B else headA
        return A

环形链表

给定一个链表,判断链表中是否有环,返回True或者False。

class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        # 使用哈希表,记录是否有重复出现的节点
#         visited = set()
#         while head:
#             if head not in visited:
#                 visited.add(head)
#             else:
#                 return True
#             head = head.next
#         return False
        
        # 通过使用具有 不同速度 的快、慢两个指针遍历链表,可以降低空间复杂度
        # 如果有环,那么slow与fast两个指针会在环上的某个点相遇(类似两个人在跑圈)
        if not head or not head.next:
            return False
        slow, fast = head, head.next
        while slow != fast:
            if not fast.next or not fast.next.next:
                return False
            slow = slow.next
            fast = fast.next.next
        return True

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        # O(n)空间复杂度
        # visited = set()
        # ind = 0
        # while head:
        #     if head not in visited:
        #         visited.add(head)
        #     else:
        #         return head
        #     head = head.next
        # return None
    
        
        # 使用双指针 O(1)空间复杂度
        if not head or not head.next:
            return None
        
        slow = head
        fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                break
        else:
            return None
        
        while head != slow:
            head = head.next
            slow = slow.next
        return slow

移除链表元素

删除链表中等于给定值 val 的所有节点。

class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        dummy_head = ListNode(0)
        dummy_head.next = head
        q = dummy_head
        while q.next is not None:
            if q.next.val == val:
                q.next = q.next.next
            else:
                q = q.next
        return dummy_head.next

合并K个排序链表

合并K个排序链表

利用优先队列

from queue import PriorityQueue

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        dummy = ListNode(0)
        cur = dummy
        q = PriorityQueue()

        for i, node in enumerate(lists):
            if node:
                q.put((node.val, i, node))
        while q.qsize() > 0:
            item = q.get()
            cur.next, ind = item[2], item[1]
            cur = cur.next
            if cur.next:
                q.put((cur.next.val, ind, cur.next))
        return dummy.next

逐一两两合并链表

class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        amount = len(lists)
        interval = 1
        while interval < amount:
            for i in range(0, amount - interval, interval * 2):
                lists[i] = self.merge2Lists(lists[i], lists[i + interval])
            interval *= 2
        return lists[0] if amount > 0 else None

    def merge2Lists(self, l1, l2):
        head = point = ListNode(0)
        while l1 and l2:
            if l1.val <= l2.val:
                point.next = l1
                l1 = l1.next
            else:
                point.next = l2
                l2 = l2.next
            point = point.next
        point.next = l1 or l2
        return head.next

参考

链表-Leetcode