C/C++算法-----------------------双指针详解技巧及例题

双指针

  • 基本介绍
  • 降低时间复杂度
    • 降低时间复杂度例题
  • 验证回文串
  • 判断是否为环
  • 反转链表
  • 总结

基本介绍

双指针(two poinnters)实际上是一种算法编程里的一种思想,它更像是一种编程思想,提供看非常高的算法效率,一般来说双指针指的是在遍历对象时使用两个或多个指针遍历进行操作,经常可以用来降低时间复杂度,那么双指针主要分为以下三种:

普通的指针:两个指针往一个方向移动
对撞指针:一般是在有序的情况下两个指针进行面对面的移动,适合解决约束条件的一组元素问题以及字符串反转问题
快慢指针:定义两个指针,一个快指针一个慢指针,用于判断是否为环或者长度的问题很方便

降低时间复杂度

假设我们有一个二维数组arr,大小为n x m,我们要对其进行双层循环遍历,原代码如下:

for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        // 执行操作
    }
}

我们使用双指针优化这个循环。首先,我们定义两个指针p1和p2,初始时分别指向数组的第一个元素。然后,我们使用一个循环来遍历数组,每次迭代更新指针的位置,并执行操作

int p1 = 0; // 第一个指针初始位置
int p2 = 0; // 第二个指针初始位置

while (p1 < n && p2 < m) {
    // 执行操作

    // 更新指针的位置
    p2++;
    if (p2 == m) {
        p2 = 0;
        p1++;
    }
}

这个例子将时间复杂度从O(n^2)降低到了O(n),需要注意的是,双指针优化适用于一些特定情况,例如对称矩阵、上三角矩阵或者二维数组中的某种特定模式,在其他情况下,双指针可能并不适用或者无法有效地优化时间复杂度,因此,在具体问题中,需要根据实际情况判断是否可以使用双指针优化,接下来看一个降低时间复杂度的例题:

降低时间复杂度例题

给定一个有序的递增数组,数组arr={1,3,4,9,11,12,14},找到两个数之和为12,找到一组即可停止
思路:这题最容易想到的就是暴力算法,即直接两层循环嵌套逐个查找,但时间复杂度为:O(n^2)
暴力算法:

for(int i=0;i<n;i++){
	for(int j=0;j<n;j++){
	if(arr[i]+arr[j]==k)
	cout<<arr[i]<<arr[j]>>endl;
	}
}

双指针:这题使用双指针大大降低了时间复杂度

	int i = 0; // 从头开始的索引
	int j = arr.size() - 1; // 从尾开始的索引
	
while (i < j)
{
    if (arr[i] + arr[j] < k)
    {
        i++; // 如果arr[i]和arr[j]的和小于k,则增加i的值
    }
    else if (arr[i] + arr[j] > k)
    {
        j--; // 如果arr[i]和arr[j]的和大于k,则减小j的值
    }
    else
    {
        cout << arr[i] << arr[j] << endl; // 如果arr[i]和arr[j]的和等于k,则输出这两个数并结束循环
        break;
    }
}

return 0;


验证回文串

回文串:是指正向读和反向读都相同的字符串
很经典的题目,用双指针更能事半功倍,这里使用了对撞指针,即两个指针不断往中间靠拢,并对比是否相等,如果相等并且比较完了则代表是回文串

bool isPalindrome(string s) {
    // 定义左右指针
    int left = 0;
    int right = s.length() - 1;
    // 循环进行比较,直到两个指针相遇
    while (left < right) {
        // 跳过非字母和数字字符,只比较字母和数字字符
        if (!isalnum(s[left])) {
            left++;
            continue;
        }
        if (!isalnum(s[right])) {
            right--;
            continue;
        }
        // 将字符转换为小写比较
        if (tolower(s[left]) != tolower(s[right])) {
            return false; // 不是回文串,直接返回false
        }
        // 移动指针继续比较下一对字符
        left++;
        right--;
    }
    return true; // 是回文串,返回true
}

判断是否为环

力扣第LeetCode第141.环形链表:https://leetcode.cn/problems/linked-list-cycle/description/
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false
示例 1:
在这里插入图片描述

class Solution {
public:
    bool hasCycle(ListNode *head) {
        // 检查链表是否为空,如果为空则返回 false
        if (head == NULL)
            return false;
        // 初始化慢指针和快指针,开始位置都是头节点的位置
        ListNode* solt = head;
        ListNode* fast = head->next;
        // 循环遍历链表,直到慢指针和快指针相遇或者快指针到达链表末尾
        while (solt != fast) {
            // 如果快指针到达链表末尾或者倒数第二个节点,则表示链表没有环,返回 false
            if (fast == NULL || fast->next == NULL)
                return false; 
            // 更新慢指针和快指针的位置
            solt = solt->next;
            fast = fast->next->next;
        }
        // 如果循环结束后慢指针和快指针相遇,则表示链表有环,返回 true
        return true;
    }
};

