Leetcode Hot100之链表

1.相交链表

  • 解题思路
    快慢指针:分别求出两个链表的长度n1和n2,在长度较长的那个链表上,快指针先走n2 - n1,慢指针再出发,最后能相遇则链表相交
    时间复杂度O(m+n),空间复杂度O(1)
  • 代码
    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
            if not headA or not headB:
                return None
            l_a = 0
            l_b = 0
            node = headA
            while node:
                l_a += 1
                node = node.next
            node = headB
            while node:
                l_b += 1
                node = node.next
            node1 = headA 
            node2 = headB
            if l_b > l_a:
                l_a, l_b = l_b, l_a
                node1, node2, = node2, node1
            for _ in range(l_a - l_b):
                node1 = node1.next
            while node1 and node2:
                if node1 == node2:
                    return node1
                node1 = node1.next
                node2 = node2.next
            return None
    

2.翻转链表

  • 解题思路
    最基本的题目,一定要掌握。prev初始化成None,不需要dummy_head
    时间复杂度O(N),空间复杂度O(1)
  • 代码
    class Solution:
        def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
            if not head:
                return head
            # prev直接初始化成None就好
            prev = None
            cur = head
            nex = None
            while cur:
                nex = cur.next
                cur.next = prev
                prev = cur
                cur = nex
            return prev
    

3.回文链表

  • 解题思路
    查找中间链表,然后翻转后半段,接着使用双指针比较判断即可
    时间复杂度O(N),空间复杂度O(1)
  • 代码
    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        def isPalindrome(self, head: Optional[ListNode]) -> bool:
            def reverse(head):
                if not head:
                    return head
                prev = None
                cur = head
                nex = None
                while cur:
                    nex = cur.next
                    cur.next = prev
                    prev = cur
                    cur = nex
                return prev
            if not head:
                return False
            if not head.next:
                return True
            slow = head
            fast = head.next
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
            
            head2 = reverse(slow.next)
            node_1 = head
            node_2 = head2
            
            while node_1 and node_2:
                if node_1.val != node_2.val:
                    return False
                node_1 = node_1.next
                node_2 = node_2.next
            return True
    

4. 环形链表

  • 题目描述
    判断链表是否有环
  • 解题思路
    快慢指针,快指针一次走一步,慢指针一次走两步,如果有环的话,他们一定在环中相遇。类比于操场跑圈的套圈,对于slow来说,fast是一个节点一个节点的靠近slow的
    时间复杂度:O(N),
    空间复杂度:O(1)。
  • 代码
    class Solution:
        def hasCycle(self, head: Optional[ListNode]) -> bool:
            if not head or not head.next:
                return False
            
            fast = slow = head
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
                if slow == fast:
                    return True
            return False
    

5. 环形链表2

  • 题目描述
    如果链表有环需要返回入环的节点,无环返回None
  • 解题思路
    图片来自代码随想录;查找是否有环和上一题目一样,使用快慢指针,如果有环,那么他们一定在环内相遇。如下图所示,慢指针走过的路程是x + y,快指针走过的路程是 x + y + (y + z) * n,又因为快指针走的路程是慢指针的两倍,因此有x + y + (y + z) * n = 2* (x + y), 也就是(y + z) * n = x + y, 也就是(y+z)*(n-1) + z = x,那么如果两个节点分别从头结点和相遇节点出发,一定可以在入口节点处相遇;
    时间复杂度:O(N),
    空间复杂度:O(1)。在这里插入图片描述
  • 代码
    class Solution:
        def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
            if not head or not head.next:
                return None
            slow = fast = head
            has_cycle = False
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
                if fast == slow:
                    has_cycle = True
                    break
            if not has_cycle:
                return None
            node1 = head
            node2 = slow
            while node1 != node2:
                node1 = node1.next
                node2 = node2.next
            return node1
    

6. 合并两个有序链表

  • 解题思路
    是链表排序和合并K个有序链表等题目要用的基本模块
    时间复杂度O(n+m), 空间复杂度O(n+m)
  • 代码
    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
            if not list1:
                return list2
            if not list2:
                return list1
            head = ListNode()
            cur = head
            node1 = list1
            node2 = list2
            while node1 and node2:
                if node1.val < node2.val:
                    cur.next = node1
                    node1 = node1.next
                else:
                    cur.next = node2
                    node2 = node2.next
                cur = cur.next
            if node1:
                cur.next = node1
            if node2:
                cur.next = node2
            return head.next
    

