【九】【算法分析与设计】双指针(3)

15. 三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 解释: nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。 注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1] 输出:[] 解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0] 输出:[[0,0,0]] 解释:唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000

  • -10(5) <= nums[i] <= 10(5)

暴力枚举:

为了避免出现重复的元素,我们对nums数组进行排序,然后把相同的元素不计算,直接跳过。

题目的要求是不包含重复的三元组,所以我没每一次后移i,j,k的时候不可以简单的后移,而是往后移动到下一个不重复的数字上,这样就不会重复计算相同的元素。

 
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {sort(nums.begin(), nums.end());vector<vector<int>> ret;int n = nums.size();for (int i = 0; i < n;) {for (int j = i + 1; j < n;) {for (int k = j + 1; k < n;) {if (nums[i] + nums[j] + nums[k] == 0) {ret.push_back({nums[i], nums[j], nums[k]});}k++;while (k < n && nums[k] == nums[k - 1])k++;}j++;while (j < n && nums[j] == nums[j - 1])j++;}i++;while (i < n && nums[i] == nums[i - 1])i++;}return ret;}
};

双指针优化暴力枚举:

首先对nums排序。

固定三元组中最大的一个数,记为nums[i],那么问题就转化为找两数之和为target=-nums[i]的组合。令left=0,right=i-1,我们需要遍历的是[left,right]区间内,每两两组合。

sum=nums[left]+nums[right],如果sum>target,那么对于rightleft+1,left+2.....right-1这些数的组合一定也大于target,因为nums是有序的,下标和值分别看作x与y函数,函数就是递增的。所以nums[left+1],nums[left+2].....一定大于nums[left],所以right与后面的组合都可以不用再考虑,一定也不符合题意。这样对于right的组合情况就全部考虑完毕了。此时对right--,相同的数再次right--,直到数不相同,防止重复计算。

如果sum<target,那么对于leftright-1,right-2....left+1这些数的组合一定也小于target,因为nums是有序的,下标和值分别看作xy函数,函数就是递增的。所以nums[right-1],nums[right-2].....一定小于nums[right],所以left与后面的组合都可以不用再考虑,一定也不符合题意。这样对于left的组合情况就全部考虑完毕了。此时对left++,相同的数再次left++,直到数不相同,防止重复计算。

 
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {sort(nums.begin(), nums.end());vector<vector<int>> ret;int n = nums.size();for (int i = n - 1; i >= 2;) {int left = 0, right = i - 1;int target = -nums[i];while (left < right) {int sum = nums[left] + nums[right];if (sum > target) {right--;} else if (sum < target) {left++;} else {ret.push_back({nums[left++], nums[right--], nums[i]});while (left < right && nums[left] == nums[left - 1])left++;while (left < right && nums[right] == nums[right + 1])right--;}}i--;while (i >= 2 && nums[i] == nums[i + 1])i--;}return ret;}
};

