[Leetcode]数据结构之链表——python版本

本篇文章根据labuladong的算法小抄汇总链表的常见算法,采用python3实现

单链表基本技巧

合并两个有序链表

class ListNode:
    def __init__(self,val=0,next=None):
        self.val = val
        self.next = next
def mergeTwoLists(l1,l2):
    #虚拟头结点
    dummy = ListNode(-1)
    p = dummy
    p1 = l1
    p2 = l2
    while (p1 is not None) and (p2 is not None):
        if p1.val > p2.val:
            p.next = p2
            p2 = p2.next
        else:
            p.next = p1
            p1 = p1.next
        p = p.next
    if p1 is not None:
        p.next = p1
    if p2 is not None:
        p.next = p2
    return dummy.next

合并k个有序链表

class ListNode:
    def __init__(self,val=0,next=None):
        self.val = val
        self.next = next
        
import heapq
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        n = len(lists)
        if n == 0:
            return None
        dummy = ListNode(-1)
        p = dummy
        pq = []
        #遍历所有结点,每个值都插入最小堆
        for head in lists:
            while head is not None:
                heapq.heappush(pq,head.val)
                head = head.next
        #二叉堆中元素构建新链表
        while len(pq) != 0:
            p.next = ListNode(heapq.heappop(pq))
            p = p.next
        return dummy.next

单链表的倒数第k个结点

def findFromEnd(head,k):
    p1 = head
    for i in range(k):
        p1 = p1.next
    p2 = head
    while p1 is not None:
        p2 = p2.next
        p1 = p1.next
    return p2

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

双指针技巧:p1先走n步,p2指向头结点,那么p1走完时,p2刚好走到倒数第n个结点

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        #注意如果不添加虚拟头结点,要删除原头结点就很麻烦
        dummy = ListNode(-1)
        dummy.next = head
        p1 = dummy
        for i in range(n):
            p1 = p1.next
        p2 = dummy
        while p1.next:
            p1 = p1.next
            p2 = p2.next
        p2.next = p2.next.next
        return dummy.next

单链表的中点

快慢指针:每当慢指针slow前进一步,快指针fast就前进两步,那么当fast走到末尾时,slow就指向中点

def middleNode(head):
    slow = head
    fast = head
    while fast and fast.next:
        fast = fast.next.next
        slow = slow.next
    return slow

判断链表是否包含环

快慢指针:每当慢指针slow前进一步,快指针fast就前进两步,那么如果fast最终遇到空指针,说明没有环;如果fast最终和slow相遇,那肯定是fast超过slow一圈,说明有环

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        fast = head
        slow = head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                return True
        return False

返回链表入环的第一个结点

快慢指针:若链表含有环,当快慢指针相遇时,将一个指向头指针,以同样速度遍历,会在环起点相遇

def detectCycle(head):
    slow = head
    fast = head
    while fast and fast.next:
        fast = fast.next.next
        slow = slow.next
        if slow == fast:
            break
    if (not fast) or (not fast.next):
        return None
    slow = head
    while slow != head:
        slow = slow.head
        fast = fast.head
    return slow

两个链表是否相交

img

让p1走掉a1,a2,c1,c2,b1,b2,b3→c1

让p2走掉b1,b2,b3,c1,c2,a1,a2→c1

其实就是走完自己的链表走另一个链表

def getIntersectionNode(headA,headB):
    p1 = headA
    p2 = headB
    while p1 != p2:
        if not p1:
            p1 = headB
        else:
            p1 = p1.next
        if not p2:
            p2 = headA
        else:
            p2 = p2.next
    return p1

反转链表

迭代反转整个链表

def reverse(head):
    if (not head) or (not head.next):
        return head
    prev = None
    curr = head
    while curr:
        next_ = curr.next
        curr.next = prev
        prev = curr
        curr = next_
    return prev

递归反转整个链表

reverse函数的定义:输入一个结点head,将以head为起点的链表反转,并返回反转之后的头结点

def reverse(head):
    if (not head) or (not head.next):
        return head
    last = reverse(head.next)
    head.next.next = head
    head.next = None
    return last

递归反转链表前N个结点

def reverseN(head,n):
    if n == 1:
        successor = head.next
        return head
    last = reverseN(head.next,n-1)
    head.next.next = head
    head.next = successor
    return last

递归反转一部分链表

索引从1开始,反转区间[m,n]中的链表元素

def reverseBetween(head,left,right):
	def reverseN(head,n):
        if n == 1:
            self.successor = head.next
            return head
        last = reverseN(head.next,n-1)
        head.next.next = head
        head.next = self.successor
        return last
    if left == 1:
        return reverseN(head,right)
    head.next = self.reverseBetween(head.next,left-1,right-1)
    return head

迭代反转一部分链表

class Solution:
    def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
        def reverse(head):
            pre = None
            cur = head
            while cur:
                next_ = cur.next
                cur.next = pre
                pre = cur
                cur = next_
        dummy = ListNode(-1)
        dummy.next = head
        left_connect = dummy #不反转的左边最后一个结点
        for i in range(left-1):
            left_connect = left_connect.next
        right_node = left_connect #反转的最后一个结点
        for i in range(right-left+1):
            right_node = right_node.next
        right_connect = right_node.next #不反转的右边第一个结点
        #切断连接
        left_node = left_connect.next #反转的第一个结点
        left_connect.next = None
        right_node.next = None
        #反转
        reverse(left_node)
        #连接
        left_connect.next = right_node
        left_node.next = right_connect
        return dummy.next

K个一组反转链表

def reverseKGroup(head,k):
    #反转区间[a,b)的元素
    def reverse(a,b):
        pre = None
        cur = a
        while cur != b:
            next_ = cur.next
            cur.next = pre
            pre = cur
            cur = next_
        return pre
    if not head:
        return None
    a = head
    b = head
    for i in range(k):
        if not b:
            return head
        b = b.next
    newHead = reverse(a,b)
    a.next = reverseKGroup(b,k)
    return newHead

判断单链表是否回文

寻找回文串是从中间向两端扩展,判断回文串是从两端向中间收缩

法1:把原始链表反转存入新链表,比较两条链表是否相同。

法2:借助二叉树的后序遍历,模仿双指针判断回文

def isPalindrome(head):
    def traverse(right):
        if not right:
            return True
        res = traverse(right.next)
        res = res and (right.val == left.val)
        left = left.next
        return res
    left = head
    return traverse(head)

法1和法2的时间复杂度都为O(N),空间复杂度也都为O(N)

优化空间复杂度到O(1):

(1)快慢指针找到中点

(2)如果fast没有指向None,说明链表长度为奇数,slow需要再向前一步

(3)从slow开始反转后面的链表,比较回文串

def isPalindrome(head):
    def reverse(head):
        pre = None
        cur = head
        while cur:
            next_ = cur.next
            cur.next = pre
            pre = cur
            cur = next_
        return pre
    if not head.next:
        return True
    slow = head
    fast = head
    while fast and fast.next:
        fast = fast.next.next
        slow = slow.next
    if fast:
        slow = slow.next
    left = head
    right = reverse(slow)
    while right:
        if left.val != right.val:
            return False
        right = right.next
        left = left.next
    return True
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值