三、双指针(two-point)

news2024/11/26 11:44:03

文章目录

  • 一、算法核心思想
  • 二、算法模型
    • (一)对撞指针
      • 1.[704.二分查找](https://leetcode.cn/problems/binary-search/)
        • (1)思路
        • (2)代码
        • (3)复杂度分析
      • 2.[15.三数之和](https://leetcode.cn/problems/3sum/description/)
        • (1)思路
        • (2)代码
        • (3)复杂度分析
      • 3.[167.两数之和-输入有序数组](https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/description/)
        • (1)思路
        • (2)代码
        • (3)复杂度分析
    • (二)快慢指针
      • 1.[392.判断子序列](https://leetcode.cn/problems/is-subsequence/)
        • (1)思路
        • (2)代码
        • (3)复杂度分析
      • 2.[876.链表的中心节点](https://leetcode.cn/problems/middle-of-the-linked-list/description/)
        • (1)思路
        • (2)代码
        • (3)复杂度分析
    • (三)滑动窗口

一、算法核心思想

双指针是指在遍历对象时,使用两个或多个指针进行遍历及相应的操作。大多用于数组操作,这利用了数组连序性的特点。双指针常用来降低算法的时间复杂度,因为使用两个指针可以避免多层循环。
双指针的三个关键点:

  • 指针的起始位置的选取
  • 指针的移动方向
  • 指针的移动速度
    这三个关键点是双指针算法的核心也是难点!

二、算法模型

(一)对撞指针

对撞指针:左右两个指针,向中间靠拢。

1.704.二分查找

(1)思路

数组有序的前提下用双指针进行二分查找,双指针的作用在于"二分"。首先左右两个指针l r,分别指向数组的首元素和尾元素,判断左右指针中间数组下标mid所对应的数组值与目标值的大小关系,共有如下三种情况:

nums[mid] == target 找到目标值,记录数组下标,结束
nums[mid] > target 中间的值大于目标值,应当在区间 [ l, mid-1 ] 中继续查找
nums[mid] < target 中间值小于目标值,应当在区间 [ mid+1 , r ] 中继续查找

(2)代码

class Solution {
public:
    int search(vector<int>& nums, int target) {
    if (target < nums[0] || target > nums[nums.size() - 1]) {
          return -1;
    }
    int left = 0, right = nums.size() - 1;
    while (left <= right) {
        int med = left + ((right - left) >> 1);
        if (nums[med] > target) {
              right = med - 1;
        }
        else if (nums[med] < target) {
              left  = med + 1;
        }
        else {
            return med;
        }
    }
return -1;
    }
};

(3)复杂度分析

时间复杂度:O(logn)
空间复杂度:O(1)

2.15.三数之和

(1)思路

先将 nums 排序,时间复杂度为 O(NlogN)。

固定 3 个指针中最左(最小)元素的指针 k,双指针 i,j 分设在数组索引 (k,len(nums))两端。

双指针 i , j 交替向中间移动,记录对于每个固定指针 k 的所有满足 nums[k] + nums[i] + nums[j] == 0 的 i,j 组合:

  1. 当 nums[k] > 0 时直接break跳出:因为 nums[j] >= nums[i] >= nums[k] > 0,即 3 个元素都大于 0 ,在此固定指针 k 之后不可能再找到结果了。
  2. 当 k > 0且nums[k] == nums[k - 1]时即跳过此元素nums[k]:因为已经将 nums[k - 1] 的所有组合加入到结果中,本次双指针搜索只会得到重复组合。
  3. i,j 分设在数组索引 (k,len(nums))两端,当i < j时循环计算s = nums[k] + nums[i] + nums[j],并按照以下规则执行双指针移动:
    当s < 0时,i += 1并跳过所有重复的nums[i];
    当s > 0时,j -= 1并跳过所有重复的nums[j];
    当s == 0时,记录组合[k, i, j]至res,执行i += 1和j -= 1并跳过所有重复的nums[i]和nums[j],防止记录到重复组合;

(2)代码

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        // 犹豫不决先排序,步步逼近双指针
        sort(nums.begin(),nums.end());
         vector<vector<int>> res;
        for (int k = 0; k < nums.size() - 2; k ++) {
            if (nums[k] > 0) break;
            if (k > 0 && nums[k] == nums[ k - 1]) continue;
            int i = k + 1,j = nums.size() - 1;
            while (i < j) {
                int sum = nums[k] + nums[i] + nums[j];
                if(sum < 0){
                    while(i < j && nums[i] == nums[++i]);
                } else if (sum > 0) {
                    while(i < j && nums[j] == nums[--j]);
                }
                else {
                    res.push_back(vector<int>{nums[k], nums[i], nums[j]});
                    while(i < j && nums[i] == nums[++i]);
                    while(i < j && nums[j] == nums[--j]);
                } 
            
            }
        }
        return res;
    }
};

(3)复杂度分析

时间复杂度:O(N2)
空间复杂度:O(1)

3.167.两数之和-输入有序数组

(1)思路

  1. 初始化: 双指针 i , j分别指向数组 numbers的左右两端 (俗称对撞双指针)。
  2. 循环搜索: 当双指针相遇时跳出。
  3. 计算和 s=numbers[i]+numbers[j]
    若 s>targets,则指针j向左移动,即执行 j=j−1 。
    若 s<targets ,则指针i向右移动,即执行 i=i+1 。
    若 s=targets ,由于题目要求索引从1开始,因此返回数组 [i+1,j+1]。
    在这里插入图片描述

(2)代码

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
         if (numbers.size() == 0) {
            return {0,0};
        }
        int slow = 0,fast = numbers.size() - 1;
        while (slow < fast) {
            int sum = numbers[slow] + numbers[fast];
            if (sum < target) slow ++;
            else if (sum > target) fast--;
            else return {slow + 1,fast + 1};
        }
        return {};
    }
};

