【优选算法必刷100题】第007~008题(双指针算法):三数之和、四数之和问题求解

【优选算法必刷100题】第007~008题(双指针算法):三数之和、四数之和问题求解

🔥艾莉丝努力练剑:个人主页

专栏传送门:《C语言》《数据结构与算法》C/C++干货分享&学习过程记录Linux操作系统编程详解笔试/面试常见算法:从基础到进阶

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬艾莉丝的算法专栏简介:



目录

007  三数之和

1.1  题目解析

1.2  思路1:暴力

1.3  思路2:排序 + 双指针

1.3.1  算法思路

1.3.2  代码实现

1.4  过程推算

1.5  反思

008  四数之和

2.1  题目解析

2.2  思路1:暴力

2.3  思路2:排序 + 双指针

2.3.1  算法思路

2.3.2  代码实现

2.4  过程推算

2.5  反思

结尾


007  三数之和

力扣链接:15. 三数之和

力扣题解链接:排序+双指针解决【三数之和】

题目描述:

1.1  题目解析

1.2  思路1:暴力

解法一:暴力求解(会超时)——

时间复杂度:O(n^3),空间复杂度:O(logn)。

1.3  思路2:排序 + 双指针

1.3.1  算法思路

本题与两数之和类似,是非常经典的面试题。与两数之和稍微不同的是,题目中要求找到所有【不重复】的三元组。那我们可以利用在两数之和那里用的双指针思想,来对我们的暴力枚举做优化:

1、先排序;

2、然后固定一个数a:

3、在这个数后面的区间内,使用「双指针算法」快速找到两个数之和等于a即可。

但是要注意的是,这道题里面需要有【去重】操作——

