力扣hot100 -- 双指针

news2025/1/17 0:21:07

目录

🎂移动零

🌙盛最多水的容器

🌼三数之和

🌼接雨水

前缀和 + 辅助数组

双指针

单调栈


🎂移动零

283. 移动零 - 力扣(LeetCode)

关于swap

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7};
    
    // 交换 vec 中第 1 个和第 7 个元素
    std::swap(vec[0], vec[6]);
    
    // 输出交换后的结果
    for (const auto &num : vec) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}
7 2 3 4 5 6 1

思路

i = 0, j = 0

为了保证 0 都在末尾,且顺序不变

i 指向 0 && j 指向 非0 元素时

交换两者(交换后,nums[x] <= i 都是非0元素; i < nums[x] <= j,都是 0)

力扣核心代码模式,很容易越界,所以vector遍历时,要注意数组下标的问题 

AC  代码

O(n) 

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int i = 0, j = 0, n = nums.size();
        while (j < n && i < n) {
            if (!nums[i] && nums[j]) 
                swap(nums[i], nums[j]);
            if (nums[i] != 0) 
                i++; // 索引自增放后面,防止越界
            j++;
        }
    }
};

🌙盛最多水的容器

11. 盛最多水的容器 - 力扣(LeetCode)

 i = 0, j = n - 1

ans = 距离 * 短板长度

所以,移动长板,距离变小,短板长度不可能变大,ans↓

只能移动短板

O(n) 

class Solution {
public:
    int maxArea(vector<int>& height) {
        int ans = 0, n = height.size();
        int i = 0, j = n - 1;
        while (i < j) {
            int Min = min(height[i], height[j]); // 短板长度
            ans = max(ans, Min * (j - i));
            if (height[i] <= height[j]) i++;
            else j--;
        }
        return ans;
    }
};

🌼三数之和

15. 三数之和 - 力扣(LeetCode)

思路

外层 for 遍历 i

内层 l, r 双指针

if( + +  == 0) 此时 l 和 r 都需要移动

否则,根据 > 0 或 < 0

只移动一个即可

 关于去除重复解

1)
if (i > 0 && nums[i] == nums[i - 1])
    continue;

2)
while (l < r && nums[l] == nums[l + 1])
    l++; // 重复解
while (l < r && nums[r] == nums[r - 1])
    r--; // 重复解

1) 去掉下标 i 的重复解

2) 去掉下标 l, r 的重复解

AC  代码 

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        if (nums.size() < 3) return {}; // 返回空数组

        vector<vector<int> > ans;
        sort(nums.begin(), nums.end()); // 排序

        int n = nums.size();
        for (int i = 0; i < n - 2; ++i) { // i l r
            if (nums[i] > 0) return ans; // 直接返回已有答案

            if (i > 0 && nums[i] == nums[i - 1])
                continue; // 去除重复解

            int l = i + 1, r = n - 1;

            while (l < r) { // 双指针循环判断

                if (nums[i] + nums[l] + nums[r] == 0) {
                    ans.push_back({nums[i], nums[l], nums[r]});
                    while (l < r && nums[l] == nums[l + 1])
                        l++; // 重复解
                    while (l < r && nums[r] == nums[r - 1])
                        r--; // 重复解
                    l++, r--; // 上面while结束后,还需要再移动一次
                }

                else if (nums[i] + nums[l] + nums[r] > 0)
                    r--;
                else
                    l++;
            }
        }
        return ans;
    }
};

🌼接雨水

42. 接雨水 - 力扣(LeetCode)

前缀和 + 辅助数组

辅助数组 l[] 和 r[]

思路

每个柱子能盛水的高度,取决于左边最高和右边最高的柱子的短板

具体就是,min(l[i], r[i]) - h[i]

ans[] 可以省略,直接 res += ...

O(n) * 3