(3)复杂度分析

时间复杂度:O(N) N 为数组 numbers 的长度;双指针共同线性遍历整个数组。
空间复杂度:O(1)

(二)快慢指针

快慢指针:左右两个指针,一块一慢

1.392.判断子序列

(1)思路

(2)代码

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int n = s.length();
        int m = t.length();
        if (s.size() == 0) return true;
        if (n > m) return false;

        int i = 0,j = 0;
        while (i < n && j < m) {
            if (s[i] == t[j]) 
                i ++;
                j ++;
            
        }
        return i == n;
    }
};

(3)复杂度分析

时间复杂度:O(n+m)
空间复杂度:O(1)

2.876.链表的中心节点

(1)思路

考虑借助快慢双指针 fast, slow ,「快指针 fast」每轮走 2 步,「慢指针 slow」每轮走 1 步。
fast 的步数恒为 slow 的 2 倍,因此当快指针遍历完链表时,慢指针就指向链表中间节点。而由于长度为偶数的链表有两个中间节点,因此需要分两种情况考虑:
链表长度为奇数: 当 fast 走到链表「尾节点」时,slow 正好走到「中间节点」。
链表长度为偶数: 当 fast 走到「null」时(越过「尾节点」后),slow 正好走到「第二个中间节点」。
总结以上规律,应在当 fast 遇到或越过尾节点 时跳出循环,并返回 slow 即可。

(2)代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        if (head == nullptr || head -> next == nullptr)
            return head;
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast && fast -> next) {
            fast = fast -> next -> next;
            slow = slow -> next;
        }
        return slow;
    }
};

(3)复杂度分析

时间复杂度:O(N)
空间复杂度:O(1)

(三)滑动窗口

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

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

相关文章

Maven源码阅读(一)

获取源码 apache maven官网地址&#xff1a;https://maven.apache.org/ 不用点击&#xff0c;页面往下滚动&#xff0c;你会看到 找到源码地址&#xff0c;最终都是github&#xff1a;https://github.com/apache/maven 被墙了&#xff0c;可以用gitcode&#xff1a;https:…

