LeetCode.双指针(三)

news2024/11/18 3:34:12

例题一 

一、题目

两数之和 II - 输入有序数组
给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列  ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用常量级的额外空间。

示例 1:

输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
示例 2:

输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。
示例 3:

输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
 

提示:

2 <= numbers.length <= 3 * 104
-1000 <= numbers[i] <= 1000
numbers 按 非递减顺序 排列
-1000 <= target <= 1000
仅存在一个有效答案

作者:LeetCode
链接:https://leetcode.cn/leetbook/read/all-about-array/x9i1x6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

二、代码

分析:

        找nums[i] + nums[j] = target,找到对应的i和j。

        由于nums数组是非递减的,也就是nums[i+1] >= nums[i],那么target = a(小一点) + b(大一点)的两个数相加,自然我们想到双指针操作。

思路:

        1.设置两个指针:i,j分别指向数组的首尾

        2.i,j相向移动,同时判断 nums[i] + nums[j] ==target

                2.1 if sum > target , 则说明需要小一点的数,又因为右边的数较大,则j--

                2.2 if sum < target ,则说明需要大一点的数,则 i++

                2.3 if sum == target ,则记录下i,j 的值,同时 i++, j--

        3. 知道i == j时,结束

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){
    // 思路:双指针
    //     1.两个指针i,j分别指向数组首尾,
    //     2.相向移动,判断numbers[i] + numbers[j] == target
    //         2.1如果>target,则需要小一点的元素,则i不动,j--
    //         2.2如果<target,则需要大一点的元素,则j不同,i++
    //         2.3如果=target,则记录下i,j的索引,然后i++,j--
    //     3.直到i==j,则结束

    int i = 0;//指向第一个元素
    int j = numbersSize - 1;//指向最后一个元素
    int *index = (int *)malloc(sizeof(int) * 2);//满足target的元素序号数组
    *returnSize = 0;
    while(i < j){
        if(numbers[i] + numbers[j] > target){
            j--;
        }else if(numbers[i] + numbers[j] < target){
            i++;
        }else{
            index[(*returnSize)++] = i + 1;
            index[(*returnSize)++] = j + 1;
            //由于只有一组,所以当=target时,即可退出遍历
            break;
            // i++;
            // j--;
        }
    }
    return index;
}

注意:本题中两数之和要等于target,在移动指针的过程中,保持一个原则就是一个数小 ,另一个数大 。也正是数组是非递减的,才可以用这样——双指针的方式解决问题。

如果数组是非递减的,则使用二重循环的方式找出答案。

时间复杂度:O(n)

空间复杂度:O(1)

例题二

一、题目

验证回文串
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。

示例 1:

输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。
示例 2:

输入:s = "race a car"
输出:false
解释:"raceacar" 不是回文串。
示例 3:

输入:s = " "
输出:true
解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。
 

提示:

1 <= s.length <= 2 * 105
s 仅由可打印的 ASCII 字符组成

作者:LeetCode
链接:https://leetcode.cn/leetbook/read/all-about-array/x9tqjc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

二、代码

分析:

        同样采用双指针的思路

思路:

        1.设置i,j两个指针,分别指向字符串的首尾

        2.i,j相向移动,当遇到非字母时则忽略跳过

        3.判断 s[i] == s[j],相等则同时 i++, j-- ;若不相等则直接返回结果false

        4.当i==j时,移动结束,可以返回true

bool isCharacterOrNumber(char c){
    if(c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= '0' && c <= '9'){
        return true;
    }
    return false;
}
bool isPalindrome(char * s){
    // 对撞指针
    // 思路:1. i,j两个指针相向而动
    //     2. 若s[i],s[j] 不是字母或者数字,则跳过,i++,或者j--
    //     3. 若s[i],s[j] 是大写字母,则转换成小写字母
    //     4. 判断 s[i] == s[j] 是则i++,j--;不是则返回false
    //     5.直到i == j时,则返回true;
    int i = 0;//i指向第一个字符
    int j = strlen(s) - 1;//j指向最后一个字符
    while(i < j){
        //当前字符若不是字母或者数字,则下移
        while(!isCharacterOrNumber(s[i]) && i < j){
            i++;
        }
        while(!isCharacterOrNumber(s[j]) && i < j){
            j--;
        }
        //判断i,j指向的元素是否一致
        if(tolower(s[i]) != tolower(s[j])){
            return false;
        }
        i++;
        j--;
    }
    return true;
}

