算法打卡12天

news2025/6/6 13:59:32

19.链表相交

(力扣面试题 02.07. 链表相交)

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

图示两个链表在节点 c1 开始相交**:**

img

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

示例 1:

img

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

img

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

img

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

解题思路

题目要求找到两个单链表的第一个公共节点。核心思路是利用链表长度的差值来对齐链表尾部,从而让两个指针能够同时遍历并找到交点。

  1. 计算链表长度
    • 遍历链表 A 和链表 B,分别计算它们的长度 lenAlenB
  2. 对齐链表尾部
    • 如果链表 B 比链表 A 长,交换它们的长度和头指针,确保链表 A 是较长的链表。
    • 计算长度差 gap = lenA - lenB,让较长的链表(链表 A)的指针先走 gap 步,这样两个链表的尾部对齐。
  3. 同时遍历链表
    • 从对齐后的起点开始,同时遍历链表 A 和链表 B。
    • 如果两个指针相遇(即 curA == curB),说明找到了第一个公共节点,直接返回该节点。
    • 如果遍历完都没有相遇,则返回 NULL,表示两个链表没有交点。

代码

#include <iostream>
#include <algorithm>

struct ListNode
{
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

class Solution
{
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
    {
        // 交点不是数值相等,而是指针相等
        ListNode *curA = headA;
        ListNode *curB = headB;
        int lenA = 0;
        int lenB = 0;
        // 求链表A的长度
        while (curA != NULL)
        {
            lenA++;
            curA = curA->next;
        }
        // 求链表B的长度
        while (curB != NULL)
        {
            lenB++;
            curB = curB->next;
        }
        // 重新指向链表A和B的头节点   curA 和 curB现在是NULL

        curA = headA;
        curB = headB;
        // 如果链表B比链表A长,交换它们的长度和头指针
        if (lenB > lenA)
        {
            std::swap(lenA, lenB);
            std::swap(curA, curB);
        }
        // 求长度差
        int gap = lenA - lenB;

        // 让curA先走gap步,这样两个链表的尾部对齐
        while (gap--)
        {
            curA = curA->next;
        }

        // 同时遍历curA和curB
        while (curA != NULL)
        {
            // 如果两个指针相遇,说明找到了第一个公共节点(返回指针)
            if (curA == curB)
            {
                return curA;
            }
            // 移动链表的指针
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};
  • 时间复杂度:O(n + m)
  • 空间复杂度:O(1)

39.逆波兰表达式求值

(力扣150题)

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

  • 有效的算符为 '+''-''*''/'
  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  • 两个整数之间的除法总是 向零截断
  • 表达式中不含除零运算。
  • 输入是一个根据逆波兰表示法表示的算术表达式。
  • 答案及所有中间计算结果可以用 32 位 整数表示。

示例 1:

输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

示例 2:

输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

示例 3:

输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

提示:

  • 1 <= tokens.length <= 104
  • tokens[i] 是一个算符("+""-""*""/"),或是在范围 [-200, 200] 内的一个整数

逆波兰表达式:

逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。

  • 平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 )
  • 该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * )

逆波兰表达式主要有以下两个优点:

  • 去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
  • 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

解题思路

  1. 初始化栈
    • 使用一个栈(stack<long long>)来存储操作数。栈的类型为 long long,以支持较大的整数运算。
  2. 遍历表达式
    • 遍历 RPN 表达式的每个元素(tokens)。每个元素可以是数字或运算符。
  3. 处理数字
    • 如果当前元素是数字(即不是运算符),将其转换为 long long 类型并压入栈中。这里使用 std::stoll 函数将字符串转换为数字。
  4. 处理运算符
    • 如果当前元素是运算符(+, -, *, /),需要从栈中弹出两个操作数。
    • 检查栈中是否有足够的操作数(至少两个)。如果不足,说明表达式不合法,打印错误信息并返回错误码 0
    • 弹出栈顶的两个操作数(num1num2),注意顺序:num1 是栈顶元素,num2 是第二个元素。
    • 根据运算符对两个操作数进行运算,并将结果压入栈中。
  5. 检查最终结果
    • 遍历结束后,检查栈中是否只剩下一个元素。如果栈中不止一个元素,说明表达式不合法,打印错误信息并返回错误码 0
    • 如果栈中只剩下一个元素,该元素即为 RPN 表达式的结果。
  6. 返回结果
    • 弹出栈顶元素并返回其值。

代码实现的逻辑

