Leetcode刷题:双指针

算法——双指针技巧总结 双指针技巧可细分分为两类,一类是**快慢指针**,一类是**左右指针**。 前者解决主要解决链表中的问题,比如典型的判定链表中是否包含环、反转链表、找链表的中间节点、删除链表的倒数第 N 个结点;也用来解决数组中的问题,如移动/移除元素、删除有序数组中的重复项。 后者主要解决数组(或者字符串)中的问题,比如二分查找,滑动窗口。 阅读详情

27. 移除元素

27. 移除元素

数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
暴力求解的时间复杂度为 O ( n 2 ) O(n^2) O(n2),双指针可以变为 O ( n ) O(n) O(n)

在这里插入图片描述

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int flag = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] != val) {
                nums[flag] = nums[i];
                flag++;
            } 
        }
        return flag;
    }
};

26. 删除有序数组中的重复项

26. 删除有序数组中的重复项
在这里插入图片描述

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int slowIndex = 0;
        for (int fastIndex = 1; fastIndex < nums.size(); fastIndex++) {
            if (nums[slowIndex] != nums[fastIndex]) {
                slowIndex++;
                nums[slowIndex] = nums[fastIndex];
            }
        }
        return ++slowIndex;

    }
};

通用解法

为了让解法更具有一般性,我们将原问题的「最多保留 1 位」修改为「最多保留 k 位」。

对于此类问题,我们应该进行如下考虑:

由于是保留 k 个相同数字,对于前 k 个数字,我们可以直接保留。
对于后面的任意数字,能够保留的前提是:与当前写入的位置前面的第 k 个元素进行比较,不相同则保留。
举个🌰,我们令 k=1,假设有样例:[3,3,3,3,4,4,4,5,5,5]

  1. 设定变量 idx,指向待插入位置。idx 初始值为 0,目标数组为 []
  2. 首先我们先让第 1 位直接保留(性质 1)。idx 变为 1,目标数组为 [3]
  3. 继续往后遍历,能够保留的前提是与 idx 的前面 1 位元素不同(性质 2),因此我们会跳过剩余的 3,将第一个 4 追加进去。idx 变为 2,目标数组为 [3,4]
  4. 继续这个过程,跳过剩余的 4,将第一个 5 追加进去。idx 变为 3,目标数组为 [3,4,5]
  5. 当整个数组被扫描完,最终我们得到了目标数组 [3,4,5] 和 答案 idx 为 3。
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        return process(nums,1);
    }
    int process(vector<int>& nums,int k){
        int idx = 0;
        for(auto x : nums){
            if(idx < k or nums[idx - k] != x){
                nums[idx++] = x;
            }
        }
        return idx;  
    }
};

283. 移动零

283. 移动零
在这里插入图片描述

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (nums[fastIndex] != 0) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        while (slowIndex < nums.size()) {
            nums[slowIndex++] = 0;
        }

    }
};

减少操作次数:左指针寻找第一个0值,右指针寻找第一个非0值,交换。

344. 反转字符串

在这里插入图片描述

class Solution {
public:
    void reverseString(vector<char>& s) {
        for (int left = 0, right = s.size() - 1; left < right; left++, right--) {
            swap(s[left], s[right]);
        }
    }
};

swap这种简单的函数可以直接用

844. 比较含退格的字符串

在这里插入图片描述
1. 重构字符串

复杂度分析

时间复杂度:O(N+M),其中 N 和 M 分别为字符串 S 和 T 的长度。我们需要遍历两字符串各一次。

空间复杂度:O(N+M),其中 N 和 M 分别为字符串 S 和 T 的长度。主要为还原出的字符串的开销。

class Solution {
public:
    bool backspaceCompare(string S, string T) {
        return build(S) == build(T);
    }

    string build(string str) {
        string ret;
        for (char ch : str) {
            if (ch != '#') {
                ret.push_back(ch);
            } else if (!ret.empty()) {
                ret.pop_back();
            }
        }
        return ret;
    }
};

2. 双指针

class Solution {
public:
    bool backspaceCompare(string S, string T) {
        int i = S.length() - 1, j = T.length() - 1;
        int skipS = 0, skipT = 0;

        while (i >= 0 || j >= 0) {
            while (i >= 0) {
                if (S[i] == '#') {
                    skipS++, i--;
                } else if (skipS > 0) {
                    skipS--, i--;
                } else {
                    break;
                }
            }
            while (j >= 0) {
                if (T[j] == '#') {
                    skipT++, j--;
                } else if (skipT > 0) {
                    skipT--, j--;
                } else {
                    break;
                }
            }
            if (i >= 0 && j >= 0) {
                if (S[i] != T[j]) {
                    return false;
                }
            } else {
                if (i >= 0 || j >= 0) {
                    return false;
                }
            }
            i--, j--;
        }
        return true;
    }
};