时间复杂度:O(n)

空间复杂度:O(1)

例题三

一、题目

反转字符串中的元音字母
给你一个字符串 s ,仅反转字符串中的所有元音字母,并返回结果字符串。

元音字母包括 'a'、'e'、'i'、'o'、'u',且可能以大小写两种形式出现不止一次。

示例 1:

输入:s = "hello"
输出:"holle"
示例 2:

输入:s = "leetcode"
输出:"leotcede"
 

提示:

1 <= s.length <= 3 * 105
s 由 可打印的 ASCII 字符组成

作者:LeetCode
链接:https://leetcode.cn/leetbook/read/all-about-array/x93lce/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

二、代码

思路:对撞指针

        1.i,j分别指向字符串的首尾

        2.i++,j--当i遇到一个元音字母时,停下;当j遇到一个元音字母时也停下

        3.交换s[i] 和 s[j]

        4.重复2-3,直到i== j时结束

bool isVowel(char c){
    if(tolower(c) == 'a' || tolower(c) == 'e' || tolower(c) == 'i' || tolower(c) == 'o' || tolower(c) == 'u'){
        return true;
    }
    return false;
}
void swap(int i,int j,char *s){
    char c = s[i];
    s[i] = s[j];
    s[j] = c;
}
char * reverseVowels(char * s){
    //对撞指针
    // 思路:1. i,j分别指向字符串的首尾
    //       2. i++,j--,当i遇到一个元音字母时,停下,当j遇到一个元音字母时停下(类似快速排序中每趟确定基准数的过程)
    //       3. 交换s[i] 和 s[j]
    //       4. 重复2,直至i == j时停止
    int i = 0;
    int j = strlen(s) - 1;
    while(i < j){
        while(!isVowel(s[j]) && i < j){
            j--;
        }
        while(!isVowel(s[i]) && i < j){
            i++;
        }
        //交换i,j所指向的元素
        swap(i,j,s);
        j--;
        i++;
    }
    return s;
}

注意:本题中,i,j指针相向移动的过程有些类似快排算法中确定基准数位置的过程,读者可以体会体会。

时间复杂度:O(n)

空间复杂度:O(1)

例题四

一、题目

盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例 1:

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:

输入:height = [1,1]
输出:1
 

提示:

n == height.length
2 <= n <= 105
0 <= height[i] <= 104

作者:LeetCode
链接:https://leetcode.cn/leetbook/read/all-about-array/x96n4v/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

二、代码

思路1:无脑暴力

        二重遍历计算所有的结果,然后比较找到最大值,这里不过多展示。

分析:

本题中,我们不难发现水箱的容量是由最短的木板和两个木板之间的距离决定的,那么,我们是否可以利用这一点使用双指针来解决问题呢?

我们同样采用对撞指针的思路,i,j指向首尾,可以发现,在i,j移动的过程中,木板间距distance在不断减小,如果木板长度不变则,容量一直减小;

而我们目标是要找到最大的容量,那么一定要改变木板长度。

显然如果改变长木板,有可能导致容量没有变化,而更改短木板,则一定会导致容量发生变换。

更改短木板后,更改后的容量再和已有的max容量比较。

思路2: 对撞指针

        1.设置i,j两个指针指向首尾

        2.每次移动较短木板。

        3.记录当前的容量cur和已有最大容量max相比较。若cur>max,则更改max = cur;若cur <max,则不变

        4.重复2-3,直至i== j结束。

int maxArea(int* height, int heightSize){
    // 思路1:暴力,找出所有水量,找出最大值
    // 思路2:双指针,
    //     1.水箱的容量由最短的木板决定
    //     2.当下一个木板比当前的木板还短或者相等时,则水箱的容量一定小于当前的容量(底变短,高度不变)
    //     3.当下一个木板比当前木板长时,则水箱容量可能变大(底变短,高变长)
    int i = 0;
    int j = heightSize - 1;
    //默认第一个木板和最后一个木板组成的水箱最大
    int minBord = height[i] < height[j] ? height[i] : height[j]; 
    int max = (j - i) * minBord;
    while(i < j){
        //移动较短的那个
        if(height[i] < height[j]){
            i++;
        }else{
            j--;
        }
        minBord = height[i] < height[j] ? height[i] : height[j];
        max = (j - i) * minBord > max ? (j - i) * minBord : max;
    }
    return max;
}