对于上诉的问题我们可以使用双指针中的快慢指针来判断是否为环,定义一个一次走一步的指针solt和一次走两步的指针fast,从起点开始如果存在为环的话fast指针一定能追上solt即再次相遇,若相遇了则代表为环,快慢指针对于判断是否为环的情况下效率很高

反转链表

力扣LCR. 024.反转链表:https://leetcode.cn/problems/UHnkqh/description/
给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。
在这里插入图片描述

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        // 如果链表为空或者只有一个节点,直接返回head(因为不需要反转)
        if (!head || !head->next) {
            return head;
        }

        ListNode* prev = nullptr;  // 用于存储当前节点的前一个节点
        ListNode* curr = head;      // 当前节点
        while (curr) {
            ListNode* nextNode = curr->next;  // 临时保存下一个节点的指针
            curr->next = prev;                // 反转指针,将当前节点指向前一个节点
            prev = curr;                      // 更新prev指针为当前节点
            curr = nextNode;                  // 更新curr指针为下一个节点
        }

        return prev;  // 返回反转后的头节点
    }
};

这道题使用了双指针:
prev指针用于存储当前节点的前一个节点,在代码中初始化为nullptr,表示当前节点没有前一个节点。
1.curr指针用于遍历链表,初始时指向头节点。
2.在循环中,首先将curr的下一个节点保存到临时变量nextNode中,以便后续使用。
3.接着将curr的next指针指向prev,实现了指针的反转。
4.然后更新prev为curr,将curr指针移动到下一个节点nextNode。
重复上述步骤直至遍历完整个链表。

总结

以上就是关于双指针的案例及分析,双指针并不是一种数据结构而是一种很经典的算法思想,可以通过它解决很多问题,其核心思想是设计一个不同速度、不同间距、及不同方向的两个指针来解决问题,在具体问题中,需要根据实际情况判断是否可以使用双指针,最重要的还是多刷题

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

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

相关文章

FL Studio水果软件2025中文破解版注册机

你是否体验过Tomorrowland现场万人蹦迪的的激情&#xff1f;又是否加入过“死墙&#xff08;Mosh pit&#xff1a;一种Bass音乐节常有的娱乐方式&#xff09;”的狂欢盛宴&#xff1f;随着时代发展&#xff0c;以电子音乐为代表的数字音乐已然象征着时尚与潮流。在这股风靡全球…

fiddler展示接口的响应时间

最近项目组迁移了一个新项目&#xff0c;想对比迁移前后访问菜单的响应时间是否有变化&#xff0c;因为没需求文档&#xff0c;所以只有靠fiddler一个个的抓接口来看&#xff0c;开发经理想要看具体每个接口耗时&#xff0c;虽然点击接口&#xff0c;在页面上也能看到接口响应时…

HackTheBox-Starting Point--Tier 2---Markup

文章目录 一 Markup测试过程1.1 打点1.2 权限获取1.3 权限升级 二 题目 一 Markup测试过程 1.1 打点 1.端口扫描 nmap -A -Pn -sC 10.129.95.1922.访问web网站&#xff0c;登录口爆破发现存在弱口令admin&#xff1a;password 3.抓包&#xff0c;发现请求体是XML格式 4.尝试使…

购买阿里云服务器需要多少钱?活动价3000元-5000元的阿里云服务器汇总

购买阿里云服务器需要多少钱&#xff1f;如果我们只有3000元-5000元的预算可以购买什么实例规格和配置的阿里云服务器呢&#xff1f;因为阿里云服务器价格是由实例规格、配置、带宽等众多配置决定的&#xff0c;所以&#xff0c;目前阿里云活动中的价格在3000元-5000元的云服务…

购买阿里云服务器需要多少钱?活动价2000元-3000元的阿里云服务器汇总

购买阿里云服务器需要多少钱&#xff1f;如果我们只有2000元-3000元的预算可以购买什么实例规格和配置的阿里云服务器呢&#xff1f;因为阿里云服务器价格是由实例规格、配置、带宽等众多配置决定的&#xff0c;所以&#xff0c;目前阿里云活动中的价格在2000元-3000元的云服务…

一文揭秘共享wifi二维码项目推广技巧!

随着无线网络的普及和移动互联网的快速发展&#xff0c;共享WiFi已成为人们生活中不可或缺的一部分。共享WiFi二维码项目作为一个独具创意的共享项目&#xff0c;将二维码推广与共享WiFi相结合&#xff0c;不仅可以提升品牌曝光度&#xff0c;还能为用户提供便捷的上网体验。那…

什么是圆锥的准线?

定曲线C叫做锥面的准线&#xff0c;构成曲面的每一条直线叫做母线。

RedHat公司及红帽认证介绍丨红帽认证等级介绍

RedHat公司及红帽认证介绍 红帽公司成立工1993年&#xff0c;是全球首家收入超10亿美元的开源公司&#xff0c;总部位于美国&#xff0c;分支机构遍布全球。红帽公司作头全球领先的开源和Linux系统提供商&#xff0c;其产品已被业界广泛认可并使用&#xff0c;尤其是RHEL系统在…