977. 有序数组的平方

在这里插入图片描述

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int flag = -1;
        vector<int> ans;
    for (int i = 0; i < nums.size(); i++) {
        if (nums[i] <= 0) {
            flag = i;
        } else {
            break;
        }
    }
        
    int left = flag, right = flag + 1;
    while (left >= 0 || right < nums.size()) {
        if (left < 0) {
            ans.push_back(nums[right] * nums[right]);
            right++;
        } else if (right == nums.size()) {
            ans.push_back(nums[left] * nums[left]);
            left--;
        } else if (nums[left] * nums[left] < nums[right] * nums[right]){
            ans.push_back(nums[left] * nums[left]);
            left--;
        } else {
            ans.push_back(nums[right] * nums[right]);
            right++;
        }
    }
        return ans;
    }
};
  • 先找到正负数交接的位置,然后向两边扩展(并比较大小)

80. 删除有序数组中的重复项 II

在这里插入图片描述

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int slowIndex = 0, k = 2;
        for (auto x: nums){
            if(slowIndex < k or nums[slowIndex - k] != x) {
                nums[slowIndex++] = x;
            }
        }
        return slowIndex;
    }
};

5. 最长回文子串

在这里插入图片描述

class Solution {
public:
    string palindrome(string s, int left, int right) {
        while (left >= 0 && right < s.size() && s[left] == s[right]) {
            left--;
            right++;
        }
        return s.substr(left + 1, right - left - 1);
    }
    string longestPalindrome(string s) {
        int res = 0;
        string ans;
        for (int i = 0; i < s.size(); i++) {
            string s1 = palindrome(s, i, i);
            string s2 = palindrome(s, i, i + 1);
            int cnt1 = s1.size();
            int cnt2 = s2.size();
            if (cnt1 > res) {
                res = cnt1;
                ans = s1;
            }
            if (cnt2 > res) {
                res = cnt2;
                ans = s2;
            }
        }
        return ans;
    }
};
  • 从中间向两边找

557. 反转字符串中的单词 III

在这里插入图片描述

1. 没用双指针

class Solution {
public:
    string reverseWords(string s) {
        int cnt = 0;
        string s1 = "", ans = "";
       
        for (int i = 0; i <= s.size(); i++) {
            if (i == s.size() or s[i] == ' ') {
                ans += s1;
                s1 = "";
                if (i != s.size()) {
                    ans += ' ';
                }
            } else {
                s1 = s[i] + s1;
                
            }
        }
        return ans;
    }
};
  • 时间复杂度:O(N),其中 NN 为字符串的长度。原字符串中的每个字符都会在 O(1) 的时间内放入新字符串中。

  • 空间复杂度:O(N)。我们开辟了与原字符串等大的空间。

2. 双指针

class Solution {
public: 
    string reverseWords(string s) {
        int length = s.length();
        int i = 0;
        while (i < length) {
            int start = i;
            while (i < length && s[i] != ' ') {
                i++;
            }

            int left = start, right = i - 1;
            while (left < right) {
                swap(s[left], s[right]);
                left++;
                right--;
            }
            while (i < length && s[i] == ' ') {
                i++;
            }
        }
        return s;
    }
};

  • 时间复杂度:O(N)。字符串中的每个字符要么在 O(1) 的时间内被交换到相应的位置,要么因为是空格而保持不动。

  • 空间复杂度:O(1)。因为不需要开辟额外的数组。

刷题笔记:力扣第15、16题(排序+双指针) 本文总结了力扣第15题(三数之和)和第16题(最接近的三数之和)的解题思路。对于三数之和问题,作者经历了三重循环暴力解法、排序去重优化、最终采用排序+双指针的O(n²)解法,详细说明了内存分配、去重处理等关键点。最接近的三数之和问题借鉴了类似思路,通过排序后使用双指针逐步逼近目标值,重点阐述了初始化、距离比较和指针移动的逻辑。两题都强调了排序的重要性以及处理重复值的技巧,展现了从基础解法到优化解法的完整思考过程。 阅读详情

相关推荐

前端力扣刷题 | 2:hot100之 双指针

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i!= k ,同时还满足 nums[i] + nums[j] + nums[k] == 0。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i])。不同的三元组是 [-1,0,1] 和 [-1,-1,2]。输入:nums = [-1,0,1,2,-1,-4]输入: nums = [0,1,0,3,12]输出:[[-1,-1,2],[-1,0,1]]

2502_90366796的博客 686

算法——双指针

双指针的简单介绍及四种常用解题模型

雪月清的博客 1万+

