牛客面试必刷TOP101——链表

本文深入探讨了链表的各种操作,包括单链表反转、链表区间反转、每k个节点反转、合并有序链表、处理链表环及找到环的入口节点、寻找链表倒数k个节点、删除链表特定位置节点、查找链表第一个公共节点等。通过高效算法实现,确保在限定的时间和空间复杂度内完成操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目】
反转链表
链表内指定区间反转
链表中的节点每k个一组翻转
合并两个有序链表
合并K个有序链表
判断链表中是否有环
链表中环的入口结点
链表中倒数最后k个结点
删除链表的倒数第n个节点
两个链表的第一个公共结点
链表相加(二)
单链表的排序
判断一个链表是否为回文结构
链表的奇偶重排
删除有序链表中重复的元素-I
删除有序链表中重复的元素-II

反转链表
class Solution:
    def ReverseList(self , head: ListNode) -> ListNode:
        # write code here
        if not head:
            return head
        pre = None
        while head:
            nx = head.next
            head.next = pre
            pre = head
            head = nx
        return pre
链表指定区间反转

将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O*(n),空间复杂度 O(1)。

思路1:提到反转,想到栈

将m到n位置的结点加入到栈中,对栈内元素不断弹出,实现反转

class Solution:
    def reverseBetween(self , head: ListNode, m: int, n: int) -> ListNode:
        # write code here
        if not head:
            return head
        if m == n:
            return head
        dummpy = ListNode(-1)
        dummpy.next = head
        cur = head
        pre = dummpy
        for i in range(m-1):
            pre = cur
            cur = cur.next
        s = []
        for j in range(n-m):
            s.append(cur)
            cur = cur.next
        pre.next = cur
        nx = cur.next
        while s:
            cur.next = s.pop()
            cur = cur.next
        cur.next = nx
        return dummpy.next

思路2:每次将当前结点放到该区间的最前面实现反转,具体可以参考K个一组翻转,里面有详解

class Solution:
    def reverseBetween(self , head: ListNode, m: int, n: int) -> ListNode:
        # write code here
        if not head:
            return head
        if m == n:
            return head
        dummpy = ListNode(-1)
        dummpy.next = head
        cur = head
        pre = dummpy
        for i in range(m-1):
            pre = cur
            cur = cur.next
        for j in range(m,n):
            nx = cur.next
            cur.next = nx.next
            nx.next = pre.next
            pre.next = nx
        return dummpy.next
链表中的节点每k个一组翻转

详解

合并两个有序链表

思路:双指针

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def Merge(self , pHead1: ListNode, pHead2: ListNode) -> ListNode:
        p = head = ListNode(0)
        while pHead1 and pHead2:
            if pHead1.val < pHead2.val:
                head.next = pHead1
                pHead1 = pHead1.next
                head = head.next
            else:
                head.next = pHead2
                pHead2 = pHead2.next
                head = head.next
        head.next = pHead1 or pHead2
        return p.next
合并K个有序链表 【BM5】

合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。

思路

  • 合并两个有序链表,可以采用双指针,分别指向两个有序链表,依次比较,令新链表结点head指向较小的指针,即merge()
  • 合并K组,可以考虑使用分治法;
    • 分:将K组不断划分,直到子问题只有一组链表;
    • 治:合并两个子问题,即合并两个有序链表
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def mergeKLists(self , lists: List[ListNode]) -> ListNode:
        # write code here
        if len(lists) == 0:
            return None
        def merge(p,q):
            node = ListNode(-1)
            head = node
            while p and q:
                if p.val <= q.val:
                    head.next = p
                    p = p.next
                else:
                    head.next = q
                    q = q.next
                head = head.next
            head.next = p or q
            return node.next
        def divide(lists,left,right):
            if left > right:
                return None
            elif left == right:
                return lists[left]
            else:
                mid = (left+right)//2
                return merge(divide(lists, left, mid), divide(lists, mid+1, right))
        
        return divide(lists, 0, len(lists)-1)
判断链表中是否有环 【BM6】
  1. 定义快慢指针,一个指针每次走一步,另一指针每次走两步;如果链表有环,则两个指针终究会在环中相遇
  2. 使用哈希表存储遍历过的结点,如果当前结点已经存在于哈希表中说明有环
def hasCycle(self , head: ListNode) -> bool:
    if not head:
        return False
    slow,fast = head,head
    while slow and fast:
        slow = slow.next
        if fast.next:
            fast = fast.next.next
        else:
            return False
        if slow == fast:
            return True
    return False
链表中环的入口结点 【BM7】
  1. 定义快慢指针
  2. 借助哈希表存储遍历过结点,当前结点若存在于哈希表中,则当前结点就是环的入口结点
class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        if not pHead:
            return pHead
        d = []
        while pHead:
            if pHead.val not in d:
                d.append(pHead.val)
            else:
                return pHead
            pHead = pHead.next
链表中倒数最后k个结点 【BM8】

题解

使用快慢指针,快指针比慢指针多走K步,当快指针为空时,慢指针即指向倒数第k个结点
特殊情况:1、输入为空;2、k值大于链表长度

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def FindKthToTail(self , pHead: ListNode, k: int) -> ListNode:
        # write code here
        if not pHead:
            return pHead
        p = pHead
        n = 1
        while p.next:
            n += 1
            p = p.next
        if n < k:
            return None
        front,behind = pHead,pHead
        for i in range(k):
            front = front.next
        while front:
            front = front.next
            behind = behind.next
        return behind