Java基础13——异常的捕获与处理

什么是异常 异常是指程序在运行过程中出现的非正常情况&#xff0c;如用户输入错误&#xff0c;除数为零&#xff0c;文件不存在&#xff0c;数组下标越界等。 Java 异常体系结构 所有异常类都是Throwable 类的子类&#xff0c;他派生出两个子类&#xff0c;Error和Exception…

Git学习笔记6

Github分支开发&#xff1a; 第1步&#xff1a;在github上创建一个新的dev分支&#xff1a; 更新了微信的PC版本&#xff0c;发现默认的箭头比以前加粗了&#xff0c;变得更好看了。 create branch: dev from master。 切换到该分支&#xff0c;看到里面的内容跟master分支的…

macOS 运行xxx.command文件提示”无法执行,因为您没有正确的访问权限“解决方法

使用苹果mac电脑运行.command文件时&#xff0c;是否遇到弹出”无法执行&#xff0c;因为您没有正确的访问权限“的窗口&#xff1f;遇到这种问题怎么解决呢&#xff1f;这里小编为大家带来了详细的解决方法&#xff0c;一起来看看吧&#xff01; 解决方法&#xff1a; 方法一…

从零学习开发一个RISC-V操作系统(一)丨计算机组成原理相关知识与RISC-V指令集简介

本篇文章的内容 一、计算机组成原理的相关知识1.1 计算机的硬件组成1.2 程序的存储与执行1.3 程序语言的设计和进化1.4 存储设备的层次结构1.5 操作系统 二、RISC-V的指令集ISA简介2.1 什么是ISA2.2 复杂指令集&#xff08;CISC&#xff09;和精简指令集&#xff08;RISC&#…

halcon算子2、gray_histo

gray_histo 计算直方图 原形&#xff1a;gray_histo(Regions, Image : : : AbsoluteHisto, RelativeHisto) 功能&#xff1a;计算直方图 参数&#xff1a;Regions&#xff1a;区域&#xff0c;要计算的区域&#xff08;在image上的区域&#xff09; Image &#xff1a;要计算的…

【IC设计】ZC706板卡点灯入门(含Verilog代码,xdc约束,实验截图)

文章目录 假定已知的前置知识需求&#xff1a;注意点&#xff1a;代码实现&#xff1a;顶层模块led闪烁模块xdc约束 这篇博客将针对AMD Zynq 7000 SoC ZC706 Evaluation Kit板卡(对应Vivado创建工程时FPGA型号&#xff1a;XC7Z045ffg900-2)实现基本的点灯程序。 假定已知的前置…

JUC中创建的组件 多线程使用“哈希表”

JUC中创建的组件 JUC中创建的组件这些内容都不太常用&#xff0c;偶尔用到面试的时候&#xff0c;偶尔用到&#xff01;到时候自行查找即可&#xff0c;本文主要来快速的过一下&#xff0c;留个印象即可~ JUC&#xff08;java.util.concurrent&#xff09;和多线程相关的工具…

【APUE】文件I/O(系统调用I/O)

目录 1、简介 2、文件描述符的本质 3、文件IO操作 3.1 open 3.2 close 3.3 read 3.4 write 3.5 lseek 4、文件IO与标准IO的区别 5、IO的效率问题 关键补充&#xff1a;进程的内存空间布局 代码区 常量区 全局区 .bss段 .data段 堆区 栈区 6、文件共享 7…

详解【异质图卷积网络 RGCN】回顾:图神经网络问题的三大处理步骤 | 从起源说起,RGCN核心公式解释,两种降低模型参数量/优化的方式,附核心代码实现讲解

书上说了,天下没有不散的宴席,但你别怕,书上还说了,人生何处不相逢。 【纯手工】优质讲解,码字不易,写作不易,走过路过,大家点个赞呗! 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领跑者TOP…

*p 和p区别