Unity URP项目里Post Processing死活没效果?别慌,这5个坑我帮你踩过了

本文详细解析了Unity URP项目中Post Processing效果失效的五大常见问题,包括渲染管线配置冲突、Volume作用域误判、摄像机参数漏配、Layer层级冲突和Profile引用异常。通过实际项目案例和代码示例,提供专业级解决方案,帮助开发者快速排查并修复后处理效果不生效的问题。

weixin_33721427的博客 333

双指针(Two Pointers)用法精讲及解题案例

双指针(Two Pointers):指的是在遍历元素的过程中,不是使用单个指针进行访问,而是使用两个指针进行访问,从而达到相应的目的。如果两个指针方向相反,则称为「对撞指针」。如果两个指针方向相同,则称为「快慢指针」。如果两个指针分别属于不同的数组 / 链表,则称为「分离双指针」。在数组的区间问题上,暴力算法的时间复杂度往往是 $O(n^2)$。而双指针利用了区间「单调性」的性质,可以将时间复杂度降到 $O(n)$。双指针分为「对撞指针」、「快慢指针」、「分离双指针」。对撞指针:两个指针方向相反。

白话机器学习 5177

CondaHTTPError: HTTP 404 NOT FOUND for url

用conda创建新环境时出现问题了。

qq_40713201的博客 1269

ESP32开发环境与微信小程序通信的完整示例

通过搭建稳定的esp32开发环境,实现其与微信小程序间的双向通信,涵盖Wi-Fi连接、数据收发与协议设计,为物联网应用提供低成本解决方案。

weixin_42126677的博客 1130

【算法】双指针

常见的双指针有两种形式,⼀种是对撞指针,⼀种是快慢指针。

春风不语。 4090

VulnHub系列之BadStore_123渗透测试

BADSTORE: 1.2.3 早期网商系统漏洞鉴赏(2004)

Flynn的博客 1629

力扣刷题笔记:合并两个有序链表(LeetCode21)—— 双指针 + 虚拟头节点轻松搞定

题目大家好呀~ 今天来刷链表的经典入门题 ——LeetCode 21 题 “合并两个有序链表”。这题是链表操作的基础,学会它能摸清链表的核心玩法,我们一起一步步拆解,保证学得明明白白~题目题目是说:给你两个非递减顺序排列的链表list1和list2,请你将它们合并成一个同样按非递减顺序排列的新链表。新链表应该由原来两个链表的节点组成(不能新建节点,要复用原节点)。举个栗子:输入:list1 = [1,2,4],list2 = [1,3,4] → 输出:[1,1,2,3,4,4]

2501_90563889的博客 857

告别裸机:在FreeRTOS上为STM32移植SOEM EtherCAT主站的思路与实战

本文详细介绍了在FreeRTOS环境下为STM32移植SOEM EtherCAT主站的工程实践。通过重构任务调度模型、优化内存管理和中断响应,成功将通信周期抖动控制在±100ns以内,满足工业控制系统的严苛实时性要求。文章包含完整的移植策略、性能优化方法和实测数据,为开发者提供了一套可靠的EtherCAT主站解决方案。

weixin_30342363的博客 388

VGG16

VGG16学习笔记

一去不复返的通信er 1177

leetcode刷题:双指针

系列文章目录 leetcode刷题:第一周 leetcode刷题:第二周 leetcode刷题:二分查找 leetcode刷题:双指针 文章目录系列文章目录前言双指针1.删除排序链表中的重复元素 II2.三数之和3.比较含退格的字符串4.区间列表的交集5.盛最多水的容器总结 前言 本次博客内容记录一些有关双指针的算法题。 双指针 1.删除排序链表中的重复元素 II 给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表。 示例 1: 输入:he

weixin_50835854的博客 361

BPMN2.0规范及流程引擎选型方案

本文主要简明扼要的介绍了BPMN2.0规范的内容,可让读者快速度的了解BPMN2.0所涵盖的主要内容和图形样式。也较为详细的阐述了如何绘制标准的流程图,以及在软件层面中如何实现业务流程功能,和技术选型。

比特桃 1万+

双指针1:力扣刷题

题目:力扣167. 两数之和 II - 输入有序数组 给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值 index1 和 index2,其中 index1必须小于index2。 说明: 返回的下标值(index1 和 index2)不是从零开始的。你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。 示例: 输入: numbers = [2, 7, 11, 15], target = 9 输出: [1,2] 解释: 2 与 7 ...

gtjsjj的博客 199

基于改进Unet的多场景水果图像分割与分类研究