  • 栈的使用:栈用于存储操作数,支持后进先出(LIFO)的操作,非常适合处理 RPN 表达式。
  • 错误处理:通过检查栈的大小来确保每次运算符操作时有足够的操作数,避免运行时错误。
  • 最终检查:确保栈中只剩下一个元素,验证 RPN 表达式的合法性
#include <iostream>
#include <stack>
#include <string>
#include <vector>
#include <cstdlib>
using namespace std;

class Solution
{

public:
    int evalRPN(vector<string> &tokens)
    {
        // 栈
        stack<long long> st;
        for (int i = 0; i < tokens.size(); i++)
        {
           
            // 如果遍历到运算符
            if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/")
            {
                 // 检查栈中是否有足够的元素
                 if(st.size() < 2)
                 {
                    perror("Error: Not enough operands for the operator");
                    return 0;
                 }
                // 获取栈顶元素 就是要运算的数字
                long long num1 = st.top();
                // 弹出栈
                st.pop();
                long long num2 = st.top();
                st.pop();
                // 计算数值 再加入栈里面
                if (tokens[i] == "+")
                {
                    st.push(num2 + num1);
                }
                if (tokens[i] == "-")
                {
                    st.push(num2 - num1);
                }
                if (tokens[i] == "*")
                {
                    st.push(num2 * num1);
                }
                if (tokens[i] == "/")
                {
                    st.push(num2 / num1);
                }
            }
            // 如果遍历到数字 加入栈
            else
            {
                //  std::stoll,表示 “string to long long”,也就是将字符串转换为 long long 类型的整数。
                st.push(std::stoll(tokens[i]));
            }
        }
        // 如果最后结果不是一个元素 表达式不合法
        if(st.size() != 1)
        {
            perror("Error: Invalid RPN expression");
            return 0;
        }
        // 统计最后的数值 此时栈就一个元素(结果)
        long long result = st.top();
        //  弹出
        st.pop();
        return result;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

40.滑动窗口最大值

(力扣239题)

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

示例 2:

输入:nums = [1], k = 1
输出:[1]

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
  • 1 <= k <= nums.length

解题思路

本题的目标是求解滑动窗口中的最大值。滑动窗口的大小为 k,随着窗口的滑动,我们需要高效地找到每个窗口的最大值。为了实现这一目标,我们使用了一个自定义的单调队列(Mydeque)来维护窗口内的元素。

单调队列的特性
  • 单调递减:队列中的元素从大到小排列。队列头部始终是当前窗口的最大值。
  • 高效移除:当窗口滑动时,窗口外的元素需要被移除。通过 pop 方法,我们检查队列头部是否是窗口外的元素,如果是,则移除。
  • 动态维护:当新元素加入窗口时,通过 push 方法,将队列尾部所有小于新元素的值移除,确保队列始终保持单调递减。
算法步骤
  1. 初始化窗口:将前 k 个元素加入单调队列。
  2. 记录第一个窗口的最大值:队列头部即为第一个窗口的最大值。
  3. 滑动窗口
    • 移除窗口外的元素:通过 pop 方法,移除队列头部的窗口外元素。
    • 加入新元素:通过 push 方法,将新元素加入队列,并维护队列的单调性。
    • 记录当前窗口的最大值:队列头部即为当前窗口的最大值。
  4. 返回结果:所有窗口的最大值存储在结果数组中。
#include <iostream>
#include <deque>
#include <vector>
class Solution
{
    // 单调队列(从大到小)
private:
    class Mydeque
    {
    public:
        // 使用deque来实现单调队列
        std::deque<int> que;
        // 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则
        // 同时pop之前判断队列当前是否为空
        void pop(int value)
        {
            if (!que.empty() && value == que.front())
            {
                que.pop_front();
            }
        }

        void push(int value)
        {
            // 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
            // 这样就保持了队列里的数值是单调从大到小的了。
            while (!que.empty() && value > que.back())
            {
                // 列尾部的值在滑动窗口中不再有用,可以移除。
                que.pop_back();
            }
            // 没有的话就单纯把值添加到单调队列里
            que.push_back(value);
        }

        // 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
        int front()
        {
            return que.front();
        }
    };

public:
    std::vector<int> maxSlidingWindow(std::vector<int> &nums, int k)
    {
        Mydeque que;
        std::vector<int> result;
        // 先将前k的元素放进队列
        for (int i = 0; i < k; i++)
        {
            que.push(nums[i]);
        }
        // result 记录前k的元素的最大值
        result.push_back(que.front());
        for (int i = k; i < nums.size() ; i++)
        {
             // 滑动窗口移除最前面元素
            que.pop(nums[i - k]);
             // 滑动窗口前加入最后面的元素
             que.push(nums[i]);
             // 记录对应的最大值
             result.push_back(que.front());

        }
        return result;
    }
};
时间复杂度

每个元素最多被加入和移除队列一次,因此时间复杂度为 O(n),其中 n 是数组的长度。

空间复杂度

单调队列的空间复杂度为 O(k),因为队列中最多存储窗口大小的元素。

通过单调队列,我们能够高效地解决滑动窗口的最大值问题,避免

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

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

相关文章

蓝桥杯国赛训练 day1 Java大学B组

目录 k倍区间 舞狮 交换瓶子 k倍区间 取模后算组合数就行 import java.util.HashMap; import java.util.Map; import java.util.Scanner;public class Main {static Scanner sc new Scanner(System.in);public static void main(String[] args) {solve();}public static vo…

PyTorch——非线性激活(5)

非线性激活函数的作用是让神经网络能够理解更复杂的模式和规律。如果没有非线性激活函数&#xff0c;神经网络就只能进行简单的加法和乘法运算&#xff0c;没法处理复杂的问题。 非线性变化的目的就是给我们的网络当中引入一些非线性特征 Relu 激活函数 Relu处理图像 # 导入必…

OPenCV CUDA模块目标检测----- HOG 特征提取和目标检测类cv::cuda::HOG

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::HOG 是 OpenCV 的 CUDA 模块中对 HOG 特征提取和目标检测 提供的 GPU 实现。它与 CPU 版本的 cv::HOGDescriptor 类似&#xff0c;但利…

MATLAB读取文件内容:Excel、CSV和TXT文件解析

MATLAB读取文件内容&#xff1a;Excel、CSV和TXT文件解析 MATLAB 是一款强大的数学与工程计算工具&#xff0c;广泛应用于数据分析、模型构建和图像处理等领域。在处理实际问题时&#xff0c;我们常常需要从文件中读取数据进行分析。本文将介绍如何使用 MATLAB 读取常见的文件…

Spring MVC 之 异常处理

使用Spring MVC可以很灵活地完成数据的绑定和响应&#xff0c;极大的简化了Java Web的开发。但Spring MVC提供的便利不仅仅如此&#xff0c;使用Spring MVC还可以很便捷地完成项目中的异常处理、自定义拦截器以及文件上传和下载等高级功能。本章将对Spring MVC提供的这些高级功…

ubuntu24.04 使用apt指令只下载不安装软件

比如我想下载net-tools工具包及其依赖包可以如下指令 apt --download-only install net-tools 自动下载的软件包在/var/cache/apt/archives/目录下

计算机网络安全问答数据集(1788条) ,AI智能体知识库收集! AI大模型训练数据!

继续收集数据集&#xff0c;话不多说&#xff0c;见下文&#xff01; 今天分享一个计算机网络安全问答数据集&#xff08;1788条)&#xff0c;适用于AI大模型训练、智能体知识库构建、安全教育系统开发等多种场景&#xff01; 一、数据特点 结构清晰&#xff1a;共计1788条&…

WinCC学习系列-高阶应用(WinCC REST通信)

WinCC作为一个经典SCADA系统&#xff0c;它是OT与IT数据无缝集成桥梁&#xff0c;自WinCC7.5版本开始&#xff0c;可以直接提供Rest服务用于其它系统数据访问和操作。 WinCC REST 服务允许外部应用程序访问 WinCC 数据。 外部应用程序可以通过 REST 接口读取和写入 WinCC 组态…

使用交叉编译工具提示stubs-32.h:7:11: fatal error: gnu/stubs-soft.h: 没有那个文件或目录的解决办法

0 前言 使用ST官方SDK提供的交叉编译工具、cmake生成Makefile&#xff0c;使用make命令生成可执行文件提示fatal error: gnu/stubs-soft.h: 没有那个文件或目录的解决办法&#xff0c;如下所示&#xff1a; 根据这一错误提示&#xff0c;按照网上的解决方案逐一尝试均以失败告…

macOS 连接 Docker 运行 postgres,使用navicat添加并关联数据库

下载 docker注册一个账号&#xff0c;登录 Docker创建 docke r文件 mkdir -p ~/.docker && touch ~/.docker/daemon.json写入配置&#xff08;全量替换&#xff09; {"builder": {"gc": {"defaultKeepStorage": "20GB",&quo…

指针的使用——基本数据类型、数组、结构体

1 引言 对于学习指针要弄清楚如下问题基本可以应付大部分的场景&#xff1a; ① 指针是什么&#xff1f; ② 指针的类型是什么&#xff1f; ③ 指针指向的类型是什么&#xff1f; ④ 指针指向了哪里&#xff1f; 2 如何使用指针 任何东西的学习最好可以总结成一种通用化的…

TK海外抢单源码/指定卡单

​ 抢单源码&#xff0c;有指定派单&#xff0c;打针&#xff0c;这套二改过充值跳转客服 前端vue 后端php 两端分离 可二开 可以指定卡第几单&#xff0c;金额多少&#xff0c; 前后端开源 PHP7.2 MySQL5.6 前端要www.域名&#xff0c;后端要admin.域名 前端直接静态 伪静…

【Linux】Linux 环境变量

参考博客&#xff1a;https://blog.csdn.net/sjsjnsjnn/article/details/125533127 一、环境变量 1.1 基本概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&am…

OpenCV在图像上绘制文字示例

OpenCV计算机视觉开发实践&#xff1a;基于Qt C - 商品搜索 - 京东 OpenCV中除了提供绘制各种图形的函数外&#xff0c;还提供了一个特殊的绘制函数&#xff0c;用于在图像上绘制文字。这个函数是putText()&#xff0c;它是命名空间cv中的函数&#xff0c;其声明如下&#xff…

为什么要选择VR看房?VR看房有什么优点?

VR看房&#xff1a;革新传统&#xff0c;重塑体验 在当今社会&#xff0c;虚拟现实&#xff08;VR&#xff09;技术正以前所未有的速度渗透到我们生活的各个领域&#xff0c;其中VR看房作为房地产领域的重要创新。本文将讨论为什么要选择VR看房以及VR看房的主要优点&#xff0…

pytorch基本运算-范数

引言 前序学习进程中&#xff0c;已经对pytorch基本运算有了详细探索&#xff0c;文章链接有&#xff1a; 基本运算 广播失效 乘除法和幂运算 hadamard积、点积和矩阵乘法 上述计算都是以pytorch张量为运算元素&#xff0c;这些张量基本上也集中在一维向量和二维矩阵&#x…

Transformer实战——词嵌入技术详解

Transformer实战——词嵌入技术详解 0. 前言1. 词嵌入基础2. 分布式表示3. 静态嵌入3.1 Word2Vec3.2 GloVe 4. 使用 Gensim 构建词嵌入5. 使用 Gensim 探索嵌入空间6. 动态嵌入小结系列链接 0. 前言 在本节中&#xff0c;我们首先介绍词嵌入的概念&#xff0c;然后介绍两种实现…

[pdf、epub]300道《软件方法》强化自测题业务建模需求分析共257页(202505更新)

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 在本账号CSDN资源下载&#xff0c;或者访问链接&#xff1a; http://www.umlchina.com/url/quizad.html 如果需要提取码&#xff1a;umlc 文件夹中的“300道软件方法强化自测题2025…

Vue3入门指南:从零到精通的快速上手

齐爷学vue3 一、Vue3入门 vite&#xff1a;前端构架工具&#xff0c;构建速度快于webpack。轻量快速、对TS&#xff0c;JSX&#xff0c;CSS开箱即用、按需编译。 创建Vue3工程 1.在想要创建Vue3的位置打开cmd&#xff0c;执行如下命令。 npm create vuelatest 2.功能只选择…

吴恩达MCP课程(5):mcp_chatbot_prompt_resource.py

前提条件&#xff1a; 1、吴恩达MCP课程&#xff08;5&#xff09;&#xff1a;research_server_prompt_resource.py 2、server_config_prompt_resource.json文件 {"mcpServers": {"filesystem": {"command": "npx","args"…