删除链表倒数第n个结点 【BM9】
  • 定义快慢指针,快指针比慢指针多走n步
  • 当快指针为空时,说明慢指针即为要删除的结点
class Solution:
    def removeNthFromEnd(self , head: ListNode, n: int) -> ListNode:
        # write code here
        pHead = ListNode(-1)
        pHead.next = head
        pre,cur = pHead,pHead
        for i in range(n):
            cur = cur.next
        while cur.next:
            pre = pre.next
            cur = cur.next
        pre.next = pre.next.next
        return pHead.next
两个链表的第一个公共结点 【BM10】

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空

  • 定义两个指针p1、p2,分别指向两个链表
  • 当每个指针走完各自的链表后,互换赛道继续走
    • 当p1为空时即走完第一个链表,将p1指向pHead2,走第二个赛道
    • 当p2为空时即走完第二个赛道,将p2指向pHead1,走第一个赛道
    • 最终p1和p2会在公共节点相遇,如果两个链表没有公共节点,则两个指针都走完两个链表,最终两个指针都为空,返回None
class Solution:
    def FindFirstCommonNode(self , pHead1 , pHead2 ):
        # write code here
        if not pHead1 or not pHead2:
            return None
        p1,p2 = pHead1,pHead2
        while p1 != p2:
            if not p1:
                p1 = pHead2
            else:
                p1 = p1.next
            if not p2:
                p2 = pHead1
            else:
                p2 = p2.next
        return p1
链表相加 【BM11】

题目:假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。给定两个这种链表,请生成代表两个整数相加值的结果链表

  • 将两个链表倒序
  • 长度长的链表存储相加结果
def f(head):
    '''把节点放入列表中,并反转'''
    num = []
    while head:
        num.append(head)
        head = head.next
    return num[::-1]
class Solution:
    def addInList(self , head1 , head2 ):
        num1, num2, carry = f(head1), f(head2), 0
        if len(num1) < len(num2):
            num1, num2 = num2, num1
        for i in range(len(num1)):
            if i < len(num2) or carry:
                summation = num1[i].val + (num2[i].val if i < len(num2) else 0) + carry
                carry, num = divmod(summation, 10)
                num1[i].val = num
            else:
                break
        if carry:
            carryhead = ListNode(carry)
            carryhead.next = num1[-1]
            return carryhead
        return num1[-1]
判断一个链表是否为回文结构

思路:
将链表结点的所有值存储在数组中,在数组中使用双指针判断是否为回文结果。

class Solution:
    def isPail(self , head: ListNode) -> bool:
        # write code here
        res = []
        while head:
            res.append(head.val)
            head = head.next
        if not res:
            return True
        i = 0
        j = len(res)-1
        while i <= j:
            if res[i] != res[j]:
                return False
            i += 1
            j -= 1
        return True
链表的奇偶重排 【牛客BM14】

给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。注意是节点的编号而非节点的数值。

要求:空间复杂度 O(n),时间复杂度 O(n)
题解

  1. 使用两个链表存储奇偶结点,最后进行连接,时间复杂度和空间复杂度均为O(n)
  2. 待更新
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def oddEvenList(self , head: ListNode) -> ListNode:
        # write code here
        if not head:
            return head
        p_node = ListNode(-1)
        q_node = ListNode(-1)
        p = head
        p_node.next = p
        q = head.next
        q_node.next = q
        while p.next and q.next:
            p.next = p.next.next
            q.next = q.next.next
            p = p.next
            q = q.next
        p.next = q_node.next
        return p_node.next
删除有序链表中重复的元素-I【BM15

删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次
例如:
给出的链表1→1→2,返回1→2.
给出的链表为1→1→2→3→3,返回1→2→3.
题解:

  • 定义两个指针p和q,q指向p的下一个结点;
  • 如果p和q的值相同,q指针就一直往后走,直到q的值与p不同;令p.next = q,既可实现相同元素的结点只保留一个。
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def deleteDuplicates(self , head: ListNode) -> ListNode:
        # write code here
        if not head:
            return head
        p = head
        q = p.next
        while q:
            while q and q.val == p.val:
                q = q.next
            p.next = q
            p = p.next
            if q:
                q = q.next
        return head
删除有序链表中重复的元素-II 【BM16】

给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。
例如:
给出的链表为1→2→3→3→4→4→5, 返回1→2→5.
给出的链表为1→1→1→2→3, 返回2→3.
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)

进阶:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

题解:

和BM15不同的是,该题需要将所有重复的元素都删除,因此可以再加一个指针pre,关系是pre->p->q。

  1. 定义一个哨兵结点node,令node.next = head。
  2. 比较p和q结点的值是否相同,如果相同,则q指针后移,直到q的值与p不同。
  3. 令pre.next = q,p = pre.next q = p.next

注意:当找到和p不同值的指针q后,15是直接令p指向q,保留一个重复的元素;而该题需要全部删掉,所以是;令pre指向q。因此需要定义一个哨兵结点,当链表最开始就全部是重复结点时,头结点也会被删掉。

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def deleteDuplicates(self , head: ListNode) -> ListNode:
        # write code here
        if not head:
            return head
        node = ListNode(-1)
        node.next = head
        pre = node
        p = head
        q = p.next
        while q:
            if p.val == q.val:
                while q and p.val == q.val:
                    q = q.next
                pre.next = q
                p = q
                if p:
                    q = p.next
            else:
                pre = pre.next
                p = p.next
                q = q.next
        return node.next
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃头嘤嘤魔

感谢厚爱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值