class Solution {
public:
    int trap(vector<int>& h) {
        int n = h.size();
        // ans[] 某一根柱子上的雨水
        vector<int> ans(n), l(n), r(n); // l[] 左边开始最高点, r[] 右边开始最高点
        
        l[0] = h[0], r[n - 1] = h[n - 1]; // 这里的赋值,需要以上面的分配空间为前提
        for (int i = 1; i < n; ++i) 
            l[i] = max(l[i - 1], h[i]); // 上一个l[]或当前h[] 取最大值
        for (int i = n - 2; i >= 0; --i)
            r[i] = max(r[i + 1], h[i]);

        // 对比 h[] 和 l[] / r[] 得到每个柱子接的雨水
        int res = 0;
        for (int i = 1; i < n - 1; ++i) // i==0 或 i==n-1,肯定没有雨水
            if (h[i] < l[i] && h[i] < r[i])
                res += min(l[i], r[i]) - h[i];
        
        return res;
    }
};

双指针

在  前缀和  思路的基础上,用两个指针 left, right 和两个变量 l_max, r_max ,代替两个辅助数组

优化空间复杂度 O(n) --> O(1)

补充解释

前缀和,是顺序遍历所有柱子

而双指针,是两个指针 left(顺序) 和 right(逆序),双向同时向中间遍历

取较小那边的 max 值,用那边的 max 值,减去那边当前柱子的高度的 h[] 

为什么要选  较小  那边的来计算呢?(关键是  当前  两个字)

因为对于 当前的 left 或者 right 来说,较小那边的,一定是制约当前柱子雨水量的  短板

对照着理解下👈

AC  代码

O(n) * 1

class Solution {
public:
    int trap(vector<int>& h) {
        int n = h.size(), res = 0;
        int l = 0, r = n - 1, l_max = 0, r_max = 0;
        while (l < r) { 
            l_max = max(h[l], l_max);
            r_max = max(h[r], r_max);
            // 注意 += 操作完后,对应一边的指针(l 或 r)要移动
            res += (l_max < r_max) ? (l_max - h[l++]) : (r_max - h[r--]); // 减去对应一边的柱子
        }
        return res;
    }
};

单调栈

简介

单调栈 - OI Wiki (oi-wiki.org)

模拟 

单调栈【基础算法精讲 26】_哔哩哔哩_bilibili

思路 

42. 接雨水 - 力扣(LeetCode)

力扣官方视频 -- 4'56开始看,13'56结束(9分钟)

关于代码中 st.pop() 以及 distance(积水宽度) 的计算👇 结合图理解

实际就是对单调栈的模拟,结合  += ( min(h1, h2) - h ) * 宽,即,(短板 - 当前) * 宽度 

过程

1)单调递减栈,储存可能形成 “低洼处” 的柱子

2)遇到更低的柱子,就插入 索引

3)遇到更高的柱子,意味着和前面 更低的,形成了低洼,就进入 内层 while 循环计算积水

4)被弹出的索引 top,h[top] 作为被积水的柱子高度,弹出栈顶后的新栈顶 left,作为 左端点

5)右端点即 当前 for 循环的 i, i - left - 1 即 积水宽度

6)高度取 当前高度 h[i] 和 左端点高度 h[left] 的 较小值

7)积水高度即,min( h[i], h[left] ) - h[top]

8)接着用积水 高度 * 宽度,即得到 += 的积水

9)while ( 栈非空 && 当前高度 h[i] > 栈顶高度 h[st.top()] ) ,重复 (3) ~ (8)

AC  代码

class Solution {
public:
    int trap(vector<int>& h) {
        stack<int> st; // 存储下标
        int n = h.size(), ans = 0;
        for (int i = 0; i < n; ++i) {
            while (!st.empty() && h[st.top()] < h[i]) { // 满足 低洼处 条件
                int top = st.top();
                st.pop(); // 弹出栈顶
                if (st.empty()) break; // 左边没有更高的柱子来形成积水

                int left = st.top();
                int height = min(h[i], h[left]) - h[top];
                int width = i - left - 1;

                ans += height * width;
            }
            // stack, queue 都是 push, vector 才是 push_back
            st.push(i); // 高度降低, 直接插入
        }
        return ans;
    }
};

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

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

