【LeetCode热题100】双指针

news2024/12/23 1:12:28

class Solution {
public:
    void moveZeroes(vector<int>& nums) 
    {
        int dst = -1,cur = 0;
        while(cur<nums.size())
        {
            if(nums[cur] == 0)
            {
                cur++;
            }
            else
            {
                swap(nums[dst+1],nums[cur]);
                cur++;dst++;
            }
        }
    
    }
};

题目分析:对于数组分块/数组划分的问题,我们可以使用双指针算法进行求解(这道题的“指针”是数组下标)。我们使用两个“指针”dst和cur,将数组划分为三个区间:[0,dst]已处理过且不为0的区间、[dst+1,cur-1]已处理过且为0的区间、[cur,n-1]待处理的区间。两个指针的作用:cur--从左往右扫描数组,遍历数组;dst--已处理的区间内,非零元素的最后一个位置。cur向后依次遍历,可能会有两种情况:1.cur指向的元素为0,cur++  2.cur指向的元素不为0,swap(nums[dst+1],nums[cur]),cur++,dst++。由于刚开始时没有已处理的区间,所以dst=-1,cur=0。

 

class Solution {
public:
    void duplicateZeros(vector<int>& arr) 
    {
        //1.找到要复写的最后一个数
        int cur = 0,dst = -1;
        while(cur < arr.size())
        {
            if(arr[cur] == 0) dst+=2;
            else dst++;
            if(dst >= arr.size()-1)
            {
                break;
            }
            cur++;
        }
        //处理一下边界情况:dst加了两下,加过了
        if(dst == arr.size())
        {
            arr[dst-1]=0 ;
            dst-=2;
            cur--;
        }
        //2.从后向前复写
        while(cur >= 0)
        {
            if(arr[cur] == 0)
            {
                arr[dst]=arr[dst-1]=0;
                dst -= 2;
                cur--;
            }
            else
            {
                arr[dst]=arr[cur];
                dst--;
                cur--;
            }
        }
    }
};

 题目分析:这道题是对数组元素进行操作,可以考虑双指针算法。整体思路就是先根据“异地”操作,然后优化成双指针下的“就地”操作。我们首先想到的是另外创建一个数组,进行异地操作,一个指针cur指向原数组,另一个指针dst指向新建数组,cur依次遍历每一个数,按照要求在新建数组进行复写。然而,这样的做法明显不符合“就地操作”的要求,因此,我们需要让cur和dst都指向原数组,如果它们都指向了起始位置,这样遇到复写0之后就会覆盖后面的元素,肯定会出问题。所以我们需要从后往前复写,dst需要指向数组最后一个元素,cur要指向最后一个“复写”的数,因此,我们总共有两步:1.先找到最后一个“复写”的数  2.“从后向前”完成复写操作。经过多次实践,可以通过如下方法找到最后一个复写的数:1)先判断cur位置的值 2)决定dest向后移动一步或者两步 3)判断一下dest是否已经结束 4)cur++  在开始之前,我们需要先让dst指向-1,cur指向0(因为最终dst指向最后一个要复写的位置,刚开始的时候并不知道最后一个位置在哪,因此设为-1),当这个循环退出时,cur指向了最后一个“复写”的数。此外,在找最后一个要复写的数的过程中,我们还可能因为cur最后一次指向0,dst需要加两次,跳到了arr的外面,即下标为n的位置,对于这种情况,我们需要单独处理一下,让arr[n-1]=0,然后dst回退两次,cur--即可。

class Solution {
public:
    int bitSum(int n)
    {
        int sum = 0;
        while(n)
        {
            int lastbit = n%10;
            sum += lastbit*lastbit;
            n /= 10;
        }
        return sum;
    }

    bool isHappy(int n) 
    { 
        int slow = n, fast = bitSum(n);
        while(slow != fast)
        {
            slow = bitSum(slow);
            fast = bitSum(bitSum(fast));
        }

        return slow == 1;
    }
};