时间复杂度:O(n)

空间复杂度:O(1)

三、总结

本次几个题目均是对撞指针的思路,这类题目关键是要能够把握

  1. 指针移动的条件
  2. 指针暂停移动的条件
  3. 指针结束移动的条件

一般条件3都是两个指针相遇则结束,而1、2两个条件则需要根据具体问题具体分析。

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

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

相关文章

思维导图怎么制作?了解一下这几种制作方法

思维导图怎么制作&#xff1f;思维导图是一种非常有效的组织思考和表达想法的工具。它可以帮助人们更好地理解和记忆信息&#xff0c;并且可以通过可视化的方式帮助人们更好地理解复杂的关系和概念。制作思维导图有多种方法&#xff0c;例如手绘、使用电子表格或专业的思维导图…

《软件方法》强化自测题-分析(4)

DDD领域驱动设计批评文集 通过做强化自测题加入“软件方法建模师”群 《软件方法》各章合集 按照业务建模、需求、分析、设计工作流考察&#xff0c;答案不直接给出&#xff0c;可访问自测链接或扫二维码自测&#xff0c;做到全对才能知道答案。 知识点见《软件方法》&…

不同企业如何选择合适的CRM系统?

市场上的CRM系统千差万别&#xff0c;如何选到适合的CRM系统&#xff1f;很多企业凭借感觉盲目选型&#xff0c;结果上线后发现CRM系统功能不符合需求。这就好比买衣服&#xff0c;不试穿就买回家&#xff0c;结果发现尺码不合适&#xff0c;还不能退换。下面说说企业如何进行C…

一起学SF框架系列7.4-spring-AOP-AOP代理创建

AOP的BeanDefinition加载后&#xff0c;Spring提供了自动代理机制&#xff0c;让容器自动根据目标bean生成AOP代理bean&#xff0c;本文讲述具体如何实现。 基本机制 Spring的启动过程中&#xff0c;在bean实例化前后、初始化前后均提供了外部介入处理机制&#xff08;详见“…

三、SQLServer 数据库安装集

一、Docker 安装 Docker下安装SqlServer2019Docker 安装 SQLServer 1. 创建容器 前置准备 # 1. 创建主机映射目录 mkdir -p /root/sqlserver # 2. 修改主机映射目录权限 chown -R 10001:0 /root/sqlserver创建容器 # 1、拉取镜像。 #sudo docker pull mcr.microsoft.com/mssql/…

超越函数界限:探索JavaScript函数的无限可能

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4da; 前言 &#x1f4d8; 1. 函数的基本概念 &#x1f4df; 1.1 函数的定义和调用 &#x1f4df; 1.2 …

用加持了大模型的 Byzer-Notebook 做数据分析是什么体验

Byzer-Notebook 是专门为 SQL 而研发的一款 Web Notebook。他的第一公民是 SQL&#xff0c;而 Jupyter 则是是以 Python 为第一公民的。 随着 Byzer 引擎对大模型能力的支持日渐完善&#xff0c; Byzer-Notebook 也在不自觉中变得更加强大。我和小伙伴在聊天的过程中才发现他已…

TCP定制协议,序列化和反序列化

目录 前言 1.理解协议 2.网络版本计算器 2.1设计思路 2.2接口设计 2.3代码实现&#xff1a; 2.4编译测试 总结 前言 在之前的文章中&#xff0c;我们说TCP是面向字节流的&#xff0c;但是可能对于面向字节流这个概念&#xff0c;其实并不理解的&#xff0c;今天我们要介…

QT:绘图事件QPainter

绘图事件QPainter 绘图事件&#xff08;需要重写的函数&#xff09;&#xff1a;paintEvent 声明一个画家对象 QPainter painter(this) 指定绘图设备 画线&#xff0c;画圆&#xff0c;画矩形&#xff0c;画文字 可设置画笔&#xff0c;画刷#include <QPainter> ...... …

剑指 Offer 48. 最长不含重复字符的子字符串(C++实现)