相关文章

FastJson、Jackson使用AOP切面进行日志打印异常

FastJson、Jackson使用AOP切面进行日志打印异常 一、概述 1、问题详情 使用FastJson、Jackson进行日志打印时分别包如下错误&#xff1a; 源码&#xff1a; //fastjon log.info("\nRequest Info :{} \n"&#xff0c; JSON.toJSONString(requestInfo)); //jackson …

ubuntu22.04安装部署03: 设置root密码

一、前言 ubuntu22.04 安装完成以后&#xff0c;默认root用户是没有设置密码的&#xff0c;需要手动设置。具体的设置过程如下文内容所示&#xff1a; 相关文件&#xff1a; 《ubuntu22.04装部署01&#xff1a;禁用内核更新》 《ubuntu22.04装部署02&#xff1a;禁用显卡更…

剑指offer——二维数组中的查找(杨氏矩阵)

目录 1. 题目描述2. 常见错误思路3. 分析3.1 特例分析3.2 规律总结 4. 完整代码 1. 题目描述 在一个二维数组中&#xff0c;每一行都按照从左到右递增的顺序排序&#xff0c;每一列都按照从上到下递增的顺序排序。请完成一个函数&#xff0c;输入这样的一个二维数组和一个整数&…

黄金交易策略(Nerve Nnife):大K线对技术指标的影响

我们使用heiken ashi smoothed来做敏感指标&#xff08;大趋势借助其转向趋势预判&#xff0c;但不是马上转变&#xff09;&#xff0c;has默认使用6根k线的移动平均值来做计算的。若在6根k线规范内有一个突变的行情&#xff08;k线很长&#xff09;&#xff0c;那么整个行情的…

基于鲲鹏服务器的LNMP配置

基于鲲鹏服务器的LNMP配置 系统 Centos8 # cat /etc/redhat-release CentOS Linux release 8.0.1905 (Core) 卸载已经存在的旧版本的安装包 # rpm -qa | grep php #查看已经安装的PHP旧版本# rpm -qa | grep php | xargs rpm -e #卸载已经安装的旧版&#xff0c;如果提示有…

CSP-202112-1-序列查询

CSP-202112-1-序列查询 提示&#xff1a;若存在区间[i,j) 满足&#xff1a;f(i)f(i1)…f(j-1)&#xff0c;使用乘法运算 f(i)x(j-i)代替将 f()到 f(j- 1)逐个相加,或可大幅提高算法效率。 一定要看提示&#xff01;单纯的模拟时间会超限&#xff01;算法也是根据提示设计的。 …

【Spring】Bean 的生命周期

一、Bean 的生命周期 Spring 其实就是一个管理 Bean 对象的工厂&#xff0c;它负责对象的创建&#xff0c;对象的销毁等 所谓的生命周期就是&#xff1a;对象从创建开始到最终销毁的整个过程 什么时候创建 Bean 对象&#xff1f;创建 Bean 对象的前后会调用什么方法&#xf…

项目02《游戏-10-开发》Unity3D

【完成本集功能后共享1-10集整套代码】 基于 项目02《游戏-09-开发》Unity3D &#xff0c; 任务&#xff1a;传送至其他场景&#xff0c; 首先在场景中加入传送门&#xff0c; 设置人物标签&#xff0c; using UnityEngine; using UnityEngine.SceneManagement; u…

Tomcat 原理分析

1、Tomcat 的组成 如下图&#xff1a; Tomcat组成 Server&#xff1a; Tomcat 封装的、对外提供完整的、基于组件的 web 服务&#xff0c;包含 Connectors、Container 两个核心组件&#xff0c;以及多个功能组件&#xff0c;各个 Service 之间是独立的&#xff0c;但是共享 同…