题目分析:给定一个正整数,按照题目给定的过程重复计算,会有两种结果:其一是这个数在某一次变成1,那么之后会一直循环1;第二种是这个数会在某个圈里无限循环,但是不是1。因此,一定会进入循环(鸽巢原理),我们可以通过判断当进入循环后,这个值是不是1来确定这个数是不是快乐数。

那么如何找到进入循环的某一个数呢?这个就需要用到快慢指针的思想:

(一)定义快慢指针

(二)慢指针每次向后移动一步,快指针每次向后移动两步

(三)判断相遇时候的值即可

class Solution {
public:
    int maxArea(vector<int>& height) 
    { 
        int max= 0;
        int i =0,j=height.size()-1;
        int v = 0;//体积
        while( i < j)
        {
            v = (j - i) * (height[i]<height[j]?height[i]:height[j]);
            if(v > max) max = v;
            if(height[i] < height[j]) 
                i++;
            else 
                j--;
        }

        return max;

    }
};

题目分析:第一种方法就是暴力求解,遍历每一个可能得组合,计算出它们的体积的最大值,但是这种方法是运行超时的,因此我们还要另辟蹊径。

第二种方法比较巧妙,我们先来看一个简单例子,如果数组是[6,2,5,4],那我们先计算最外面两个数的组合:6和4,宽度是3,高度是max(6,4)=4,体积=3*4=12,我们再往里面深入,如果是4和2组合,首先它们的宽度变成2(往里深入宽度必定减少),高度变成2(高度必定是≤4,),很显然,宽度必定减少,高度最多保持不变,那么4和往里面的树组合体积必定减少,因此就不用考虑4和里面任何数的组合,所以直接忽略4(两侧--6和4中较小的那一个值),数组变成[6,2,5],然后继续依照上面逻辑,先计算出最外侧两个数的体积,和上一个体积比较,最后直到整个数组被遍历完!

class Solution {
public:
    int triangleNumber(vector<int>& nums) 
    {
        //1.优化
        sort(nums.begin(),nums.end());
        int size = nums.size();
        int fixi = size-1;
        int left = 0;
        int right = fixi-1;
        int ret = 0;
        while(fixi > 1)
        {
            while(left < right)
            {
                if(nums[left] + nums[right] > nums[fixi])
                {
                    ret += right - left;
                    right--;
                }
                else
                {
                  left++;
                }
            }
            fixi--;
            left = 0;
            right = fixi-1;
        }

        return ret;
    }
        
};

题目分析:如果要判断的三个数是有序的(a  <  b  <  c),那么只需要判断一次即可,就是判断 a+b是否大于c,因为b+c肯定大于a,a+c也肯定大于b。

这道题有两种解法,一种是暴力解法,遍历这个数组得到三个数,依次判断是否符合,这种解法的时间复杂度很高。另外一种解法,也是利用双指针的思想,比如给出的数组是[2,3,5,5,7,8,9,12],先固定一个最大的c,也就是最后的12,然后让left指向剩下的最小的,即2,再让right指向剩下最大的,即9,判断left+right和固定最大值c=12的大小关系,那么就有两种情况:

        1.left+right>c,那么right和left与right中间的任意一个组合都符合条件,不用继续遍历中间的,符合的组数为right-left,再让right--;

        2.left+right<=c,那么,left和left与right中间任意一个组合都不符合条件,left++;

当left=right后,这一回合遍历结束,再让固定的最大值c向前调整一个,然后继续上述步骤即可。

class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target)
    {
        vector<int> ret;
        int size = price.size();
        int left = 0;
        int right = size - 1;
        while(left < right)
        {
            if(price[left] + price[right] < target)
            {
                left++;
            }
            else if(price[left] + price[right] > target)
            {
                right--;
            }
            else
            {
                return {price[left],price[right]};//隐式类型转换
            }
        }
        //虽然我们知道这条语句不会被执行,但是为了照顾编译器,
        //需要在每条返回路径上都返回
        return {1,1};

    }
};