7. 两数相加

  • 题目描述
    在这里插入图片描述

  • 解题思路
    注意考虑进位、两个数字位数不同的情况
    时间复杂度:O(max(m,n)),其中 m 和 n 分别为两个链表的长度。我们要遍历两个链表的全部位置,而处理每个位置只需要 O(1) 的时间。
    空间复杂度:O(1)。注意返回值不计入空间复杂度。

  • 代码

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
            if not l1:
                return l2
            if not l2:
                return l1
            prev = 0
            n1 = n2 = 0
            new_head = ListNode()
            node1 = l1
            node2 = l2
            cur = new_head
            while node1 or node2:
                n1 = node1.val if node1 else 0
                n2 = node2.val if node2 else 0
                s = n1 + n2 + prev
                node = ListNode(s % 10)
                prev = s // 10
                cur.next = node
                cur = cur.next
                if node1:
                    node1 = node1.next
                if node2:
                    node2 = node2.next
            if prev != 0:
                node = ListNode(prev)
                cur.next = node
            return new_head.next
    

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

  • 解题思路
    快慢指针,快指针先走N,然后快慢一起走,当快指针走到末尾时,慢指针指向的就是要删除的节点
  • 代码
    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
            if not head:
                return None
            new_head = ListNode(0, head)
            prev = new_head
            slow = fast = head
            for i in range(n):
                if not fast:
                    raise ValueError("n must greter than length of list")
                fast = fast.next
            while fast:
                prev = slow
                slow = slow.next
                fast = fast.next
            prev.next = slow.next
            return new_head.next
    

9. 两两交换链表中的节点

  • 题目描述
    给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
  • 解题思路
    模拟两两交换的过程即可
  • 代码
    class Solution:
        def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
            if not head:
                return head
            new_head = ListNode(next=head)
            cur = head
            prev = new_head
            while cur and cur.next:
                next = cur.next
                cur.next = next.next
                next.next = cur
                prev.next = next
                prev = cur
                cur = prev.next
            return new_head.next
    

10. k个一组翻转链表

  • 题目描述
    给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

    k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

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

  • 解题思路
    将前k个节点切断成一个链表,进行翻转,并递归对剩下的链表进行‘k个一组翻转链表’操作,再将两个链表连起来,即可

  • 代码

    class Solution:
        def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
            def reverse(head):
                if not head:
                    return head
                prev = None
                cur = head
                nex = None
                while cur:
                    nex = cur.next
                    cur.next = prev
                    prev = cur
                    cur = nex
                return prev
            
            if not head:
                return head
            l = 0
            node = head
            while node:
                l += 1
                node = node.next
            if l < k:
                return head
            node = head
            for i in range(k - 1):
                node = node.next
            new_head = node.next
            node.next = None
            reverse_head = reverse(head)
            head.next = self.reverseKGroup(new_head, k)
            return reverse_head
    

11. 随机链表的复制

  • 解题思路
    第一遍循环,复制每个节点,并把他们通过next连接成一个普通的链表,同时构建哈希表,哈希表的key是旧的节点,value是复制的节点;
    第二遍循环,通过哈希表完成random的指定,注意random可能是空的
    时间复杂度O(N), 空间复杂度O(N)
  • 代码
    class Solution:
        def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
            if not head:
                return head
            dic = {}
            node = head
            new_head = Node(0)
            prev = new_head
            while node:
                new_node = Node(x=node.val)
                prev.next = new_node
                dic[node] = new_node
                prev = new_node
                node = node.next
            
            node = head
            while node:
                # 一定要注意原始节点的random是不是空的
                if node.random:
                    dic[node].random = dic[node.random]
                node = node.next
            return new_head.next
    

