链表--Leetcode(python)

本文详细介绍了链表的各种操作,包括排序链表、判断环形链表、查找链表相交节点、反转链表、两两交换节点、判断回文链表等。提供了相应的解题思路和代码实现,涵盖了链表的基本操作和常见的算法挑战。
摘要由CSDN通过智能技术生成

排序链表

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4
示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5

解法
首先是O(nlogn)的时间复杂度,所以想到快速排序和归并排序,但是快速排序最坏情况下是n^2的,所以用归并,而且对于链表来说使用归并排序占用空间为O(1)。
链表和数组最大的不同就是链表需要从前往后一个个找,而且要注意指针的问题,不要被覆盖,以及随时判断为none的情况。
链表获取中间值的方式比较有意思。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def sortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head == None or head.next == None:
            return head
        
        mid = self.getmid(head)
        l = head
        r = mid.next
        mid.next = None
        
        return self.merge(self.sortList(l), self.sortList(r))
    
    def merge(self, l, r):
        root = ListNode(0)
        tmp = root
        curl, curr = l, r
        while curl != None and curr != None:
            if curl.val < curr.val:
                tmp.next = curl
                curl = curl.next
            else:
                tmp.next = curr
                curr = curr.next
            tmp = tmp.next
            
        if curl != None:
            tmp.next = curl
        
        if curr != None:
            tmp.next = curr
        
        return root.next
    
    def getmid(self, node):
        if node == None:
            return node
        fast = slow = node
        while fast.next != None and fast.next.next != None:
            slow = slow.next
            fast = fast.next.next

        return slow
        
        

环形链表

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?

解法
判断单链表是否有环的经典问题。类似于求中值一样,设置两个指针,快指针每次步长是2,慢指针步长为1,如果有环,两个必定相遇。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        if head == None:
            return False
        fast = slow = head
        while fast.next and fast.next.next:
            slow = slow.next
            fast = fast.next.next
            
            if fast == slow:
                return True
        
        return False
        

相交链表

编写一个程序,找到两个单链表相交的起始节点。
在这里插入图片描述
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

注意:

如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存

解法
有两种方法:

  1. 把B链表接在A链表后面,如果有交点就会有环,所以可以直接用判断单链表是否有环的方法来做。
  2. 如果两个链表长度相同且存在交点的话,就可以同时一起向后遍历。那么就是先得到两个链表的长度,并让长链表先向后走lenA-lenB步。

因为要让链表保持原有结构,所以只能用第二种方法。且要先判断是否不存在交点的情况。

def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        if not headA or not headB:
            return None
        tempA = headA
        tempB = headB
        while tempA!=tempB:
            tempA = tempA.next
            tempB = tempB.next
            if not tempA and not tempB:
                return None
            if not tempA:     #A链表后面接上B链表
                tempA = headB
            if not tempB:     #B链表后面接上A链表
                tempB = headA
        return tempA
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        a, b = headA, headB
        l, m = 0, 0
        
        while a:
            l += 1
            a = a.next
        while b:
            m += 1
            b = b.next
            
        if a != b:
            return Null
        
        a, b = headA, headB
        if l > m:
            diff = l - m
            while diff > 0:
                a = a.next
                diff -= 1
        if l < m:
            diff = m - l
            while diff > 0:
                b = b.next
                diff -= 1
                
        while a != b:
            a = a.next
            b = b.next
            
        return a
                

反转链表

反转一个单链表。

示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

解法

  1. 迭代:循坏迭代算法需要三个临时变量:pre、head、next,只需先保存下cur.next,再让其指向第一个节点即可。临界条件是链表为None或者链表就只有一个节点。
  2. 递归:思想与迭代相似,关键是让原来指向None的节点,改为指向前一个节点,也就是head.next.next = head,并且截断前一个节点,防止出现环。