题目分析:和有效三角形的个数的思想非常类似,也是用到双指针的思想。刚开始的时候,左指针left指向数组最左,右指针right指向数组最右,整个数组是单调递增的,先判断left+right和target的关系,分为三种情况:

        1.left+right > target,那么right和left与right之间的任意数之和都大于target,就没有必要继续遍历了,让right--;

        2.left+right < target,那么left和left与right之间的任意数之和都小于target,也没有必要继续遍历了,让left++;

        3.left+right > target,那么直接返回即可,走隐式类型转换。

需要注意的是,需要在循环外加一个return语句,这样编译器才不会报错,认为每条路径都会有返回值。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        sort(nums.begin(),nums.end());
        int size = nums.size();
        vector<vector<int>> vv;

        for(int i = 0; i < size - 2; )
        {
            if(nums[i] > 0) break;
            int left = i + 1;
            int right = size - 1;
            while(left < right)
            {
                if(-1 * nums[i] < nums[left] + nums[right])
                {
                    right--;
                }
                else if(-1 * nums[i] > nums[left] + nums[right])
                {
                    left++;
                }
                else
                {
                    vv.push_back({nums[i],nums[left],nums[right]});
                    left++;
                    right--; 
                    //去重操作 left right
                    while(left < right && nums[left] == nums[left-1]) left++;
                    while(left < right && nums[right] == nums[right+1]) right--;
                }
            }
            i++;
            //去重i
            while(i < size && nums[i] == nums[i-1])
            {
                i++;
            }          
        }
        return vv;
    }
};

题目分析:这道题依然是有两种解法:暴力解法和优化解法,

暴力解法:先对数组进行排序,然后对数组中的每三个数进行遍历,当找到三个数的符合条件后,将这三个数的组合插入set中,这样方便去重,但是暴力解法的时间复杂度是O(N3)。

优化解法:先对数组进行排序,然后固定一个数a(从最左侧开始固定),在该数后面的区间内,利用上题“双指针”算法,快速找到这个区间内和为-a的两个数。

处理细节

1.去重:找到一种结果之后,left和right指针要跳过重复元素,直到找到下一个不重复的元素,当使用完一次双指针算法后,a也要跳过重复元素。

2.不漏:找到一种结果之后,不要“停”,缩小区间,继续寻找

3.小优化:只有a≤0时,才有可能在后面的区间找到两个数之和=-a,因此a只需要遍历非正值即可。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) 
    {
        //1.排序
        sort(nums.begin(),nums.end());
        //2.按“双指针”查找
        vector<vector<int>> ret;
        int n = nums.size();
        for(int i = 0;i<n;)//固定a
        {
            for(int j=i+1;j<n;)//固定b
            {
                long long val = (long long)target - nums[i] - nums[j];
                int left = j+1;
                int right = n-1;
                while(left < right)
                {
                    int sum = nums[left] + nums[right];
                    if(val > sum)
                    {
                        left++;
                    }
                    else if(val < sum)
                    {
                        right--;
                    }
                    else
                    {
                        ret.push_back({nums[i],nums[j],nums[left],nums[right]});
                        left++;
                        right--;
                        //去重1
                        while(left < right && nums[left] == nums[left-1]) left++;
                        while(left < right && nums[right] == nums[right+1]) right--;
                    }          
                }
                //去重2
                j++;
                while(j < n && nums[j-1] == nums[j]) j++;
            }
            //去重3
            i++;
            while(i < n && nums[i-1] == nums[i]) i++;
        }

        return ret;
    }
};

题目分析:这道题会借用上面三数之和的思想,首先还是对数组进行排序,然后先固定最左边第一个数a,然后从后面区间找出和为target-a的三个数,然后就是借助三数之和的思想,先固定剩下区间的第一个数b,再从剩下区间找和为target-a-b的两个数,然后继续遍历,要和三数之和一样要做到不重不漏。

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

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

相关文章