12. 排序链表

  • 题目描述

  • 解题思路
    解题思路:归并排序的思想,找到中间节点,然后分别对左右两边进行排序,最后合并左右两边的有序链表。
    这道题目的关键:1. 找到链表的中间节点:用快慢指针实现,慢指针一次走一步,快指针一次走两步,当快指针走到末尾时,慢指针指向的就是中间节点(不用求出链表长度再计算中间节点的位置);2.将链表从中间节点切断成两个链表;3. 合并两个有序链表。
    时间复杂度:O(nlogn),其中 n 是链表的长度。
    空间复杂度:O(logn),其中 n 是链表的长度。空间复杂度主要取决于递归调用的栈空间。

  • 代码

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
       def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
           def merge(l1, l2):
               if not l1:
                   return l2
               if not l2:
                   return l1
               head = ListNode()
               cur = head
               node1 = l1
               node2 = l2
               while node1 and node2:
                   if node1.val < node2.val:
                       cur.next = node1
                       node1 = node1.next
                   else:
                       cur.next = node2
                       node2 = node2.next
                   cur = cur.next
               if node1:
                   cur.next = node1
               if node2:
                   cur.next = node2
               return head.next
           if not head or not head.next:
               return head
           # 找到中间节点的方法:快慢指针
           slow = head
           fast = head.next
           while fast and fast.next:
               slow = slow.next
               fast = fast.next.next
           node1 = head
           node2 = slow.next
            # 从中间断开链表
           slow.next = None
           head1 = self.sortList(node1)
           head2 = self.sortList(node2)
           return merge(head1, head2)
    

13. 合并k个升序链表

  • 题目描述
    给你一个链表数组,每个链表都已经按升序排列。
    请你将所有链表合并到一个升序链表中,返回合并后的链表。
  • 解题思路
    分治,通过递归两两合并,其中会用到合并两个有序链表这个函数,在上一个题目排序链表中也用到了,因此这个模块函数要掌握好;
  • 代码
    class Solution:
        def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
            def merge(l1, l2):
                if not l1:
                    return l2
                if not l2:
                    return l1
                head = ListNode()
                cur = head
                node1 = l1
                node2 = l2
                while node1 and node2:
                    if node1.val < node2.val:
                        cur.next = node1
                        node1 = node1.next
                    else:
                        cur.next = node2
                        node2 = node2.next
                    cur = cur.next
                if node1:
                    cur.next = node1
                if node2:
                    cur.next = node2
                return head.next
            n = len(lists)
            if n == 0:
                return None
            if len(lists) == 1:
                return lists[0]
            mid = n // 2
            head1 = self.mergeKLists(lists[: mid])
            head2 = self.mergeKLists(lists[mid :])
            return merge(head1, head2)
    

13 LRU

  • 题目描述
    请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
    实现 LRUCache 类:
    LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
    int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
    void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
    函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
  • 解题思路
    哈希表 + 双向链表。哈希表用于快速查找节点,双向链表用于存储使用情况,最近被使用的节点被放在双向链表的后端
    时间复杂度: O(1), 空间复杂度:O(capacity)。
    注意python的字典删除元素的方法是:pop(key[,default])
    删除字典给定键 key 所对应的值,返回值为被删除的值。key值必须给出。 否则,返回default值。
  • 代码
    class BiNode:
        def __init__(self, val=0, next=None, prev=None, key=None):
            self.val = val
            self.next = next
            self.prev = prev
            self.key = key
    
    
    class LRUCache:
    
        def __init__(self, n):
            self.n = n
            self.dic = {}
            self.head = BiNode()
            self.tail = BiNode()
            self.head.next = self.tail
            self.tail.prev = self.head
        
        def add_node_to_tail(self, node):
            self.tail.prev.next = node
            node.prev = self.tail.prev
            node.next = self.tail
            self.tail.prev = node
        
        def rm_node(self, node):
            prev = node.prev
            nex = node.next
            prev.next = nex
            nex.prev = prev
        
        def get(self, key):
            if key in self.dic:
                self.rm_node(self.dic[key])
                self.add_node_to_tail(self.dic[key])
                return self.dic[key].val
            else:
                return -1
        def put(self, key, value):
            if key in self.dic:
                self.dic[key].val = value
                self.rm_node(self.dic[key])
                self.add_node_to_tail(self.dic[key])
            else:
                if len(self.dic) == self.n:
                    to_delete = self.head.next
                    self.rm_node(to_delete)
                    self.dic.pop(to_delete.key)
                new_node = BiNode()
                new_node.val = value
                new_node.key = key
                self.dic[key] = new_node
                self.add_node_to_tail(new_node)
    
    
    # Your LRUCache object will be instantiated and called as such:
    # obj = LRUCache(capacity)
    # param_1 = obj.get(key)
    # obj.put(key,value)
    

