Leetcode Hot100之链表

news2024/9/20 10:59:00

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手写SpringMVC之调度器DispatcherServlet

DispatcherServlet&#xff1a;分发、调度 根据上一节&#xff0c;已经实现了将controller的方法添加到容器中&#xff0c;而DispatcherServlet的作用就是接收来自客户端的请求&#xff0c;然后通过URI的组合&#xff0c;来找到对应的RequestMapping注解的方法&#xff0c;调用…

基于esp-idf的arm2d移植

什么是ARM2D Arm在Github上发布了一个专门针对“全体” Cortex-M处理器的2D图形加速库——Arm-2D 我们可以简单的把这个2D图形加速库理解为是一个专门针对Cortex-M处理器的标准“显卡驱动”。虽然这里的“显卡驱动”只是一个夸张的说法——似乎没有哪个Cortex-M处理器“配得上…

记一次对ouija渗透测试c语言逆向学习

概要 初始知识 web应用枚举 二进制逆向 文件枚举 堆栈溢出 学到知识 hash长度攻击 任意文件读取 二进制逆向分析 信息收集 端口扫描 nmap --min-rate 1000 -p- 10.129.30.104 发现22&#xff0c;80&#xff0c;3000端口 网站探测 目录枚举 feroxbuster -u http://10.1…

Qt 基于FFmpeg的视频播放器 - 播放、暂停以及拖动滑动条跳转

Qt 基于FFmpeg的视频转换器 - 播放、暂停以及拖动进度条跳转 引言一、设计思路二、核心源码以及相关参考链接 引言 本文基于FFmpeg&#xff0c;使用Qt制作了一个极简的视频播放器. 相比之前的版本&#xff0c;加入了播放、暂停、拖动滑动条跳转功能&#xff0c;如上所示 (左图)…

局域网聊天软件 matrix

窝有 3 只 Android 手机 (3 号手机, 6 号手机, 9 号手机), 2 台 ArchLinux PC (4 号 PC, 6 号 PC), 1 台 Fedora CoreOS 服务器 (5 号). (作为穷人, 窝使用的基本上是老旧的二手设备, 比如 5 年前的手机, 9 年前的笔记本, 10 年前的古老 e5v3 主机, 都比较便宜. ) 窝经常需要 …

format()函数

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法介绍 format()可以对数据进行格式化处理操作&#xff0c;语法如下&#xff1a; format(value, format_spec) format_spec为格式化解释。当参数…

高性能Web服务器-Nginx的常用模块

文章目录 Nginx安装Nginx平滑升级与回滚平滑升级流程第1步&#xff0c;下载新版本第2步&#xff0c;编译第3步&#xff0c;执行make第4步&#xff0c;对比新旧版本第5步&#xff0c;备份旧nginx二进制文件第6步&#xff0c;模拟用户正在访问nginx第7步&#xff0c;替换旧的ngin…

The First Descendant第一后裔联机失败、联机报错这样处理

第一后裔/The First Descendant是一款免费的多人合作射击游戏&#xff0c;玩家将进入一片混乱的英格里斯大陆&#xff0c;扮演继承者后裔&#xff0c;通过各种主支线任务和故事剧情触发&#xff0c;最终揭开自身的秘密&#xff0c;并带领大家一起抵抗邪恶势力的入侵。为了避免玩…

Flume学习

Flume(分布式数据采集系统)学习 1.Flume架构 什么是flume&#xff1f; flume是一个分布式、可靠、和高可用的海量日志采集、聚合和传输的系统。 支持在日志系统中定制各类数据发送方&#xff0c;用于收集数据; 同时&#xff0c;Flume提供对数据进行简单处理&#xff0c;并写到…

华为昇腾310B1芯片DVPP模块VENC视频编码接口调用流程以及视频编码代码梳理

目录 1 接口调用流程 2 代码流程梳理 1 接口调用流程 在CANN 8.0.RC1 AscendCL应用软件开发指南 (C&C, 推理) 01.pdf 文档中有接口调用流程 2 代码流程梳理 代码在samples: CANN Samples - Gitee.com 然后我把这个代码完整的看了一遍&#xff0c;然后梳理了详细的代码…