本文针对复杂场景下的水果图像分割问题,提出了一种改进Unet模型。通过构建包含8类水果、3种成熟度及多种干扰场景的数据集,采用ResNet50与MobileNetV3作为骨干网络,并在跳跃连接层集成CBAM注意力机制。实验表明,改进模型在测试集上达到91.6%的mIoU,推理速度提升23%,特别在重叠水果和低光照场景表现突出。研究为农业视觉检测提供了有效解决方案。

AI的内部世界的博客 303

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

LeetCode HOT100刷题笔记(一):哈希表, 双指针

ps:笔记和代码按本人理解整理,重思路

学习记录 1702

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

【Leetcode刷题】:三道例题,带你走进双指针的世界

上一篇文章,我们讲到了双指针的解法。 双指针并不是指某种数据结构,而是一种解题方法 基于暴力解法的优化 其指的是利用两个变量在线性结构上遍历,进而可以解决的某些问题。 双指针一般可以用于降低时间复杂度

翼同学 1074

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

leetcode刷题技巧(一):双指针

双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。1,快慢指针扫描过程中,一般有两种情况 1)快指针进行逐个扫描,慢指针依赖快指针进行更新,慢指针则作为一个记录变量辅助完成对快指针扫描到的元素的操作,或者说慢指针只是依赖于快指针单纯进行更新,等到循环的最后才会使用到更新完毕的慢指针的值。(即:快指针与慢指针之间有一个核心的关系,这个关系决定了慢指针如何更新)例如leetcode第206题:反转链表;l

weixin_42445051的博客 1879

LeetCode刷题—数组:双指针

数组题目中有两种双指针的应用:左右指针和快慢指针。 目录如下: 双指针(左右) 双指针(快慢) 相关题目有: 167,两数之和Ⅱ 344,反转字符串 26,删除排序数组中的重复项 27,移除元素 283,移动零 485,最大连续1的个数 540,有序数组中的单一元素 209,长度最小的子数组 双指针(左右) 左右指针在数组中实际是指两个索引值,一般初始化为left = 0, right = nums.length - 1。循环条件为 while(left < right) 。 在上篇文章二分查找中,凸

qq_39181839的博客 270

刷题笔记:力扣第33、34题(双指针)

本文探讨了力扣第33题和第34题的二分查找解法。第33题要求在旋转排序数组中搜索目标值,通过比较中间值与左右边界来判断有序区间,逐步缩小搜索范围。第34题需在排序数组中查找元素的起止位置,通过两次二分查找分别确定左右边界,避免线性扫描的低效。两题均采用二分法的变种,时间复杂度为O(logn),其中第34题特别处理了全数组相同元素的极端情况。文章详细分析了算法思路,并提供了优化后的代码实现。

hanlin03的博客 394

LeetCode刷题3:双指针算法

 双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延伸到多个数组的多个指针。   若两个指针指向同一数组,遍历方向相同且不会相交,则也称为滑动窗口(两个指针包围的区域即为当前的窗口),经常用于区间搜索。   若两个指针指向同一数组,但是遍历方向相反,则可以用来进行搜索,待搜索的数组往往是排好序的。...

欧特GO 268

【LeetCode刷题】:双指针篇(移动零、复写零)

大致题意就是将数组中所有为0的元素往后移,移到数组的末尾,但是所有的非零元素的顺序不能发生改变。所以只要cur遍历完数组后,就说明待处理的区间已经结束,而dest已经将数组划分为两个部分,一部分是非零元素,另一个区间的元素全是零,及完成题目要求。因为要复写的最后一个数是0,所以只要数组越界,就只要将n - 1位置的数据修改成0(n为数组大小),然后cur减一,dest减二即可。对于这道题,就是将数组划分成两个区间,左边为非零元素,右边为零的元素。都加一,当cur指向的值为零时,将零拷贝给新数组,然后。

书山有路勤为径,学海无涯苦作舟。 1851

力扣刷题实战:从零搭建Python环境到双指针算法精解

算法与数据结构是计算机科学的核心基础,其原理在于通过特定的数据组织和操作逻辑,高效解决各类计算问题。掌握经典算法能显著提升代码效率与问题解决能力,在面试、竞赛和工程开发中具有极高价值。对于初学者和进阶者而言,从理解到实现常存在鸿沟,尤其是在处理输入输出、边界条件和环境配置等工程细节时。本文聚焦于双指针这一常用算法模式,通过力扣热门题目“移动零”的实战拆解,演示如何从搭建稳定的本地Python调试环境开始,逐步完成题意分析、手动模拟、代码实现、边界测试与优化迭代的全流程。文中融入了对链表、二叉树等数据结构的本

weixin_29103191的博客 342
上一篇: Leetcode刷题:螺旋矩阵
下一篇: Leetcode刷题:空间缩减思想
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值