Jmeter请求发送加密参数详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 最近在做http加密接口&#xff0c;请求头的uid参数及body的请求json参数都经过加密再发送请求&#xff0c;加密方式为&#xff1a;ase256。所以&#xff0c;jmeter…

社区志愿者服务系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图详细视频演示技术栈系统测试为什么选择我官方认证玩家&#xff0c;服务很多代码文档&#xff0c;百分百好评&#xff0c;战绩可查&#xff01;&#xff01;入职于互联网大厂&#xff0c;可以交流&#xff0c;共同进步。有保障的售后 代码参考数据库参…

【AI落地应用实战】Amazon SageMaker JumpStart 体验零一万物的 Yi 1.5 模型

目录 一、前言1.1、Amazon SageMaker JumpStart1.2、Yi-1.5 9B Chat 模型 二、Yi 1.5实践流程2.1、准备SageMaker Studio环境2.2、部署Yi-1.5-9B模型2.3、运行推理Yi-1.5-9B模型 三、体验总结 中国大模型公司零一万物发布开源大模型Yi-1.5&#xff0c;包含多个版本。同时发布多…

YOLOv8轻量化改进之slimneck

目录 一、原理 二、代码 三、修改到YOLOv8中 四、yaml文件修改 一、原理 论文地址:2206.02424 (arxiv.org) 主要模块的网络结构 二、代码 slimneck的代码如下,slimneck主要由GSConv和VoVGSCSPC两部分组成。 class GSConv(nn.Module):# GSConv https://github.com/Alan…

分布式高可用架构设计

一、限流 1、单机限流 如图&#xff0c;应用C的资源c/x被上游的应用A和应用C并发访问&#xff0c;应用C的系统能力支持c/x资源最高5000/qps的访问量&#xff1b;为了不让高并发流量或尖峰流量压垮应用C&#xff0c;可以针对应用C的资源c/x做限流&#xff1b;比如设置限流4500…

为什么会有浮毛猫毛?这些不容忽视的危害宠物空气净化器能解决!

很多人看到朋友家可爱的猫猫狗狗&#xff0c;自己也心痒痒想要养一只。但一想到宠物可能会带来的掉毛、体臭等问题&#xff0c;又犹豫不决。诚然&#xff0c;养宠物确实会对室内空气造成一定影响&#xff0c;但养宠人的共识是&#xff1a;那份与宠物共度的快乐时光&#xff0c;…

mysql索引探索

《令人困惑的mysql索引》在这篇文章中&#xff0c;遗留了几个困惑。为了解决这些困惑&#xff0c;我又进行了一些测试。 本文中索引的建立&#xff0c;并不一定科学&#xff0c;查询的SQL也不一定科学&#xff0c;只是为了理解mysql索引建的一些测试数据。 CREATE TABLE cms_…

加密与安全_双向RSA+AES加密及Code实现

文章目录 AES / RSA流程Code生成AES密钥 和 生成RSA密钥对AES工具类&#xff0c;提供AES加密和解密功能RSA加密工具类测试类 总结 AES / RSA AES&#xff1a;一种对称加密算法&#xff0c;意味着加密和解密使用相同的密钥。速度快&#xff0c;适合加密大量数据。RSA&#xff1…

Tina-Linux Bootloaer简述

Tina-Linux Bootloaer简述 目录介绍 ubuntuubuntu1804:~/tina-v2.0-sdk/lichee/brandy-2.0$ tree -L 1 . ├── build.sh ├── opensbi ├── spl //boot0 ├── spl-pub //boot0 ├── tools └── u-boot-2018 /ubootTina-Linux 启动流程简述

ant design pro v6 如何做好角色管理

先上图&#xff1a; 整个角色管理是如何做的吗&#xff1f; 首先你要处理后端&#xff0c;要先把角色存到用户那。 这是用户管理部分的内容&#xff1a; 可以看到一个用户是有多个角色的。 看到没有&#xff0c;存的是数组 数组的是一个 role 对象 role 对象是这样&#xf…