C#,十进制展开数(Decimal Expansion Number)的算法与源代码

1 十进制展开数 十进制展开数&#xff08;Decimal Expansion Number&#xff09;的计算公式&#xff1a; DEN n^3 - n - 1 The decimal expansion of a number is its representation in base -10 (i.e., in the decimal system). In this system, each "decimal place…

2024牛客寒假算法基础集训营2部分题解

Tokitsukaze and Bracelet 链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 《绯染天空》是一款由 key 社与飞机社共同开发的角色扮演游戏&#xff0c;剧情内容由著名的剧本作家麻枝准编写。它是一款氪金手游&#xff0c;但也有 st…

C++自定义函数详解

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 铁汁们新年好呀&#xff0c;今天我们来了解自定义函数。 文章目录 1.数学中的函数 2.什么是自定义函数 3.自定义函数如何使用&#xff1f; 4.值传递和引用传递&#xff08;形参和实参区分&#xff09; …

ES实战-book笔记1

#索引一个文档,-XPUT手动创建索引, curl -XPUT localhost:9200/get-together/_doc/1?pretty -H Content-Type: application/json -d {"name": "Elasticsearch Denver","organizer": "Lee" } #返回结果 {"_index" : "g…

极狐GitLab 与钉钉的集成实践

DingTalk OAuth 2.0 OmniAuth provider * 引入于 14.5 版本。 您可以使用您的钉钉账号登录极狐GitLab。 登录钉钉开放平台&#xff0c;创建应用。钉钉会生成一个客户端 ID 和密钥供您使用。 登录钉钉开放平台。 在顶部栏上&#xff0c;选择 应用程序开发 > 企业内部开发&am…

修改SpringBoot中默认依赖版本

例如SpringBoot2.7.2中ElasticSearch版本是7.17.4 我希望把它变成7.6.1

物联网数据隐私保护技术

在物联网&#xff08;IoT&#xff09;的世界中&#xff0c;无数的设备通过互联网连接在一起&#xff0c;不断地收集、传输和处理数据。这些数据有助于提高生产效率、优化用户体验并创造新的服务模式。然而&#xff0c;随着数据量的剧增&#xff0c;数据隐私保护成为了一个不能忽…

CSP-202012-1-期末预测之安全指数

CSP-202012-1-期末预测之安全指数 题目很简单&#xff0c;直接上代码 #include <iostream> using namespace std; int main() {int n, sum 0;cin >> n;for (int i 0; i < n; i){int w, score;cin >> w >> score;sum w * score;}if (sum > 0…

力扣刷题之旅:进阶篇(三)

力扣&#xff08;LeetCode&#xff09;是一个在线编程平台&#xff0c;主要用于帮助程序员提升算法和数据结构方面的能力。以下是一些力扣上的入门题目&#xff0c;以及它们的解题代码。 --点击进入刷题地址 一、动态规划&#xff08;DP&#xff09; 首先&#xff0c;让我们来…

Linux中ps/kill/execl的使用

ps命令&#xff1a; ps -aus或者ps -ajx或者 ps -ef可以查看有哪些进程。加上 | grep "xxx" 可以查看名为”xxx"的进程。 ps -aus | grep "xxx" kill命令&#xff1a; kill -9 pid 杀死某个进程 kill -l 查看系统有哪些信号 execl函数&#…

C#上位机与三菱PLC的通信05--MC协议之QnA-3E报文解析

1、MC协议回顾 MC是公开协议 &#xff0c;所有报文格式都是有标准 &#xff0c;MC协议可以在串口通信&#xff0c;也可以在以太网通信 串口&#xff1a;1C、2C、3C、4C 网口&#xff1a;4E、3E、1E A-1E是三菱PLC通信协议中最早的一种&#xff0c;它是一种基于二进制通信协…