迭代

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        
        if not head or not head.next:
            return head
        
        pre = None
        while head:
            ne = head.next   # 缓存当前节点的向后指针,待下次迭代用
            head.next = pre  # 这一步是反转的关键,相当于把当前的向前指针作为当前节点的向后指针
            pre = head   # 作为下次迭代时的(当前节点的)向前指针
            head = ne    # 作为下次迭代时的(当前)节点
            
        return pre   # 返回头指针,头指针就是迭代到最后一次时的head变量(赋值给了pre)
        

迭代二:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        
        if not head or not head.next:
            return head
        
        cur = head
        pre = head
        while cur and cur.next:
            tmp = cur.next
            cur.next = cur.next.next
            tmp.next = pre
            pre = tmp
            
        return tmp

递归:

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if not head:
            return None
        if not head.next:
            return head
        headNode = self.reverseList(head.next)
        head.next.next = head
        head.next = None
        return headNode
        

两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.

解法
首先,我们交换列表中的前两个节点,也就是 head 和 head.next;
然后我们以 调用函数自身,以交换头两个节点之后列表的其余部分。
最后,我们子列表的返回头与之前的节点相连,以形成新的链表。
注意:要判断节点是否存在以及head.next是否存在

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def swapPairs(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        new_h = head.next
        cur = new_h.next
        new_h.next = head
        head.next = self.swapPairs(cur)
        
        return new_h

回文链表

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false
示例 2:

输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

解法
开始想到的方法是直接用翻转链表的方法把链表翻转后判断是否相同即可,其中要用到deepcopy,不然会改变原始链表的指针,但是发现空间占用太大(因为深拷贝?)最后一个用例没有通过。
优化方法是走到链表的中间部分,然后将后半部分翻转,判断是否相同即可。

第一种错误方法:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        if not head or not head.next: 
            return True
        
        pre = None
        cur = copy.deepcopy(head)
        while cur:
            ne = cur.next
            cur.next = pre
            pre = cur
            cur = ne
            
        cur = head
        while cur:
            if cur.val != pre.val:
                return False
            cur = cur.next
            pre = pre.next
            
        return True
        

改进:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        if not head or not head.next: 
            return True
        
        fast = slow = head
        while fast.next and fast.next.next:
            slow = slow.next
            fast = fast.next.next
        
        cur = slow.next
        pre = None
        while cur:
            ne = cur.next
            cur.next = pre
            pre = cur
            cur = ne
            
        cur = head
        while pre:
            if cur.val != pre.val:
                return False
            cur = cur.next
            pre = pre.next
            
        return True
        

删除链表中的节点

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。

现有一个链表 – head = [4,5,1,9],它可以表示为:
示例 1:

输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:

输入: head = [4,5,1,9], node = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

说明:

链表至少包含两个节点。
链表中所有节点的值都是唯一的。
给定的节点为非末尾节点并且一定是链表中的一个有效节点。
不要从你的函数中返回任何结果。

解法
仅给了需要被删除的节点,又不能得到node的前节点,所以只能通过将node.next的值赋值给node的方式删除。
因为已知不是最后一个,所以不需要判断None

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void Do not return anything, modify node in-place instead.
        """
        node.val = node.next.val
        node.next = node.next.next
            

奇偶链表

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

示例 1:

输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL
示例 2:

输入: 2->1->3->5->6->4->7->NULL
输出: 2->3->6->7->1->5->4->NULL
说明:

应当保持奇数节点和偶数节点的相对顺序。
链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。

解法
类似于删除单链表中重复的值的题目:设置两个指针,遇到重复的跳过指向下一个节点即可。
空间复杂度是O(1)表示不要新建链表或者数组,时间复杂度O(nodes)表示只循环走一遍链表,原地算法表示直接对head进行修改。
只需设置两个指针,odd和even,odd.next=even.next,even.next=odd.next,并提前设置一个t指针,用于在odd的尾指向even的头。
注意:while的判断条件应该是对even和even.next进行判断,而不是odd,因为odd的已经在前面判断过了。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def oddEvenList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        
        odd  = head
        even = head.next
        t = even
        
        while even and even.next:
            odd.next = even.next
            odd = odd.next
            even.next = odd.next
            even = even.next
            
        odd.next = t
        
        return head

复制带随机指针的链表

给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的 深拷贝。

我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。

在这里插入图片描述
提示:

-10000 <= Node.val <= 10000
Node.random 为空(null)或指向链表中的节点。
节点数目不超过 1000 。

解法
类似于图有邻居的题型,用字典存储下新老节点的对应关系。对于新建节点的时候,只能新建(val, None, None),next和random都后面用字典找到对应节点。

"""
# Definition for a Node.
class Node:
    def __init__(self, x, next=None, random=None):
        self.val = int(x)
        self.next = next
        self.random = random
"""
class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        if head == None:
            return None
        
        dic = {}
        cur = head
        while cur:
            dic[cur] = Node(cur.val, None, None)
            cur = cur.next
        
        for key, val in dic.items():
            if key.next:
                val.next = dic[key.next]
            if key.random:
                val.random = dic[key.random]
                
        return dic[head]
            
        

设计链表

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
linkedList.get(1); //返回2
linkedList.deleteAtIndex(1); //现在链表是1-> 3
linkedList.get(1); //返回3

提示:

所有val值都在 [1, 1000] 之内。
操作次数将在 [1, 1000] 之内。
请不要使用内置的 LinkedList 库。

解法
题意:定义链表的各种操作
注意:可以保存一个size值,头结点设置为伪节点,这样就不用判断头结点是否存在了。

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

class MyLinkedList:
    def __init__(self):
        self.size = 0
        self.head = ListNode(0)  # sentinel node as pseudo-head
        

    def get(self, index: int) -> int:
        """
        Get the value of the index-th node in the linked list. If the index is invalid, return -1.
        """
        # if index is invalid
        if index < 0 or index >= self.size:
            return -1
        
        curr = self.head
        # index steps needed 
        # to move from sentinel node to wanted index
        for _ in range(index + 1):
            curr = curr.next
        return curr.val
            

    def addAtHead(self, val: int) -> None:
        """
        Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
        """
        self.addAtIndex(0, val)
        

    def addAtTail(self, val: int) -> None:
        """
        Append a node of value val to the last element of the linked list.
        """
        self.addAtIndex(self.size, val)
        

    def addAtIndex(self, index: int, val: int) -> None:
        """
        Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
        """
        # If index is greater than the length, 
        # the node will not be inserted.
        if index > self.size:
            return
        
        # [so weird] If index is negative, 
        # the node will be inserted at the head of the list.
        if index < 0:
            index = 0
        
        self.size += 1
        # find predecessor of the node to be added
        pred = self.head
        for _ in range(index):
            pred = pred.next
            
        # node to be added
        to_add = ListNode(val)
        # insertion itself
        to_add.next = pred.next
        pred.next = to_add
        

    def deleteAtIndex(self, index: int) -> None:
        """
        Delete the index-th node in the linked list, if the index is valid.
        """
        # if the index is invalid, do nothing
        if index < 0 or index >= self.size:
            return
        
        self.size -= 1
        # find predecessor of the node to be deleted
        pred = self.head
        for _ in range(index):
            pred = pred.next
            
        # delete pred.next 
        pred.next = pred.next.next

环形链表 II

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

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。

进阶:
你是否可以不用额外空间解决此题?

解法

  1. 最直接的方法:从前往后,依次断开pre和cur当前的连接,如果从cur开始没有环了,说明环的起始位置是pre。但是这样会超时。
  2. 设置快慢指针,快指针是慢指针的几倍,就会转几圈与慢指针相遇。相遇点距环入口的距离,等于头结点距环入口的距离。

第一种方法,超时:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return None
        fast = slow = head
        flag = 0
        while fast.next and fast.next.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                flag = 1
                break
        if flag == 0:
            return None
        
        idx = 0
        while True:
            tmp = head
            for i in range(idx):
                tmp = tmp.next
            pre = tmp
            cur = tmp.next
            fast = slow = cur
            pre.next = None
            flag = 0
            while fast.next and fast.next.next:
                slow = slow.next
                fast = fast.next.next
                if slow == fast:
                    flag = 1
                    break
            pre.next = cur
            if flag == 0:
                return pre
            
            idx += 1
        

第二种方法:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return None
        fast = slow = head
        while fast.next and fast.next.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                pre = head
                while pre != slow:
                    pre = pre.next
                    slow = slow.next
                return pre
            
        return None
        

删除链表的倒数第N个节点

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:
给定的 n 保证是有效的。

进阶:
你能尝试使用一趟扫描实现吗?

解法

  • 可以观察到,删除的位置其实就是列表的长度-n-1。所以很容易想到用一个字典存储下每个位置的指针。注意对要移除头结点时的特殊处理即可
  • 一次遍历单链表即可。 首先,用两个指针p,q分别指向头结点,然后先将其中一个指针p向后移动N个位置,再将p,q同时往后移动,当指针p移动到末尾时,q指针指向的结点即为我们要删除的结点
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
        dic = {}
        cur = head
        idx = 0
        dic[idx] = cur
        while cur.next:
            cur = cur.next
            idx += 1
            dic[idx] = cur
            
        rm_idx = idx - n
        if rm_idx == -1:
            head = head.next
        else:
            pre = dic[rm_idx]
            pre.next = pre.next.next
        
        return head
        
#coding=utf-8
# 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:
        fast, low = head
        while fast and n > 0:
            fast = fast.next
            n -= 1
        if not fast:
            return head
        while fast.next:
            low = low.next
            fast = fast.next
            
        low.next = low.next.next
        
        return head

移除链表元素

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

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5

解法
题意:删除给定值的所有节点。所以主要是要考虑head的情况
比较简单的方法,新建一个头部伪节点,这样真正的head就可以当做正常节点一起处理了。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        first = ListNode(0)
        first.next = head
        cur = first
        while cur.next:
            if cur.next.val == val:
                cur.next = cur.next.next
            else:
                cur = cur.next
            
        return first.next
                

合并两个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

解法
可以用迭代或者递归的归并排序:

迭代法:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        if not l1 and not l2:
            return None
        if not l1 or not l2:
            return l1 if l1 else l2
        l = ListNode(0)
        cur = l
        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 l.next
                

递归法:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        if not l1 and not l2:
            return None
        if not l1 or not l2:
            return l1 if l1 else l2
        
        head = ListNode(0)
        cur = head
        if l1.val < l2.val:
            cur.next = l1
            l1 = l1.next
        else:
            cur.next = l2
            l2 = l2.next
        cur = cur.next
        cur.next = self.mergeTwoLists(l1, l2)
        
        return head.next

合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

解法

  • 优先级队列:最小堆(优先队列)维护k个链表的当前头位置的值。考虑优先队列中的元素不超过 k 个,那么插入和删除的时间代价为 O(logk),这里最多有 kn个点,对于每个点都被插入删除各一次,故总的时间代价即渐进时间复杂度为 O(kn×logk)。空间复杂度:这里用了优先队列,优先队列中的元素不超过 k 个,故渐进空间复杂度为 O(k)。
  • 链表两两合并,递归「向上回升」的过程,渐进时间复杂度为 O(kn×logk)。递归会使用到O(logk) 空间代价的栈空间。

方法一:

# Definition for singly-linked list.
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        if not lists or len(lists) == 0:
            return None
        import heapq
        heap = []
        # 首先 for 嵌套 while 就是将所有元素都取出放入堆中
        for node in lists:
            while node:
                heapq.heappush(heap, node.val)
                node = node.next
        dummy = ListNode(None)
        cur = dummy
        # 依次将堆中的元素取出(因为是小顶堆,所以每次出来的都是目前堆中值最小的元素),然后重新构建一个列表返回
        while heap:
            temp_node = ListNode(heappop(heap))
            cur.next = temp_node
            cur = temp_node
        return dummy.next


方法二:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        if not lists:return 
        n = len(lists)
        return self.merge(lists, 0, n-1)
    def merge(self,lists, left, right):
        if left == right:
            return lists[left]
        mid = left + (right - left) // 2
        l1 = self.merge(lists, left, mid)
        l2 = self.merge(lists, mid+1, right)
        return self.mergeTwoLists(l1, l2)
    def mergeTwoLists(self,l1, l2):
        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

两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

解法
类似于归并操作的复杂版

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        l = ListNode(0)
        cur = l
        flag = 0
        
        while l1 and l2:
            tmp = l1.val + l2.val + flag
            if tmp < 10:
                tmp2 = tmp  
                flag = 0
            else:
                tmp2 = tmp - 10
                flag = 1
            x = ListNode(tmp2)
            cur.next = x
            l1 = l1.next
            l2 = l2.next
            cur = cur.next
            
        rest = l1 if l1 else l2
        while rest:
            tmp = rest.val + flag
            if tmp < 10:
                tmp2 = tmp  
                flag = 0
            else:
                tmp2 = tmp - 10
                flag = 1
            x = ListNode(tmp2)
            cur.next = x
            rest = rest.next
            cur = cur.next
            
        if flag == 1:
            x = ListNode(1)
            cur.next = x
            
        return l.next
            

扁平化多级双向链表

您将获得一个双向链表,除了下一个和前一个指针之外,它还有一个子指针,可能指向单独的双向链表。这些子列表可能有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。

扁平化列表,使所有结点出现在单级双链表中。您将获得列表第一级的头部。

示例:

输入:
 1---2---3---4---5---6--NULL
         |
         7---8---9---10--NULL
             |
             11--12--NULL

输出:
1-2-3-7-8-11-12-9-10-4-5-6-NULL

解法
题意:不断的将子链表插入回去
方法:使用递归,找到没有child的子链表,返回头尾节点,并插入回去
注意:双链表在插入时注意判断cur.next是否存在;递归返回的尾结点要用一个指针保存下来。

"""
# Definition for a Node.
class Node(object):
    def __init__(self, val, prev, next, child):
        self.val = val
        self.prev = prev
        self.next = next
        self.child = child
"""
class Solution(object):
    def flatten(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        h, t = self.dfs(head)
        
        return h
    
    def dfs(self, head):
        h = cur = pre = head
        while cur:
            if cur.child:
                c_h, c_t = self.dfs(cur.child)
                cur.child = None
                c_h.prev = cur
                c_t.next = cur.next
                if cur.next:
                    cur.next.prev = c_t
                cur.next = c_h
            pre = cur
            cur = cur.next
            
        return h, pre
        

旋转链表

给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:

输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL

解法
题意:将链表向右旋转K步的结果
步骤:

  • 首先给定的k要对长度求余才是真正要移动的步数;
  • 如何旋转:找到length - k - 1的位置pre,让pre.next = None,让尾结点连上头结点即可
  • 注意:当k%length == 0 的时候要特殊处理,不然最后的返回会越界。
  • 用dic存储下来每个位置的指针,比较方便寻找
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def rotateRight(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        if not head or not head.next or k == 0:
            return head
        dic = {}
        cur = head
        idx = 0
        while cur:
            dic[idx] = cur
            cur = cur.next
            idx += 1
        if k % idx == 0:
            return head
        point = idx - (k % idx) - 1
        pre = dic[point]
        pre.next = None
        dic[idx - 1].next = head
        
        return dic[point + 1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值