LeetCode:经典题之141、142 题解及延伸

系列目录

88.合并两个有序数组
52.螺旋数组
567.字符串的排列
643.子数组最大平均数
150.逆波兰表达式
61.旋转链表
160.相交链表
83.删除排序链表中的重复元素
389.找不同
1491.去掉最低工资和最高工资后的工资平均值
896.单调序列
206.反转链表
92.反转链表II
141.环形链表
142.环型链表


目录

  • 系列目录
  • 141. 环形链表
    • 常量因子
  • 142. 环型链表II


141. 环形链表

🌟链表+哈希表+快慢指针

原题链接


C++
若未特殊标明,以下题解均写用C++

方法一 哈希表
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        // 无序集合(哈希集合) 变量类型为 ListNode*
        // 检查节点是否访问过
        unordered_set<ListNode*> seen;
        // head 不为空
        while (head) {
            // 这里 count的返回值只能是 0或1
            // 不能写成 seen.count(head) != null
            if (seen.count(head) != 0)
                return true;

            // 节点不存在, 存入——seen.insert();
            // 避免无限循环导致超时
            seen.insert(head);
            head = head->next;        
        }

        return false;
    }
};

方法二 快慢指针 
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        // 如果 链表长度为0或1
        if (head == nullptr || head->next == nullptr)
            return false;

        ListNode* slow = head;
        ListNode* fast = head->next;

        while (slow != fast) {
            // 一定要先检查节点本身,避免出现 未定义行为
            // 链表长度为2
            if (fast == nullptr || fast->next == nullptr )
                return false;

            slow = slow->next;
            fast = fast->next->next;
        }

        return true;
    }
};

⚠️小细节:要先检查节点本身,再检查它的next

为什么 fast == nullptr 不能和 fast->next == nullptr 调换位置?

如果调换位置,先检查 fast->next == nullptr,当链表中只有一个节点时(即 head 是唯一节点),fast 将会是 head->next,它将是 nullptr

在这种情况下,fast->next 的检查(即 nullptr->next)将会导致一个未定义行为(通常是一个运行时错误),因为你不能在 nullptr 上解引用

所以,首先检查 fast == nullptr 是为了确保在尝试访问 fast->next 之前,fast 不是 nullptr 这样可以避免未定义行为



方法一 时间复杂度O(N) 空间复杂度O(N) 其中N为链表节点数

在这里插入图片描述


方法二 时间复杂度O(N) 空间复杂度O(1) 其中N为链表节点数

在这里插入图片描述

“相同”时间复杂度的两种算法,为什么实际执行时间差别这么大呢?

  • 在实践中,快慢指针方法通常具有较小的常数因子,因为它只涉及指针操作和比较
  • 虽然哈希表理论上的查找、插入的时间复杂度为O(1)——准确的说应该是平均O(1)
  • 哈希表方法在插入和查找元素时可能需要进行哈希计算,这可能会引入额外的计算开销
  • 但与其他数据结构相比,哈希表还是十分高效的,不必过于纠结

我们引入常量因子的概念

参考自《算法导论》

在这里插入图片描述

在这里插入图片描述

常量因子

又称常数因子

在C++代码中,常数因子经常以字面常量、const变量或枚举类型的形式出现

这些常数因子可能用于定义数组的大小、循环的次数、数学计算中的系数、物理模拟中的常量等

字面常量

#include <iostream>  
  
int main() {  
    const int arraySize = 100; // 数组大小的常数因子  
    int arr[arraySize]; // 使用字面常量作为数组大小  
  
    // 使用字面常量进行数学计算  
    double result = 3.14159 * 2 * 5; // π乘以直径,其中3.14159是π的近似值  
  
    std::cout << "Array size: " << arraySize << std::endl;  
    std::cout << "Calculation result: " << result << std::endl;  
  
    return 0;  
}

const变量

#include <iostream>  
  
int main() {  
    const double gravity = 9.8; // 重力加速度的常数因子  
  
    double height = 10.0; // 初始高度  
    double time = std::sqrt(2 * height / gravity); // 使用常数因子计算自由落体时间  
  
    std::cout << "Time to fall from " << height << " meters: " << time << " seconds" << std::endl;  
  
    return 0;  
}

枚举类型

#include <iostream>  
  
enum class Color {  
    RED = 1,     // 颜色常量的一个值  
    GREEN = 2,  
    BLUE = 3  
};  
  
int main() {  
    Color myColor = Color::RED; // 使用枚举常量  
    switch (myColor) {  
        case Color::RED:  
            std::cout << "The color is red." << std::endl;  
            break;  
        // ... 其他情况 ...  
    }  
  
    return 0;  
}

魔法数字(Magic Numbers)

在代码中直接使用字面常量而不是给它们命名可能会导致代码难以理解和维护
这些字面常量通常被称为“魔法数字” 为了改进代码的可读性和可维护性,最好将魔法数字替换为具有描述性名称的const变量或枚举常量

性能优化中的常数因子

