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

news2024/11/27 0:23:10

双指针

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

基本介绍

双指针(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。
重复上述步骤直至遍历完整个链表。

总结

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

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

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

相关文章

Android——gradle插件配置方式——dependencies和plugins

引言 我们知道Android studio 需要gradle插件进行构建和编译&#xff0c;随着AGP的升级&#xff0c;引入gradle插件也发生了变化。旧版本通过build.gradle文件中dependencies代码块引入&#xff0c;新版本通过plugins代码块引入 一、旧版本引入方式dependencies 二、新版本引入…

解决java在idea运行正常,但是打成jar包后中文乱码问题

目录 比如&#xff1a; 打包命令使用utf-8编码&#xff1a; 1.当在idea中编写的程序,运行一切正常.但是当被打成jar包时,执行的程序会中文乱码.产生问题的原因和解决方案是什么呢? 一.问题分析 分别使用idea和jar包形式打印出System中所有的jvm参数---代码如下: public static…

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

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

windows10通过L2TP VPN隧道连接到企业内网

L2TP协议通过为数据包创建一个虚拟通道&#xff0c;将数据从一个网络传输到另一个网络&#xff0c;而不用担心数据在途中被窃取或篡改。它创建一个点对点的隧道&#xff0c;通过封装PPP&#xff08;Point-to-Point Protocol&#xff09;数据包在其内部进行传输&#xff0c;极大…

JSON 格式的接口测试流程【Eolink Apikit】

在进行JSON格式的接口测试时&#xff0c;需要使用工具发送HTTP请求并获取响应。测试工具可以是单独的测试框架&#xff0c;如 Eolink Apikit。测试人员需要根据接口文档和测试用例编写测试脚本&#xff0c;然后运行测试并分析结果&#xff0c;以确保接口的质量和稳定性。 当我…

探索企业主要人员API在金融领域的应用

前言 随着金融科技的不断发展&#xff0c;企业主要人员API在金融领域的应用日益重要。本文将探讨这个话题&#xff0c;并介绍一些关键的应用案例。 企业主要人员API在金融领域的应用 企业人员信息API在金融行业有许多应用。其中一个应用是风险评估。金融机构可以使用API获取…

用requests库下载文件时的挂起问题:一步步诊断与解决方案

在使用 requests 库下载一个大小为125KB的文件时&#xff0c;用户遇到了一个问题&#xff0c;下载进程在代码的特定行挂起了。用户已经检查了操作系统的内存&#xff0c;发现大约有2GB的空闲内存可用。用户正在使用 requests 库的2、28、1版本&#xff0c;并寻求帮助来调试这个…

外汇天眼:什么是非农?非农数据对外汇市场的重要性!

非农数据在外汇市场中扮演着何等关键的角色&#xff1f; 美国非农数据&#xff0c;简称“非农”&#xff0c;具体指排除农业部门、个体户和非盈利机构雇员后的就业相关数据&#xff0c;是反映美国经济实际就业和整体经济状况的关键指标。该数据由美国劳工部劳动统计局每月发布…

Bumptop——3D桌面一键get

今天&#xff0c;博主给大家带来一款免费、炫酷的桌面真理工具——Bumptop。这是一款可使桌面变为极酷3D桌面的软件。采用极酷的3D技术&#xff0c;把图标立体化&#xff0c;同时拖拽动作也是别有动感&#xff0c;充满智慧&#xff0c;可以完全抛弃原先单调的2D和伪3D界面&…

猫罐头哪个牌子好?推荐5款猫罐头品牌排行榜!

选择猫罐头是一项非常重要的任务&#xff0c;绝对不能马虎对待。因为好的猫罐头不仅提供丰富的营养&#xff0c;充足的水分和良好的口感&#xff0c;还能被猫咪轻松吸收。然而&#xff0c;一旦选择错误&#xff0c;不仅无法达到这些效果&#xff0c;还可能产生相反的影响。 作为…

【Axure教程】滑动内容选择器

滑动内容选择器通常是一种用户界面组件&#xff0c;允许用户通过滑动手势在一组内容之间进行选择。这种组件可以在移动应用程序或网页中使用&#xff0c;以提供直观的图片选择体验。 那今天就教大家如何用中继器制作一个滑动内容选择器&#xff0c;我们会以滑动选择电影为案例…

大数据可视化是什么?

大数据可视化是将海量数据通过视觉方式呈现出来&#xff0c;以便于人们理解和分析数据的过程。它可以帮人们发现数据之间的关系、趋势和模式&#xff0c;并制定更明智的决策。大数据可视化通常通过图形、图表、地图和仪表盘等视觉元素来呈现数据。这些元素具有直观、易理解的特…

20套面向对象程序设计选题Java Swing(含教程) (三) 持续更新 建议收藏

20套面向对象程序设计选题Java Swing(含教程) (一) 20套面向对象程序设计选题Java Swing(含教程) (二) 9.交通罚单管理系统 点击查看项目演示及教程视频 源码下载 【功能描述】 ​系统有管理员、交警、驾驶员三个角色&#xff1b; 用户登录后可修改密码​&#xff1b; 管…

Jquery 通过class名称属性,匹配元素

UI自动化过程中&#xff0c;常常需要判断某个元素是否满足条件&#xff0c;再走不通的脚本逻辑&#xff1b;、本文介绍如何通过jquery判断菜单是否展开&#xff0c;来决定是否执行菜单展开脚本&#xff1b;Jquery通过class名称属性&#xff0c;匹配元素 我们先分析&#xff0c;…

MatrixOne 支持多样化生态工具

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

史上超全UI/UX设计素材网站大合集!足足26个!

作为一个设计师&#xff0c;无论是UI设计师还是UX设计师&#xff0c;我们通常每天都会花不少于2个小时的时间来寻找一些新的设计灵感&#xff0c;寻找设计新创意。而现在由于互联网的快速发展&#xff0c;我们可以很容易的就从互联网上获取到各种设计资源和设计灵感&#xff0c…

如何制作keil5的Device pack

概述&#xff1a; 作为一名嵌入式芯片相关行业人员&#xff0c;我们经常需要使用到Device pack, 比如STM32的pack如下图所示&#xff1a; 名词DFP&#xff1a; 设备家族包 DFP Pack组成&#xff1a; Boards (板级支持)Device &#xff08;芯片设备级支持&#xff09;Docum…

【代数学习题3】从零理解数域扩张与嵌入 —— 同构、商环、分裂域与同态映射

数域的结构——数域的扩张、嵌入 写在最前面从零开始的概念合集从零理解数域的扩张和同构概念基本概念同构的概念商环的概念 2 3 \sqrt[3]{2} 32 ​ 有三个 Q \mathbb{Q} Q-嵌入&#xff08;同态映射&#xff09; Q ( 2 3 ) \mathbb{Q}(\sqrt[3]{2}) Q(32 ​) 和 Q [ x ] / (…

信锐-STP专题

生成树故障及排错思路 一、故障现象&#xff1a;业务间歇性中断&#xff0c;汇聚交换机VRRP主备状态频繁切换 VRRP频繁切换&#xff0c;则说明VRRP之间交互的hello报文存在丢失&#xff0c;导致备机认为主机宕机&#xff0c;切换成master模式&#xff1b; 导致hello报文丢失的…

三成青少年“不能忍受没有手机”?数字时代家庭教育新责任考验家长

孩子放学回家第一件事就是刷手机&#xff0c;不仅耽误睡觉而且严重影响学习&#xff0c;没收手机后孩子哭闹不止&#xff0c;更无心学习&#xff0c;这样的熊孩子你遇到过没?近日&#xff0c;福建一家长在社区群里晒出自己的烦恼&#xff0c;结果骤然成为热帖&#xff0c;不少…