总结

对于链表题目,主要的解题思路有:快慢指针、翻转链表(局部)、合并有序链表、查找中间位置的链表节点、将长链表分解切断成小的链表(分治)。
需要熟练掌握的模块:翻转链表、合并有序链表、查找中间位置的链表节点
查找中间位置的链表节点,使用快慢指针:

slow = head
fast = head.next
while fast and fast.next:
    slow = slow.next
    fast = fast.next.next

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/751093.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

白敬亭章若楠甜度报表的难哄大师

#白敬亭章若楠&#xff0c;甜度爆表的难哄大师#&#x1f389;&#x1f389;&#x1f389;各位小伙伴们&#xff0c;你们还记得那个让我们心跳加速、嘴角上扬的CP组合吗&#xff1f;没错&#xff0c;就是白敬亭和章若楠&#xff01;他们可是凭借一部新剧&#xff0c;再次让我们感…

520. 检测大写字母

题目 我们定义&#xff0c;在以下情况时&#xff0c;单词的大写用法是正确的&#xff1a; 全部字母都是大写&#xff0c;比如 “USA” 。单词中所有字母都不是大写&#xff0c;比如 “leetcode” 。如果单词不只含有一个字母&#xff0c;只有首字母大写&#xff0c;比如 “Go…

World of Warcraft [CLASSIC] plugin lua