在性能优化的场景中,常数因子可能会变得很重要
例如,如果一个循环体内部有一个操作非常耗时,而这个操作是固定的(即不随循环迭代而变化),那么减少这个操作的执行次数(即减少常数因子)可能会显著提高性能

// 假设有一个耗时的操作doExpensiveOperation()  
for (int i = 0; i < 1000; ++i) { // 这里的1000是一个常数因子  
    doExpensiveOperation(); // 假设这个操作非常耗时  
}  
  
// 优化:如果可能的话,减少循环次数(即减少常数因子)  
const int optimizedIterations = 500; // 假设这是优化后的常数因子  
for (int i = 0; i < optimizedIterations; ++i) {  
    doExpensiveOperation();  
}

在上面的例子中,通过减少循环次数(即减少常数因子),我们可能能够显著提高代码的性能
但是,这种优化要在确保算法正确性和满足性能需求的前提下进行





142. 环型链表II

🌟链表+哈希表+快慢指针

原题链接


C++
若未特殊标明,以下题解均写用C++

方法一 哈希表
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        unordered_set<ListNode*> seen;
        while (head) {
            if (seen.count(head) != 0)
                // pos 不作为参数传递
                return head;
            seen.insert(head);
            head = head->next;
        }

        return nullptr;
    }
};

方法二 快慢指针

图示:

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow = head, *fast = head;
        // 若没有环,fast 比 slow先指向空
        // 以此作为while 的循环条件判定
        while (fast) {
            // 如果链表长度为 1
            if (fast->next == nullptr)
                return nullptr;

            slow = slow->next;
            fast = fast->next->next;
            // 此时定义 *pre
            if (fast == slow) {
                ListNode *pre = head;
                while (pre != slow) {    
                    pre = pre->next;
                    slow = slow->next;
                }

                return pre;
            }  
        }

        // 直到 fast指向空都没能满足 slow = fast
        return nullptr;
    }
};

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

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

相关文章

Ps:转换为配置文件

Ps菜单&#xff1a;编辑/转换为配置文件 Edit/Convert to Profile 转换为配置文件 Convert to Profile命令可用于在不同色彩空间之间转换图像的颜色配置文件&#xff0c;从而确保在不同设备和介质上颜色的一致性和准确性。 ◆ ◆ ◆ 工作原理说明 当将图像的配置文件从一种转…

秒懂双亲委派机制

前言 最近知识星球中&#xff0c;有位小伙伴问了我一个问题&#xff1a;JDBC为什么会破坏双亲委派机制&#xff1f; 这个问题挺有代表性的。 双亲委派机制是Java中非常重要的类加载机制&#xff0c;它保证了类加载的完整性和安全性&#xff0c;避免了类的重复加载。 这篇文…

北斗三号短报文通信终端 | 助力户外无网络场景作业

北斗三号短报文通信终端是一款专为户外无网络场景作业设计的先进通信工具&#xff0c;它依托于中国自主研发的北斗卫星导航系统&#xff0c;为用户在偏远地区或无网络覆盖区域提供了可靠的通信保障。以下是关于北斗三号短报文通信终端的详细介绍&#xff1a; 一、功能特点 北斗…

[Python人工智能] 四十六.PyTorch入门 (1)环境搭建、神经网络普及和Torch基础知识

从本专栏开始,作者正式研究Python深度学习、神经网络及人工智能相关知识。前文讲解合如何利用keras和tensorflow构建基于注意力机制的CNN-BiLSTM-ATT-CRF模型,并实现中文实体识别研究。这篇文章将介绍PyTorch入门知识。前面我们的Python人工智能主要以TensorFlow和Keras为主,…

JavaWeb系列十六: jQuery初步入门

跟老韩-JavaScript开发利器之jQuery 1.1 原理示意图1.2 快速入门1.2 什么是jquery对象1.3 dom对象转jQuery对象1.4 jQuery对象转dom对象 jQuery是一个快速的, 简洁的javaScript库, 使用户能更方便地处理HTML, css, dom…提供方法, events, 选择器, 并且方便地为网站提供AJAX交互…

FFmpeg交叉编译报错pkg-config not found

ffmpeg交叉编译时报错&#xff1a; WARNING: arm-linux-gnueabihf-pkg-config not found, library detection may fail.不慌&#xff0c;没有就下载嘛&#xff0c;直接install&#xff1a; sudo apt-get install pkg-config-arm-linux-gnueabihf 参考&#xff1a; How To I…

无水蒸汽室的热特性​研究

更多资讯&#xff0c;请关注公众号【莱歌数字】~~ 扩散电阻在从源到汇的整体传热过程中继续起着主导作用。 随着电子元件占地面积小和高功耗的趋势&#xff0c;需要在散热器的底部散热对于降低扩散电阻变得非常重要。 在一些应用中&#xff0c;如高功率激光器&#xff0c;可…

JavaWeb系列十七: jQuery选择器 上