web学习笔记(七十二)

目录 1.vue2通过$parent实现组件传值——父传子 2.vue2 通过$children实现组件传值——子传父 3. provide和inject传值&#xff08;依赖注入&#xff09; 4.vue2如何操作dom 5.vue2如何拿到最新的dom 6.filters过滤器 7.vue2的生命周期 8.vuex的用法 1.vue2通过$parent…

【SCI索引,Fellow主讲】2024年可持续发展与能源资源国际学术会议(SDER 2024,8月9-11)

2024年可持续发展与能源资源国际学术会议&#xff08;SDER 2024&#xff09;将在2024年8月9-11日于中国重庆召开。 大会旨在为从事可持续发展与能源资源方面的专家学者、工程技术人员、技术研发人员提供一个共享科研成果和前沿技术&#xff0c;了解学术发展趋势&#xff0c;拓…

2.4G特技翻斗车方案定制

遥控翻斗车不仅能够提供基本的前进、后退、左转和右转功能&#xff0c;还设计有多种特技动作和互动模式&#xff0c;以增加娱乐性和互动性。 1、无线遥控&#xff1a;玩具翻斗车一般通过2.4G无线遥控器进行控制&#xff0c;允许操作者在一定距离内远程操控车辆。 2、炫彩灯光…

Java程序员接单的十条“野路子”,分分钟收入20K!

Java程序员除了主业工作外&#xff0c;也要适当扩展兼职接单这条路。毕竟Java接单可以说是Java程序员进行技术变现的最佳方式之一。 因为Java程序员兼职接单的难度相对更低&#xff0c;单量也比较可观&#xff0c;最重要的是性价比也很顶&#xff0c;且听我一一道来&#xff1a…

Nature推荐的三种ChatGPT论文写作指令(含PDF下载)

1. 润色学术论文 ChatGPT学术润色指令&#xff1a; “I’m writing a paper on [topic]for a leading [discipline] academic journal. WhatItried to say in the following section is [specific point]. Please rephrase itfor clarity, coherence and conciseness, ensuri…

Charles抓包工具系列文章(五)-- DNS spoofing (DNS域名伪装)

一、背景 DNS域名是依赖DNS域名服务器&#xff0c;特别是内部域名&#xff0c;最后寻址到后端服务地址。 当我们无法修改客户端的域名&#xff0c;而想让其指向到我们期望地址时&#xff0c;可以采用charles的DNS spoofing。 何谓DNS 欺骗&#xff1a;将自己的主机名指定给远…

电商平台数据功能封装API需要注意些什么?如何调用封装后的API?

一、引言 随着电商行业的蓬勃发展&#xff0c;电商平台的数据功能愈发复杂多样&#xff0c;如何高效、安全地管理和使用这些数据成为了电商平台开发者面临的重要问题。API&#xff08;Application Programming Interface&#xff09;作为不同软件之间进行通信的桥梁&#xff0…

Win32消息机制原理及消息运转

一.消息机制原理 1.消息类型&#xff1a; WIndows定义的一系列WM_XXX开头的&#xff0c;用来表示键盘按键&#xff0c;鼠标点击&#xff0c;窗口变化&#xff0c;用户自定义等各种消息; 2.消息队列&#xff1a; Windows为每一个正在运行的程序维护一个消息队列应用程序的消…

Pycharm 文件标头设置

一、设置模板步骤&#xff1a; “文件File--设置Settings--编辑器Editor--File and Code Templates- Python Script” 里面设置模板 官方预设变量表 变量名 含义 ${DATE} 当前系统日期 ${DAY} 当前月的第几日 ${DAY_NAME_SHORT} 当前星期几的单词缩写&#xff08…

计算机网络之数据通信原理(下)

上一讲内容&#xff1a;数据传输方式、数据传输形式、传输差错处理、常用差错检测方法 数据通信过程中&#xff0c;一个很重要的问题就是如何控制数据的传输&#xff0c;就涉及到了传输控制规程&#xff08;协议&#xff09; 下面介绍两种&#xff1a; ①BSC&#xff1a;面向…