CentOs 7 PHP安装和配置

目录 1 安装epel源 2 安装REMI源 3 安装yum源管理工具 4 安装PHP7.3 5 启动php服务 6 设置PHP 6.1 查找安装包 6.2 查找PHP安装位置 6.3 查找php配置文件位置 6.4 配置PHP 6.5 设置快捷命令 6.6 查看php版本 6.7 更新php 1 安装epel源 yum -y install epel-release 2 安…

数据仓库基础

ODS&#xff08;Operational Data Store&#xff09;层&#xff1a;操作数据存储。存放原始数据&#xff0c;直接加载原始日志、数据&#xff0c;起到备份数据的作用。数据采用压缩&#xff0c;减少磁盘存储空间。创建分区表&#xff0c;防止后续的全表扫描。 DWD(data wareho…

MatrixOne 支持多样化生态工具

近日&#xff0c;云原生数据库 MatrixOne 支持多样化生态工具&#xff0c;包括&#xff1a;数据集成工具、BI 工具和数据计算引擎这三类生态工具。 云原生数据库使得传统数据库得以充分结合云服务的免运维、高弹性、高可扩展、高可用、高性价比优势&#xff0c;又顺应了云端应…

IDEA远程一键部署SpringBoot到Docker

IDEA是Java开发利器&#xff0c;Spring Boot是Java生态中最流行的微服务框架&#xff0c;docker是时下最火的容器技术&#xff0c;那么它们结合在一起会产生什么化学反应呢&#xff1f; 一、开发前准备 1. Docker安装 可以参考&#xff1a;https://docs.docker.com/install/ 2…

创作者等级终于升到4级了

写了两个月的文章&#xff0c;终于等到4级了。发文纪念一下&#xff1a;

蔚来面试题

文章目录 1.解释脏读/不可重复读/幻读不可重复读和幻读的区别2.索引失效的场景有哪些?3.Explain执行计划用过吗?4.Type字段有什么信息?5.binlog和redolog区别?6.Redis基本数据类型7.有序集合底层实现数据结构?8.跳表插入数据的过程?为什么要这样设计呢?添加流程9.线程池…

关于圆通物流在AppLink上的操作

在使用物流系统时&#xff0c;我们会出现订单变化而导致物流轨迹发生改动&#xff0c;如果反馈不及时会造成额外的工作量以及会出现人为的操作失误&#xff0c;我们尝试借助AppLink来实现圆通物流在发生变化时的信息同步 登录开放平台 复制右侧登录地址登录圆通速递管理后台&…

python数据处理作业10:将图中数据创建一个DataFrame,保存为csv格式,读取该文件,删除体育成绩,并添加语文,数学和英语的平均成绩

每日小语 心无旁骛&#xff0c;专心于事业的追求&#xff0c;就会忘掉许多烦恼&#xff0c;找到许多努力过程中的快乐。——黑格尔 gpt import pandas as pd# 创建DataFrame并保存为CSV文件 data {姓名: [张三, 李四, 王五, 赵六],语文成绩: [80, 90, 85, 75],数学成绩: [7…

<Linux>(极简关键、省时省力)《Linux操作系统原理分析之Linux 进程管理 1》(5)

《Linux操作系统原理分析之Linux 进程管理 1》&#xff08;5&#xff09; 4 Linux 进程管理4.1 Linux 进程概述4.1.1 Linux 进程的组成4.1.2 Linux 进程在处理机上的执行状态4.1.3 进程空间和系统空间4.1.4 进程上下文和系统上下文 4 Linux 进程管理 4.1 Linux 进程概述 4.1.…

恭喜顺利结项 | 开源之夏 2023openGauss项目结项结果公示

祝贺&#xff01; 激动人心的时刻终于到来&#xff01;开源之夏 2023 活动结项审核结果正式出炉。此次&#xff0c;openGauss深度参与活动&#xff0c;发布12个openGauss相关项目&#xff0c;10个项目进入开发周期&#xff0c;最终有8个项目成功结项。恭喜所有成功结项的同学&…

三菱FX3U小项目—机床定时器延时启动

目录 一、项目描述 二、IO口分配 三、项目程序 四、总结 一、项目描述 为了防止工人操作失误&#xff0c;启动按钮需要按住1s后&#xff0c;设备才启动&#xff0c;启动后第一台电机启动10s后第二台电机自动启动&#xff0c;当按下停止按钮时&#xff0c;两台电机同时停止。…

在Broker端进行消息过滤

在Broker端进行消息过滤&#xff0c;可以减少无效消息发送到Consumer&#xff0c;少占用网络带宽从而提高吞吐量。Broker端有三种方式进行消息过滤。 1.消息的Tag和Key 对一个应用来说&#xff0c;尽可能只用一个Topic&#xff0c;不同的消息子类型用Tag来标识&#xff08;每条…