*p 和 &p 是在C语言中用于处理指针的两种不同操作。 *p: * 是解引用运算符&#xff0c;用于访问指针所指向的内存地址上的值。如果 p 是一个指针变量&#xff0c;*p 就是该指针所指向的值。举例&#xff1a;如果有 int *p&#xff0c;它是一个指向整数的指针&#xff0c;那…

机器学习西瓜书+南瓜书吃瓜教程第三章学习笔记

本次学习为周老师的机器学习西瓜书谢老师南瓜书Datawhale视频 视频地址 下面为本人的学习笔记&#xff0c;最近很忙还没学多少&#xff0c;之后补&#xff01;&#xff01;&#xff01; u1s1&#xff0c;边看视频边自己手推一遍真的清楚很多&#xff0c;强烈推荐自己手推虽然花…

BUU [HCTF 2018]Hideandseek

BUU [HCTF 2018]Hideandseek 考点&#xff1a; 软连接读取任意文件Flask伪造session/proc/self/environ文件获取当前进程的环境变量列表random.seed()生成的伪随机数种子MAC地址(存放在/sys/class/net/eth0/address文件) 国赛的时候遇见过软连接&#xff0c;这次再来学习一下…

加州法案提议在州一级监管人工智能

加州高级立法者将于周三向州参议院提出一项新的人工智能 (AI) 法案&#xff0c;加强国家和全球监管快速发展技术的努力。 加州参议员斯科特表示&#xff0c;尽管国会多次尝试起草人工智能立法&#xff0c;但加州——硅谷的所在地&#xff0c;世界上大多数顶级人工智能公司都位于…

专治机器学习面试:机器学习各个算法的优缺点!

今天有朋友聊起来&#xff0c;机器学习算法繁多&#xff0c;各个算法有各个算法的特点。以及在不同场景下&#xff0c;不同算法模型能够发挥各自的优点。 今天呢&#xff0c;我把常见的、常用的算法模型进行了一个大概的总结。包括其分支以及各分支的优缺点。 涉及到的算法有…

欢迎参与体素大战活动!

全新的节目即将登陆 The Sandbox 直播流&#xff0c;我们希望你能成为其中的一员&#xff01; 我们正在寻找 20 位 VoxEdit 艺术家来参与这场惊喜的直播活动&#xff0c;本次活动只需要屏幕共享即可。您将在快节奏的环境中进行创作&#xff0c;以竞争「最佳快速设计 Voxel 艺术…

Git学习笔记7

github上多人协助开发演示过程&#xff1a; 张三是项目作者。 李四是一个协同的用户。觉得项目不错&#xff0c;想增加一些功能。 clone与fork的区别&#xff1a; clone&#xff1a;任何人都可以下载作者的整个项目&#xff0c;但是非作者不能push到作者的项目里。&#xff…

【Java 基础篇】Java对象反序列化流详解

在Java编程中&#xff0c;对象序列化和反序列化是常见的操作&#xff0c;用于将对象转换为字节流以便于存储或传输&#xff0c;并从字节流中重新构建对象。本文将重点介绍对象反序列化流的用法和相关概念&#xff0c;帮助基础小白理解这一重要的主题。 什么是对象反序列化&…

javaee之黑马乐优商城4

商品规格与数据结构 下面来说一下数据库的设计与分析 其实对于spu这张表来说&#xff0c;大体设计还是比较好设计的 看一下下面这张图是一个产品的规格参数 上面主体就是一个规格参数&#xff0c;基本信息又是一个规格参数 这里就是涉及到了一个商品的具体信息&#xff0c;sku…

在时代的浪潮中实在前行!实在智能与浪潮通软全面开启战略合作

“新”潮涌动&#xff0c;浪花朵朵。近日&#xff0c;杭州实在智能科技有限公司与浪潮通用软件有限公司签署战略合作协议。双方将加快科研创新&#xff0c;扩大合作领域&#xff0c;共同开拓数智化市场&#xff0c;携手共赢。 浪潮通软平台软件与技术研究院总经理周祥国、实在智…