1、找到一个结果之后,left和[right指针要【跳过重复】的元素;

2、当使用完一次双指针算法之后,固定的a也要【跳过重复】的元素。

1.3.2  代码实现

代码演示:

class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int>> ret; //1、排序 sort(nums.begin(),nums.end()); //2、固定 int n = nums.size(); for(int i = 0;i < n;) { int left = i + 1,right = n - 1,target = -(nums[i]); while(left < right) { //a <= 0 if(nums[i] > 0)break; int sum = nums[left] + nums[right]; if(sum > target)right--; else if(sum < target)left++; else{ ret.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在这里移动,不在for循环 i++; while(i < n && nums[i] == nums[i - 1])i++; } return ret; } };
时间复杂度:O(n^3),空间复杂度:O(logn)。

1.4  过程推算

本题整个的思路、算法原理、解题过程博主在纸上推导了一遍,大家可以参考一下!

1.5  反思

总结:

1、没学到STL,vector<vector<int>> ret;和ret.push_back({nums[i],nums[left],nums[right]});这步根本想不到,直接来刷题了,对自己的实力没有清晰的认知(QAQ);

2、没想到固定的值a <= 0,这个常数级别的小优化想不到;

3、还是去重操作,都在一个条件判断下去重了,left和right在while(left < right)里面去重,i应该在它外面去重,因为刚才我已经让for循环的i++(i移动)这个操作搬到for里面来进行了,这样去重了,这样移动完之后,得到的i就是下一步我要固定的那个数了;

4、i移动的操作在while循环里面搞定,for循环for(int i = 0;i < n;)这个可以空着不写没有想到;

5、时间复杂度:O(n^2),for循环嵌套一个while循环;

6、空间复杂度:无限接近于O(logn)——只是在排序那里稍微占了点空间,压栈会占用logn的空间,其他都是只用了有限个变量。

008  四数之和

力扣链接:18. 四数之和

力扣题解链接:排序+双指针解决【四数之和】问题

题目描述:

2.1  题目解析

如下所示——

2.2  思路1:暴力

解法一:暴力求解(会超时)——

大家直接去看上面的【三数之和】:

2.3  思路2:排序 + 双指针

2.3.1  算法思路

1、依次固定一个数a;
2、在这个数a的后面区间上,利用【三数之和】找到三个数,使这三个数的和等于target - a即可,这里注意有两个区间,大的区间里面固定一个数b,小的区间里面定义一个left和right两个下标指针,里面的两数之和,加上a和b,加起来和target比。

2.3.2  代码实现

代码演示——

class Solution { public: vector<vector<int>> fourSum(vector<int>& nums, int target) { // 创建数组,存放返回值 vector<vector<int>> ret; // 1、排序 sort(nums.begin(), nums.end()); // 2、固定i int n = nums.size(); for (int i = 0; i < n;) { // 固定j for (int j = i + 1; j < n;) { // 3、双指针 int left = j + 1, right = n - 1; while (left < right) { // // a >= 0 // if (nums[i] > 0) // break; // // b >= 0 // if (nums[j] > 0) // break; long sum = nums[left] + nums[right]; long tmp = nums[i] + nums[j]; if (sum > (target - tmp)) right--; else if (sum < (target - tmp)) left++; else { ret.push_back( {nums[i], nums[j], 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--; } } // j查重,j移动 j++; while (j < n && nums[j] == nums[j - 1]) j++; } // i查重,i移动 i++; while (i < n && nums[i] == nums[i - 1]) i++; } return ret; } };
时间复杂度:O(n^3),空间复杂度:O(logn)。

2.4  过程推算

本题整个的思路、算法原理、解题过程博主在纸上推导了一遍,大家可以参考一下!

2.5  反思

1、整体做下来思路正确、逻辑没问题,这一点值得肯定(这里出的岔子就是在a <= 0,b <= 0的判断加上,就报错了);
2、没有自己独立地想到int类型遇到超级大的数据会怎么样,这里要在定义变量的时候把类型改成long,或者直接临时强制类型转换;
3、吸取了vector<vrctor<int>> ret;和ret.push_back({...});的教训,可以的!

结尾

往期回顾:

【优选算法必刷100题】第005~006题(双指针算法):有效三角形的个数、查找总价格为目标值的两个商品问题求解

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡

૮₍ ˶ ˊ ᴥ ˋ˶₎ა


Read more

Java项目CI/CD实战:Jenkins与GitLab CI深度解析

Java项目CI/CD实战:Jenkins与GitLab CI深度解析

基于多年Java实战,我将用8000字带你穿透概念,直击本质。不聊虚的,只讲真正影响你交付效率的核心决策、避坑经验和选型逻辑。从架构差异到性能表现,从企业落地到未来趋势,一文搞定CI/CD选型。 目录 开篇:CI/CD的务实之选 核心差异:设计哲学决定使用体验 Jenkins:插件驱动的“乐高大师” GitLab CI:一体化的“精装修” 性能对决:数字不说谎 构建性能对比 资源消耗分析 核心原理:技术实现的本质差异 Jenkins Pipeline:Groovy的力量与代价 GitLab CI:YAML的简洁与限制 企业实战:从选型到落地 选型决策框架 实施路线图 混合架构实践 性能优化:从分钟到秒级 Jenkins优化实战 GitLab CI优化技巧 企业级安全与合规 安全防护体系 访问控制策略 监控与故障排查 构建监控体系

By Ne0inhk
玩转ClaudeCode:ClaudeCode安装教程(Windows+Linux+MacOS)

玩转ClaudeCode:ClaudeCode安装教程(Windows+Linux+MacOS)

本文介绍如何安装 AI 编码界一骑绝尘的最强工具 ——— Claude Code。安装不同的操作系统环境,本文会从 Windows、Linux、Mac 三个不同的系统环境依次介绍安装方法。 其中,Windows 系统作为大家最主流的操作系统,提供了两种安装方式,一种方式是直接在 Windows 的终端里安装,另一种是在 Windows 的子系统(WSL)内完成安装。其中,通过 WSL 安装,我们又可以分为,WSL 环境的直装和基于 WSL 的容器化安装(Docker),几种方法各有利弊,但均可正常使用。 Windows 环境直装 Claude Code 1. 获取 Claude Code 账号 访问 Claude Code 中国镜像站,完成账户注册。 输入邀请码

By Ne0inhk
Flutter for OpenHarmony:Flutter 三方库 xdg_directories 遵循 Linux 系统目录规范的路径指南(鸿蒙底座兼容性探索)

Flutter for OpenHarmony:Flutter 三方库 xdg_directories 遵循 Linux 系统目录规范的路径指南(鸿蒙底座兼容性探索)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 随着 OpenHarmony 在桌面和平板设备上的不断普及,以及其底层与类 Unix / Linux 系统深厚的渊源,开发者在处理本地存储路径时,不仅要考虑手机端的“沙箱”,也需要考虑符合行业标准的系统目录规范(XDG Base Directory Specification)。 xdg_directories 是一个专门用于获取 Linux 系统环境变量定义的标准目录位置的工具库。它能帮你准确定位诸如:配置文件放在哪?缓存数据放在哪?虽然鸿蒙手机端有其特有的路径设计,但在鸿蒙桌面端或利用鸿蒙内核进行 Linux 兼容层开发时,它具有不可替代的规范指导意义。 一、核心概念:XDG 规范图解 XDG 规范定义了应用程序存储不同类型数据的位置,避免了在用户主目录下乱丢文件的乱象。 /home/user $XDG_CONFIG_HOME (.config) $XDG_CACHE_HOME

By Ne0inhk
精易模块图像处理与OCR实战:构建一个自动化验证码识别系统

精易模块图像处理与OCR实战:构建一个自动化验证码识别系统

精易模块图像处理与OCR实战:构建一个自动化验证码识别系统 22.1 引言 💡 各位易语言开发者朋友大家好!前几篇我们通过中小学生成绩管理系统巩固了精易模块Excel操作的核心知识点,通过多线程电商数据采集与分析系统掌握了网络爬虫和数据分析的方法。今天我要为大家带来一个结合图像处理、OCR识别、自动化操作的深度实战项目——精易模块图像处理与OCR实战:构建一个自动化验证码识别系统。 在网站登录、注册、密码找回等场景中,验证码是防止恶意攻击的重要手段,但手动输入验证码存在效率低、容易出错等问题。易语言配合精易模块的图像处理支持库和Tesseract OCR引擎,可以开发出功能完备、稳定可靠的自动化验证码识别系统,将验证码识别时间从手动的5秒/个缩短到系统的0.5秒/个,大大提高了工作效率。 22.1.1 项目背景 某电商运营团队每天需要登录多个电商平台的后台进行数据分析和操作,每个平台的登录都需要输入验证码,每天手动输入验证码的次数达到200+次,存在以下问题: * 手动输入效率低 * 容易出错(如验证码模糊、字符重叠等) * 工作强度大 * 无法实现自动化操作

By Ne0inhk