jQuery选择器 jQuery基本选择器jquery层次选择器基础过滤选择器内容过滤选择器可见度过滤选择器 选择器是jQuery的核心, 在jQuery中, 对事件处理, 遍历 DOM和Ajax 操作都依赖于选择器jQuery选择器的优点 $(“#id”) 等价于 document.getElementById(“id”);$(“tagName”) 等价…

Anzo Capital昂首资本独家揭秘,掌握价格行为交易法则,轻松盈利

探索交易成功的秘密!Anzo Capital昂首资本独家揭秘价格行为模式的五大核心步骤&#xff0c;助各位投资者都能把握市场脉搏&#xff0c;轻松盈利。 第一步&#xff0c;精准识别市场趋势&#xff0c;为成功交易奠定坚实基础。 第二步&#xff0c;洞察图表密码&#xff0c;巧妙标…

程序员系统入门大模型的路径和资源,看这篇就够了

本篇文章面向对大模型领域感兴趣&#xff0c;又不知如何下嘴的程序员。 看一下围绕大模型的应用场景和人才需求&#xff1a; **Prompt工程&#xff1a;**基于提示词对大模型的使用&#xff0c;会问问题就行。 **基于大模型的应用&#xff08;狭义的&#xff09;&#xff1a;*…

Avalonia 常用控件二 Menu相关

1、Menu 添加代码如下 <Button HorizontalAlignment"Center" Content"Menu/菜单"><Button.Flyout><MenuFlyout><MenuItem Header"打开"/><MenuItem Header"-"/><MenuItem Header"关闭"/&…

一文讲清楚人工智能集成学习之多模型投票(Voting)

一、集成学习 集成学习是人工智能领域中一种强大的机器学习方法&#xff0c;它通过结合多个学习器来提高整体的预测或分类性能&#xff0c;通常能够比单一模型表现得更好。 1.1 集成学习的原理 集成学习的核心思想是“集思广益”&#xff0c;即通过集合多个模型的预测结果来提…

面向对象修炼手册(二)(消息与继承)(Java宝典)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;面向对象修炼手册 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 前言 消息传递 1 基本概念 1.…

Python19 lambda表达式

在 Python 中&#xff0c;lambda 表达式是一个小型匿名函数&#xff0c;通常用于实现简单、单行的函数。lambda 函数可以接受任意数量的参数&#xff0c;但只能有一个表达式。 基本语法&#xff1a; lambda arguments: expression这里&#xff0c;arguments 是传递给 lambda …

LeetCode —— 只出现一次的数字

只出现一次的数字 I 本题依靠异或运算符的特性&#xff0c;两个相同数据异或等于0&#xff0c;数字与0异或为本身即可解答。代码如下: class Solution { public:int singleNumber(vector<int>& nums) {int ret 0;for (auto e : nums){ret ^ e;}return ret;} };只出…

Kubernetes排错(十)-处理容器数据磁盘被写满

容器数据磁盘被写满造成的危害: 不能创建 Pod (一直 ContainerCreating)不能删除 Pod (一直 Terminating)无法 exec 到容器 如何判断是否被写满&#xff1f; 容器数据目录大多会单独挂数据盘&#xff0c;路径一般是 /var/lib/docker&#xff0c;也可能是 /data/docker 或 /o…

基于CDMA的多用户水下无线光通信(3)——解相关多用户检测

继续上一篇博文&#xff0c;本文将介绍基于解相关的多用户检测算法。解相关检测器的优点是因不需要估计各个用户的接收信号幅值而具有抗远近效应的能力。常规的解相关检测器有运算量大和实时性差的缺点&#xff0c;本文针对异步CDMA的MAI主要来自干扰用户的相邻三个比特周期的特…

wordpress教程自动采集并发布工具

随着互联网的快速发展&#xff0c;越来越多的人开始关注网络赚钱。而对于许多人来说&#xff0c;拥有一个自己的个人网站是一个不错的选择。然而&#xff0c;要让自己的个人网站内容丰富多样&#xff0c;就需要不断地进行更新。那么&#xff0c;有没有一种方法可以让我们轻松地…

服务器数据恢复—raid5热备盘同步失败导致阵列崩溃如何恢复数据?

服务器存储数据恢复环境&故障&#xff1a; 某品牌DS5300存储&#xff0c;包含一个存储机头和多个磁盘柜&#xff0c;组建了多组RAID5磁盘阵列。 某个磁盘柜中的一组RAID5阵列由15块数据盘和1块热备硬盘组建。该磁盘柜中的某块硬盘离线&#xff0c;热备盘自动替换并开始同步…

C语言入门系列:从内存原理看函数的值传递和引用传递

文章目录 一&#xff0c;值传递二&#xff0c;引用传递三&#xff0c;从内存原理看值传递和引用传递的区别1 值传递内存示意图2 引用传递内存示意图 参考文献 函数参数用于向函数传递数据&#xff0c;C语言支持两种传递方式&#xff1a;值传递和引用传递。 一&#xff0c;值传递…