World of Warcraft [CLASSIC] plugin lua 魔兽世界lua脚本插件 World of Warcraft API - Wowpedia - Your wiki guide to the World of Warcraft D:\World of Warcraft\_classic_\Interface\AddOns zwf.lua function CountdownFunc()CountdownFrame CreateFrame("Fram…

常见的字符串函数(包含头文件string.h)和字符函数(2)

八. strstr函数 1.strstr的定义 char *strstr( const char *str1, const char *str2 ); ->1. strstr查找子串(str2)在字符串(str2)中第一次出现的位置&#xff0c;记录并返回该位置的指针&#xff0c;如果找不到&#xff0c;则返回NULL ->2. str1&#xff1a;查找字符…

不用再找了,这是大模型实践最全的总结

随着ChatGPT的迅速出圈&#xff0c;加速了大模型时代的变革。对于以Transformer、MOE结构为代表的大模型来说&#xff0c;传统的单机单卡训练模式肯定不能满足上千&#xff08;万&#xff09;亿级参数的模型训练&#xff0c;这时候我们就需要解决内存墙和通信墙等一系列问题&am…

Mysql索引的实现原理,B+Tree,WAL

InnoDB 引擎&#xff0c;每一个数据表有两个文件 .frm和.ibd&#xff0c;分别为表结构&#xff0c;数据和索引&#xff0c;数据挂在主索引的叶子节点上&#xff0c;此主索引称为聚簇索引。 MyISAM 引擎&#xff0c;每一个数据表有三个文件.frm和.MYI和.MYD&#xff0c;分别为表…

测试报告-HTMLTestRunner报告优化(中/英文)

引用原始的HTMLTestRunner.py文件生成的测试报告在美观性不是很好&#xff0c;使用在此文件基础上优化后的HTMLTestReportCN.py文件(生成的报告为中文)、HTMLTestReportEN.py文件(生成的报告为英文)。 1 首先新建一个Python项目 例如&#xff1a;testHtmlReport 创建case包&am…

指纹浏览器是什么?跨境多账号安全如何保证?

随着电子商务的蓬勃发展&#xff0c;越来越多的商家选择开设多店来扩大经营规模。然而多店运营也带来了一系列的挑战&#xff0c;其中之一就是账号安全。 1. 了解反检测浏览器和代理服务器 在我们开始讨论如何有效地使用反检测浏览器之前&#xff0c;我们首先需要了解这两个工…

如何用亚马逊合作伙伴网络快速上线跨境电商

目前跨境电商已成为行业发展主流&#xff0c;如何快速、低成本打造品牌海外独立站和智能客服营销中心、构建全链路跨境电商体系是出海电商商家都会遇到的难题。亚马逊云科技凭借与亚马逊电商平台易于集成的先天优势成为首选的电商解决方案平台。本文介绍了如何用亚马逊云科技平…

SpringCloud分布式微服务链路追踪方案:Skywalking

一、引言 随着微服务架构的广泛应用&#xff0c;系统的复杂性也随之增加。在这种复杂的系统中&#xff0c;应用通常由多个相互独立的服务组成&#xff0c;每个服务可能分布在不同的主机上。微服务架构虽然提高了系统的灵活性和可扩展性&#xff0c;但也带来了新的挑战&#xf…

深度学习论文撰写实验对比分析时复现其它论文方法的问题

&#x1f4aa; 专业从事且热爱图像处理&#xff0c;图像处理专栏更新如下&#x1f447;&#xff1a; &#x1f4dd;《图像去噪》 &#x1f4dd;《超分辨率重建》 &#x1f4dd;《语义分割》 &#x1f4dd;《风格迁移》 &#x1f4dd;《目标检测》 &#x1f4dd;《暗光增强》 &a…

说一说ABAP CDS View的发展历史与特性

1. 背景 随着SAP Fiori应用程序的兴起&#xff0c;SAP领域的小伙伴接触和使用ABAP CDS View的机会也是越来越多。今天&#xff0c;让我们花些时间&#xff0c;一起在了解下这项技术的设计初衷和发展历史。 2. 设计初衷 说起ABAP CDS View&#xff0c;就不得不提及SAP HANA。…

Open AI限制来袭?用上这个工具轻松破局!

【导语】近日&#xff0c;AI领域掀起了一场不小的波澜。Open AI宣布&#xff0c;从7月9日起&#xff0c;将对部分地区的开发者实施API调用限制。这一消息对于许多依赖Open AI技术的国内初创团队来说&#xff0c;无疑是一个沉重的打击。 对于这些团队而言&#xff0c;Open AI的A…

Arcgis地统计分析工具灰色不可用 解决方法

使用Arcmap&#xff0c;调用地统计分析工具&#xff08;Geostatistical Analyst&#xff09;下的探索数据&#xff08;Explore Data&#xff09;&#xff0c;发现工具呈灰色不可用。这是由于扩展模块中没有将该模块做勾选设置导致的。下面介绍一下如何解决地统计分析工具不可用…

汇聚荣做拼多多运营第一步是什么?

汇聚荣做拼多多运营第一步是什么?在众多电商平台中&#xff0c;拼多多凭借其独特的社交电商模式迅速崛起&#xff0c;吸引了大量消费者和商家的目光。对于希望在拼多多上开店的商家而言&#xff0c;了解如何进行有效运营是成功的关键。那么&#xff0c;汇聚荣做拼多多运营的第…

web前端——HTML

目录 一、HTML概述 1.HTML是什么&#xff1f; 2.HTML具体化解释 二、HTML基本语法 1.声明 2. Head头标签 3.body身体标签 4.一个html的基本结构 5.标签 6.标签属性 ①属性的格式 ②属性的位置 ③添加多个属性 三、基本常用标签 1.超链接 2.图像标签 ①图像标…

C++编程(四)this指针 常函数 常对象 静态成员

文章目录 一、this指针&#xff08;一&#xff09;概念&#xff08;二&#xff09;显式使用this指针的场景1. 当形参和成员变量名一致时2. 返回对象自身的时候必须要使用this指针3. 在类中销毁一个对象 二、常函数和常对象&#xff08;一&#xff09;常函数1. 概念2. 语法格式 …

【2024.6.23】今日科技时事:科技前沿大事件

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

最新!AI大模型的研究热点!

引言 在人工智能的浪潮中&#xff0c;大模型研究如日中天&#xff0c;涵盖诸多研究方向&#xff0c;每个方向均承载着独特的研究焦点与挑战。 以下&#xff0c;我们将逐一探讨数个备受瞩目的研究方向&#xff0c;包括检索增强生成RAG、大模型Agent、Mamba、MoE、LoRA等&#…

【LeetCode】八、堆的使用:第K个最大元素 + 前K和高频单词

文章目录 1、Java中的堆结构2、leetcode215&#xff1a;数组中的第K个最大元素3、leetcode692&#xff1a;前K个高频单词 1、Java中的堆结构 PriorityQueue类取堆顶元素删除堆顶元素堆的元素个数遍历堆 2、leetcode215&#xff1a;数组中的第K个最大元素 这题应该快排来解&…