剑指 Offer 48. 最长不含重复字符的子字符串https://leetcode.cn/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/ dp 注意&#xff1a;缩小 不含重复字符子串 时的写法 dp_1 min(i - charToIndex[s[i]], dp_0 1); int lengthOfLongestSubstring(string s…

Autosar MCAL-S32K324 CAN-FD配置及使用

文章目录 前言配置MCAL CANCAN Controller配置CAN FD波特率配置Ram block关于MailBox 代码中使用CAN FD报文发送和接收CAN FD报文接收CAN FD报文发送 总结 前言 在之前的文章中&#xff0c;介绍了标准CAN的MCAL配置&#xff0c;在此基础上&#xff0c;扩展为CAN-FD就会容易很多…

6.RocketMQ之消费索引文件ConsumeQueue

功能&#xff1a;作为CommitLog文件的索引文件。 本文着重分析为consumequeue/topic/queueId目录下的索引文件。 1.ConsumeQueueStore public class ConsumeQueueStore {protected final ConcurrentMap<String>, ConcurrentMap<Integer>, ConsumeQueueInterface…

NetSuite OIDC、SAML SSO 演示

NetSuite的SSO的策略近些年处于演进过程&#xff0c;所以原来的Inbound SSO和Outbound SSO已经退出历史舞台。前者已经废止&#xff0c;后者在24年底废止。目前的SSO策略是&#xff1a; 第三方的身份认证服务商NetSuite as OIDC Provider 前者的含义是&#xff0c;把认证服务…

数据结构 - 基本概念和术语

基础概念之间的关系大致如下&#xff1a; 一、数据、数据元素、数据项和数据对象 数据 > 数据对象 > 数据元素 > 数据项 类比数据库&#xff0c;这四个概念代表的含义如下所示&#xff1a; 数据&#xff1a;整个数据库的所有数据数据对象&#xff1a;这个数据库的…

Shell脚本五:函数和数组

文章目录 1.函数1.1Shell函数的概念1.2函数的好处1.2函数的组成1.3函数的结构1.4查看函数列表1.5删除函数1.6函数的返回值1.6.1使用原则1.6.2示例 1.7函数的作用范围1.8函数递归1.8.1示例 2.数组2.1什么是数组2.2数组的作用2.3数组名和索引2.4定义数组的方式2.5普通数组和关联数…

深入理解分布式架构,构建高效可靠系统的关键

深入探讨分布式架构的核心概念、优势、挑战以及构建过程中的关键考虑因素。 引言什么是分布式架构&#xff1f;分布式架构的重要性 分布式系统的核心概念节点和通信数据分区与复制一致性与一致性模型负载均衡与容错性 常见的分布式架构模式客户端-服务器架构微服务架构事件驱动…

对Lua的理解

在redis和nginx中都潜入了Lua环境用于快速上手开发。但如何理解Lua以及Lua与宿主环境的交互是需要掌握的。 首先是Lua本身&#xff0c;打开5.1的lua版本开始编译后最后生成一个lua的可执行文件&#xff0c;这其实就是一个包含了Lua虚拟机的终端.。所以其实在不管redis也好nginx…

2023/8/20周报

目录 摘要 论文阅读 1、标题和现存问题 2、准备知识 3、模型结构 4、实验准备 5、实验结果 深度学习 1、构建图数据 2、GCN模型 3、当前实验结果 总结 摘要 本周在论文阅读上&#xff0c;阅读了一篇时空图卷积网络:交通预测的深度学习框架的论文。文章的时空图卷积…

NOIP2014普及组,提高组 比例简化 飞扬的小鸟 答案

比例简化 说明 在社交媒体上&#xff0c;经常会看到针对某一个观点同意与否的民意调查以及结果。例如&#xff0c;对某一观点表示支持的有1498 人&#xff0c;反对的有 902人&#xff0c;那么赞同与反对的比例可以简单的记为1498:902。 不过&#xff0c;如果把调查结果就以这种…

Leetcode-每日一题【剑指 Offer 33. 二叉搜索树的后序遍历序列】

题目 输入一个整数数组&#xff0c;判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true&#xff0c;否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树&#xff1a; 5 / \ 2 6 / \ 1 3 示例 1&#xff1a; 输入: […