reactive 和 ref 的区别和联系

在 Vue3 的组合式 API &#xff08;Composition API&#xff09;中&#xff0c;ref 和 reactive 是用于创建响应式数据的两个核心函数。尽管二者都用于实现响应式数据&#xff0c;但在使用方式和适用场景上存在一些区别。 1. 基本概念 1.1 ref 用途&#xff1a;用于定义 基本…

深入探讨视频美颜SDK:直播美颜工具的核心技术与实现

本篇文章&#xff0c;笔者将深入探讨视频美颜SDK的核心技术及其在直播美颜工具中的实现。 一、视频美颜SDK的核心技术 视频美颜SDK通过一系列复杂的算法和技术&#xff0c;实时处理视频流并应用各种美颜效果。这些核心技术主要包括以下几个方面&#xff1a; 1.人脸识别与追踪…

【Hot100】LeetCode—24. 两两交换链表中的节点

目录 1- 思路四指针 2- 实现⭐24. 两两交换链表中的节点——题解思路 3- ACM 实现 原题连接&#xff1a;24. 两两交换链表中的节点 1- 思路 四指针 定义 dummyHead&#xff1a;便于处理头结点① cur 指针&#xff0c;记录两个交换节点的前 前一个结点② 第一个指针 first③ 第…

嵌入式开发--STM32G030C8T6,写片上FLASH死机CFGBSY和写入出错

故障现象1 G0系列&#xff0c;写片上FLASH时&#xff0c;经常死机&#xff0c;而且按复位键都没用&#xff0c;属于不断电都救不回来的那种死法。这种情况一般是由硬件置位了某个标志导致&#xff0c;只有断电才能故障复原。 故障查找 检查FLASH的相关寄存器&#xff0c;发现…

【秋招笔试】8.18大疆秋招(第一套)-后端岗

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收…

树莓派Pico C/C++ 开发环境搭建(一键完成版)

树莓派Pico C/C 开发环境搭建(一键完成版) 因为之前使用过MicroPython开发过树莓派Pico&#xff0c;总觉得用起来怪怪的。正好最近树莓怕发布了新一代的MCU——RP2350&#xff0c;之前的RP2040在各个平台都有所降价&#xff0c;因此&#xff0c;买了几块。同时因为之前是玩stm…

【系统架构设计】系统分析与设计方法

【系统架构设计】系统分析与设计方法 定义问题与归结模型问题分析因果鱼骨图 帕累托图上下文范围图 问题定义 需求分析与软件设计结构化分析与设计结构化分析DFD数据字典技术 结构化设计结构图程序流程图和盒图 模块设计 面向对象的分析与设计用户界面设计 定义问题与归结模型…

Eureka 原理与实践全攻略

一、Eureka 概述 Eureka 在微服务架构中具有举足轻重的地位。它作为服务注册与发现的核心组件&#xff0c;为分布式系统中的服务管理提供了关键支持。 Eureka 的主要功能包括服务注册、服务发现、服务健康监测和自我保护机制。服务注册功能使得服务提供者能够在启动时将自身的…

Linux安装配置docker,k8s

1.linux虚拟机初始配置 详情操作见下&#xff0c;具体操作需要注意 1.网络模式选择 NAT模式&#xff08;对应第8点&#xff09; 2.不用选择安装 GUI图形界面 (对应19点-5) 3.分区非必须操作 可以不分区(对应20点) 4.打开网络开关 (对应23点) CentOS 7安装教程&#xff…

利用GPTs,打造你的专属AI聊天机器人

在2023年11月的「OpenAI Devday」大会上&#xff0c;OpenAI再度带来了一系列令人瞩目的新功能&#xff0c;其中ChatGPT方面的突破尤为引人关注。而GPTs的亮相&#xff0c;不仅标志着个性化AI时代的到来&#xff0c;更为开发者和普通用户提供了前所未有的便利。接下来&#xff0…