class Solution {public: vector<vector<int>> threeSum(vector<int>& nums) {

定义了一个名为 threeSum 的成员函数,它接受一个名为 nums 的整数向量的引用作为参数,并返回一个二维向量,其中包含所有满足条件的三元组。

sort(nums.begin(), nums.end());

首先对输入的向量 nums 进行排序,这是为了后续能够使用双指针技术来避免重复的三元组并简化搜索过程。

vector<vector<int>> ret;

定义了一个名为 ret 的二维向量,用于存储最终的结果。

int n = nums.size();for (int i = n - 1; i >= 2;) {

获取 nums 的大小赋值给 n,并从数组的末尾开始遍历,因为我们要找三个数的组合,所以至少需要3个数。

int left = 0, right = i - 1;int target = -nums[i];

初始化两个指针 leftright,分别指向当前子数组的开始和 i 之前的位置。target 是我们要找的两数之和,它是 nums[i] 的相反数。

while (left < right) {int sum = nums[left] + nums[right];

left 指针小于 right 指针的情况下,计算 leftright 指向的两个数的和。

if (sum > target) { right--;} else if (sum < target) { left++;} else { ret.push_back({nums[left++], nums[right--], nums[i]});

如果 sum 大于 target,需要减小 sum,所以 right 指针左移,可以理解为与right组合的所有情况考虑完毕;如果 sum 小于 target,需要增大 sum,所以 left 指针右移,可以理解为与left组合的所有情况考虑完毕;如果 sum 等于 target,说明找到了一个有效的三元组,将其添加到结果 ret 中,并移动两个指针寻找下一个可能的三元组。

while (left < right && nums[left] == nums[left - 1]) left++;while (left < right && nums[right] == nums[right + 1]) right--;

这两个循环用来跳过相同的元素,避免重复的三元组。

i--;while (i >= 2 && nums[i] == nums[i + 1]) i--;

递减 i 并跳过相同的元素,以便在下一轮循环中处理不同的 nums[i]

时间复杂度和空间复杂度分析

时间复杂度:O(n^2),其中 n 是数组 nums 的长度。外层循环最多执行 n 次,内层循环(双指针)在每次外层循环中最多执行 n 次。

空间复杂度:O(1) 或 O(n),这取决于排序算法的实现,如果排序算法是原地排序,则空间复杂度为 O(1),否则为 O(n)。

18. 四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n

  • abcd 互不相同

  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0 输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [2,2,2,2,2], target = 8 输出:[[2,2,2,2]]

提示:

  • 1 <= nums.length <= 200

  • -10(9) <= nums[i] <= 10(9)

  • -10(9) <= target <= 10(9)

暴力枚举:

为了避免重复计算,我们先把nums数组进行排序,然后跳过计算相同的元素。对于i,j,k,l每一次不是简单的往后移一位,而是往后移到下一个不重复的元素位置。

 
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {int n = nums.size();vector<vector<int>> ret;sort(nums.begin(),nums.end());for (int i = 0; i < n;) {for (int j = i + 1; j < n;) {for (int k = j + 1; k < n;) {for (int l = k + 1; l < n;) {if ((long long)nums[i] + nums[j] + nums[k] + nums[l] == target)ret.push_back({nums[i], nums[j], nums[k], nums[l]});l++;while (l < n && nums[l] == nums[l - 1])l++;}k++;while (k < n && nums[k] == nums[k - 1])k++;}j++;while (j < n && nums[j] == nums[j - 1])j++;}i++;while (i < n && nums[i] == nums[i - 1])i++;}return ret;}
};

双指针优化暴力枚举:

首先固定一位数,问题转化为求三数和,再固定一位,问题转化为求两数和,求两数和用双指针,对撞指针进行暴力枚举的优化,快速找到符合条件的情况。

需要注意的是数据的计算过程又可以超出界限,所以aim我们不用int而用longlong,后面的计算表达式只需要把一个强转为longlong即可。

 
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {int n = nums.size();vector<vector<int>> ret;sort(nums.begin(), nums.end());for (int i = 0; i < n;) {for (int j = i + 1; j < n;) {int left = j + 1, right = n - 1;long long aim = (long long)target - nums[i] - nums[j];while (left < right) {int sum = nums[left] + nums[right];if (sum < aim)left++;else if (sum > aim)right--;else {ret.push_back({nums[i], nums[j], nums[left], nums[right]});right--;left++;while (left < right && nums[left] == nums[left - 1])left++;while (left < right && nums[right] == nums[right + 1])right--;}}j++;while (j < n && nums[j] == nums[j - 1])j++;}i++;while (i < n && nums[i] == nums[i - 1])i++;}return ret;}
};

class Solution {public: vector<vector<int>> fourSum(vector<int>& nums, int target) {

定义了一个名为 fourSum 的成员函数,它接受一个整数数组 nums 和一个目标值 target 作为参数,并返回一个二维向量,其中包含所有满足条件的四元组。

int n = nums.size(); vector<vector<int>> ret;sort(nums.begin(), nums.end());

获取 nums 的大小并赋值给 n,定义一个用于存储结果的二维向量 ret,并对 nums 进行排序,排序就作用是便于我们进行去重操作。

for (int i = 0; i < n;) {

使用外层循环遍历 numsi 是四元组中第一个数字的位置。

for (int j = i + 1; j < n;) {

使用内层循环遍历 numsj 是四元组中第二个数字的位置。

int left = j + 1, right = n - 1;long long aim = (long long)target - nums[i] - nums[j];

初始化双指针 leftright,分别指向四元组中第三和第四个数字的起始位置。计算剩余两数的目标和 aim

while (left < right) {int sum = nums[left] + nums[right];

left 小于 right 的情况下,计算 leftright 指针指向的两个数的和。

if (sum < aim) left++;else if (sum > aim) right--;

如果两数之和小于 aim,则将 left 指针右移以增加和,可以理解为对于left的所有组合都考虑完毕;如果大于 aim,则将 right 指针左移以减小和,可以理解为对于right的所有组合都考虑完毕。

else { ret.push_back({nums[i], nums[j], nums[left], nums[right]}); right--; left++;

如果找到满足条件的两数之和等于 aim,将四个数作为一个四元组添加到结果集 ret 中,并同时移动 leftright 指针。

while (left < right && nums[left] == nums[left - 1]) left++;while (left < right && nums[right] == nums[right + 1]) right--;

跳过重复的元素以避免重复的四元组。

} j++;while (j < n && nums[j] == nums[j - 1]) j++;

内层循环结束,j 指针右移并跳过重复的元素。

} i++;while (i < n && nums[i] == nums[i - 1]) i++;

外层循环结束,i 指针右移并跳过重复的元素。

时间复杂度和空间复杂度分析

时间复杂度:O(n^3),其中 n 是数组 nums 的长度。最外层循环和第二层循环分别执行了 n 次,内层的双指针循环最多执行 n 次。

空间复杂度:O(1) 或 O(n),这取决于排序算法的实现。如果排序算法是原地的,则空间复杂度为 O(1),否则为 O(n)。

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

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

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

相关文章

echarts实践总结(常用一):柱状图(特点:渐变色、点击缩放、左右滑动、悬浮展示样式)

目录 第一章 echarts基本使用 第二章 echarts实践——柱状图 效果展示 第一章 echarts基本使用 Echarts常用配置项(详细入门)_echarts配置项手册-CSDN博客 第二章 echarts实践——柱状图 最近接到这么一个需求&#xff0c;需要画页面&#xff0c;然后有这么几个echarts的图需…

【网络安全】0xhacked CTF 大赛题解出炉啦!

此次 0xhacked CTF 比赛&#xff0c;ChainSecLabs 取得了第四名的成绩。让我们来看看比赛题目的题解吧。&#xff08;题目代码仓库在文末哦~&#xff09; BabyOtter 这是应该说是一个算法题&#xff0c;很明显需要溢出&#xff0c;因为精度问题&#xff0c;uint256(-1)/0x1…

m4v是什么文件格式?m4v视频用什么软件打开?

m4v文件格式的诞生可追溯到苹果公司。作为数字媒体领域的先锋&#xff0c;苹果在iTunes商店中为视频内容引入了m4v格式。其初衷是为了在保证视频质量的同时&#xff0c;通过管理系统&#xff0c;实现对数字内容的保护。这使得m4v成为iOS和macOS平台上广泛使用的视频格式。 M4V的…

工具精灵--超级好用的在线工具网站

工具精灵是一个超级好用的在线工具网站&#xff0c;它有这些功能&#xff1a;json格式化、xml格式化、markdown在线编辑、sql格式化、json转Java、xml转Java等。 虽然有很多这种类似的网站了&#xff0c;但它们并不好用&#xff0c;很粗糙。工具精灵超级好用&#xff0c;细节方…

为什么要为 App 应用加固 ?如何为 App 应用加固 ?

一&#xff1a;为什么要为 App 应用加固 来看下 腾讯开放平台 官方的解释说明 若应用不做任何安全防护&#xff0c;极易被病毒植入、广告替换、支付渠道篡改、钓鱼、信息劫持等&#xff0c;严重侵害开发者的利益。 App 加固后&#xff0c;可以对应用进行安全防护&#xff0c;防…

回归测试?

1. 什么是回归测试&#xff08;Regression Testing&#xff09; 回归测试是一个系统的质量控制过程&#xff0c;用于验证最近对软件的更改或更新是否无意中引入了新错误或对以前的功能方面产生了负面影响&#xff08;比如你在家中安装了新的空调系统&#xff0c;发现虽然新的空…

抛弃Superhuman?这些替代方案让你眼前一亮!

Superhuman是一个极好的人工智能工具在电子邮件助理领域。根据SimilarWeb的最新统计&#xff0c;它在全球网站排名中排名第21980位&#xff0c;月访问量为1751798。然而市场上还有许多其他优秀的选择。为了帮助您找到最适合您需求的解决方案&#xff0c;我们为您精心挑选了10种…

项目性能优化—使用JMeter压测SpringBoot项目

我们的压力测试架构图如下&#xff1a; 配置JMeter 在JMeter的bin目录&#xff0c;双击jmeter.bat 新建一个测试计划&#xff0c;并右键添加线程组&#xff1a; 进行配置 一共会发生4万次请求。 ctrl s保存&#xff1b; 添加http请求&#xff1a; 配置http请求&#xff1a;…

免费开源、支持自建服务的团队协作、个人学习文档管理系统

大家好&#xff0c;我是小麦。今天来给大家分享的是几款个人使用过的免费、开源、适合团队协作的文档管理工具&#xff0c;并且是完全支持自己搭建服务的文档管理系统。 相信大家在学习、办公等场景下对文档管理工具使用的场景是比较多的&#xff0c;例如技术开发手册、个人学…

SpringBoot(数据库操作 + druid监控功能)

文章目录 1.JDBC HikariDataSource&#xff08;SpringBoot2默认数据源&#xff09;1.数据库表设计2.引入依赖 pom.xml3.配置数据源参数 application.yml4.编写一个bean&#xff0c;映射表5.编写测试类来完成测试1.引入依赖 pom.xml2.使用JdbcTemplate进行测试3.成功&#xff0…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的交通信号灯识别系统(深度学习+UI界面+训练数据集+Python代码)

摘要&#xff1a;本研究详细介绍了一种采用深度学习技术的交通信号灯识别系统&#xff0c;该系统集成了最新的YOLOv8算法&#xff0c;并与YOLOv7、YOLOv6、YOLOv5等早期算法进行了性能评估对比。该系统能够在各种媒介——包括图像、视频文件、实时视频流及批量文件中——准确地…

Python从COCO数据集中抽取某类别的数据

1、问题描述 今天需要训练一个人工智能检测模型&#xff0c;用于检测图片或视频中的人。自行收集训练数据费时费力&#xff0c;因而选择从公开数据集COCO中进行抽取。 2、数据准备 2.1 下载 COCO2017 数据集 train:http://images.cocodataset.org/zips/train2017.zip valid…

2023年蓝桥杯省赛——幸运数字

目录 题目链接&#xff1a;0幸运数字 - 蓝桥云课 (lanqiao.cn) 解法 思路 高级思路 总结 题目链接&#xff1a;0幸运数字 - 蓝桥云课 (lanqiao.cn) 解法 首先是我写了差不多一个小时的解法&#xff0c;裂开了&#xff0c;为什么我如此废物 思路 寻找第2023个在二进制、八…

http协议的历史与基本概念

文章目录 历史和发展起源&#xff1a;HTTP/0.9&#xff08;1991年&#xff09;&#xff1a;HTTP/1.0&#xff08;1996年&#xff0c;RFC 1945&#xff09;&#xff1a;HTTP/1.1&#xff08;1997年&#xff0c;RFC 2068&#xff1b;1999年更新为RFC 2616&#xff09;&#xff1a…

【MIT 6.S081】2020, 实验记录(9),Lab: file system

目录 Task 1&#xff1a;Large filesTask 2&#xff1a;Symbolic links2.1 增加一个系统调用 symlink2.2 新增文件类型2.3 新增 NOFOLLOW 标志位2.4 实现 sys_symlink 系统调用2.5 修改 sys_open 函数2.6 测试 Task 1&#xff1a;Large files 现在的 xv6 系统中&#xff0c;一…

算法——贪心算法

《算法图解》——贪心算法 # 首先创建一个表&#xff0c;包含所覆盖的州 states_needed set([mt,wa,or,id,nv,ut,az]) # 传入一个数组&#xff0c;转换成一个集合#可供选择的广播台清单 stations {} stations[kone] set([id,nv,ut]) #用集合表示想要覆盖的州&#xff0c;且不…

EMQX 4.0和EMQX 5.0集群架构实现1亿MQTT连接哪些改进

EMQX 5.0水平扩展能力得到了指数级提升&#xff0c;能够更可靠地承载更大规模的物联网设备连接量。 在EMQX5.0正式发布前的性能测试中&#xff0c;我们通过一个23节点的EMQX集群&#xff0c;全球首个达成了1亿MQTT连接每秒100万消息吞吐&#xff0c;这也使得EMQX 5.0成为目前为…

Jenkins流水线将制品发布到Nexus存储库

1、安装jenkins&#xff08;建议别用docker安装&#xff0c;坑太多&#xff09; docker run -d -p 8089:8080 -p 10241:50000 -v /var/jenkins_workspace:/var/jenkins_home -v /etc/localtime:/etc/localtime --name my_jenkins --userroot jenkins/jenkins:2.449 坑1 打开x…

Python使用 k 均值对遥感图像进行语义分割

本篇文章介绍K-means语义分割来估计 2000 年至 2023 年咸海水面的变化 让我们先看一下本教程中将使用的数据。这是同一地区的两张 RGB 图像,间隔 23 年,但很明显地表特性和大气条件(云、气溶胶等)不同。这就是为什么我决定训练两个独立的 k-Means 模型,每个图像一个。 首…

仰卧起坐计数,YOLOV8POSE

仰卧起坐计数&#xff0c;YOLOV8POSE 通过计算膝盖、腰部、肩部的夹角&#xff0c;计算仰卧起坐的次数