【优选算法】双指针算法:专题二

【优选算法】双指针算法:专题二

目录

【611.有效三角形个数】

1、题目描述

2、实现核心及思路

解题步骤:

思路可视化:

代码实现:

【179.查找总价格为目标值的两个商品】

1、题目描述:

2、实现核心及思路:

代码实现:

【15.三数之和】

1、题目描述:

2、实现核心及思路:

解题步骤:

思路可视化:

代码实现:

【18.四数之和】

1、题目描述:

​编辑2、实现核心即思路:

解题步骤:

代码实现:


【611.有效三角形个数】

1、题目描述

2、实现核心及思路

构成三角形的条件:设三角形三边长分别为a(最长边),b(最短边),c。

则有 a + b > c

通过对三角形三边关系的分析,问题就是怎样找到三角形的最长边和最短边。解决这个问题:

我们可以先将数组元素进行排序(升序),让其满足单调性。每次固定最大值为第三边,最左边元素作为最短边,次最大值作为最长边,让最短边和最长边求和并与第三边比较。
解题步骤:

(1)排序(升序),降序也可。

(2)从右往左将数组元素依次作为第三边;让左指针 left 指向最左侧元素,作为最短边;右指针 right 指向第三边左侧第一个元素,作为最长边。

注意:由于需要三个边构成三角形,则第三边最多为数组的第三个元素(nums[2])。

(3)最短边与最长边求和并与第三边比较。

注意:

• 
由于已经按照升序排序,只要 nums[left] + nums[right] > 第三边,说明只要将左侧元素作为最短边均满足要求,则此时有满足要求的 right - left 个三角形(更新结果) ,然后

right--,因为nums[left] + nums[right--] 仍有可能满足要求



• 如果 nums[left] + nums[right] <= 第三边,则只有让 left++,才有可能让最短边与最长边之和大于第三边(right--只能让两者之和更小)。



结束条件:right >= left。
思路可视化:
代码实现:
class Solution { public: int triangleNumber(vector<int>& nums) { sort(nums.begin(),nums.end()); // 升序排序 int count = 0; // 记录三角形个数 // 从右往左依次将最大值作为第三边 for(int i = nums.size()-1;i >= 2;i--) { int left = 0; // 左指针(最短边) int right = i-1; // 右指针(最长边) while(left < right) { if(nums[left] + nums[right] > nums[i]) // 两边之和大于第三边 { count += right-left; // 更新结果 right--; // 寻找其他可能结果 } else left++; // 寻找与最长边相加可能大于第三边的最短边 } } return count; } };



【179.查找总价格为目标值的两个商品】

1、题目描述:

2、实现核心及思路:

实际就是求两数之和满足目标值,暴力算法非常简单,但可能超时。

与上一题类似我们也是结合单调性与双指针求解

(1)排序(默认升序),但注意数组可能已经有序(本题已经为升序数组)。

(2)让左指针 left 指向最左边元素(最小值),右指针 right 指向最右边元素(最大值)。

(3)两数求和,并与目标值比较:

• 和小于目标值,left++,只有这样才可能让和等于目标值(right-- 只能使得和更小);

• 和大于目标值,right--,只有这样才有可能让和等于目标值(left++ 只能使得和更大);

(4)输出结果。

代码实现:
class Solution { public: vector<int> twoSum(vector<int>& price, int target) { int left = 0, right = price.size() - 1; while(left < right) { if(price[left] + price[right] < target) left++; else if(price[left] + price[right] > target) right--; else break; } return {price[left], price[right]}; } };

【15.三数之和】

1、题目描述:

2、实现核心及思路:

要 求满足要求的三数之和,我们可以这样处理:nums[ i ] + nums[ j ] + nums[ k ] = 0,即

nums[ i ] + nums[ j ] = -nums[ k ],我们可以将 -nums[ k ]作为 target ,这样就转化为了求两数之和的问题。
解题步骤:

(1)排序(默认升序);

(2)从左往右依次固定一个元素作为 target,并让左指针 left 指向value 左侧元素;右指针 right 指向最右边元素。当 nums[ left ] + nums[ right ] = -target 时,即此三数满足要求(更新结果);

• 若 和小于目标值(-target),left++,只有这样才可能让和等于目标值(right-- 只能使得和更小);

• 若 和大于目标值(-target),right--,只有这样才有可能让和等于目标值(left++ 只能使得和更大);

• 更新结果,让left++,right--,继续找。



循环条件:
left < right。
注意:最后结果不可重复,所以需要去重

• 方法一:而由于数组元素升序排序的原因,对于重复的结果,其顺序也相同。所以,我们可以用 set<vector<int>> 来存储结果(set可以去重)。

• 方法二:当更新完结果后让左指针 left 和右指针 right 跳过相同的值;同时,在固定目标值时也需要跳过相同的值。



💥技巧:由于我们按照升序排序,即单调递增,当我们固定的target > 0(为正)时,往右所有的数肯定都已经大于0了,就不用找了。
思路可视化:

代码实现:
class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { sort(nums.begin(), nums.end()); // 排序 vector<vector<int>> ret; // 存储结果 // 从左往右依次固定元素为target for(int i = 0; i < nums.size() - 2;) { int target = nums[i]; // 目标值 int left = i + 1, right = nums.size() - 1; // 左右指针 while(left < right && target <= 0) { if (nums[left] + nums[right] < -target) left++; else if (nums[left] + nums[right] > -target) right--; else { ret.push_back({ target, nums[left], nums[right] }); // 更新结果 left++, right--; // 去重 while(left < right && nums[left] == nums[left - 1]) left++; while(left < right && nums[right] == nums[right + 1]) right--; } } i++; // 去重target while(i < nums.size() - 2 && nums[i] == nums[i - 1]) i++; } return ret; } };

【18.四数之和】

1、题目描述:


2、实现核心即思路:

a + b + c + d = target

求满足要求的四个数,我们可以先依次固定一个数作为 a,那么就变成找三个的和等于

target - a,不就转化为对应三数之和的问题了。
解题步骤:

(1)排序(升序);

(2)在数组中从左往右依次固定一个数作为a,转化为三数之和问题(target - a),然后在数组剩下元素中找满足的三个数。

(3)稍微修改上面的三数之和的代码,此时目标值为 target - a,再有两个参数nums 和pos(用来确定数组还剩多少元素)。

代码实现:
class Solution { public: // 找满足三数之和的数 vector<vector<int>> threeSum(vector<int>& nums, int pos, int target_val) { vector<vector<int>> ret; // 存储结果 // 从左往右依次固定元素为target int n = nums.size() - 2; for(int i = pos + 1; i < n;) { long target = (long)nums[i] - (long)target_val; // 目标值 int left = i + 1, right = nums.size() - 1; // 左右指针 while(left < right) { if (nums[left] + nums[right] < -target) left++; else if (nums[left] + nums[right] > -target) right--; else { ret.push_back({ nums[i], nums[left], nums[right] }); // 更新结果 left++, right--; // 去重 while(left < right && nums[left] == nums[left - 1]) left++; while(left < right && nums[right] == nums[right + 1]) right--; } } i++; // 去重target while(i < nums.size() - 2 && nums[i] == nums[i - 1]) i++; } return ret; } vector<vector<int>> fourSum(vector<int>& nums, int target) { sort(nums.begin(), nums.end()); // 排序 vector<vector<int>> result; // 结果 // 固定一个数作为目标元素之一 int n = nums.size() - 3; for(int i = 0; i < n;) { auto ret = threeSum(nums, i, target - nums[i]); if(!ret.empty()) { // 重新完善结果 for(auto e : ret) { e.push_back(nums[i]); result.push_back(e); } } i++; // 去重 i while(i < nums.size() - 3 && nums[i] == nums[i - 1]) i++; } return result; } };

Read more

Flutter 三方库 hashids2 基于鸿蒙安全内核的深度隐匿映射适配:数字指纹泄露防御层、生成短小精悍唯一不可逆加盐哈希,护航全链路请求 URL 隐私-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 hashids2 基于鸿蒙安全内核的深度隐匿映射适配:数字指纹泄露防御层、生成短小精悍唯一不可逆加盐哈希,护航全链路请求 URL 隐私-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 hashids2 基于鸿蒙安全内核的深度隐匿映射适配:突破高敏感数字指纹泄露防御层、生成短小精悍唯一不可逆加盐哈希,护航全链路请求 URL 隐私资产 在鸿蒙应用的高度依赖数据隐私(如隐藏数据库递增 ID、生成短网址或混淆用户主页链接)中,如何将枯燥的数字转换为非连续、看似随机且人类友好的标识符?hashids2 库提供了一套基于 Hashids 协议的工业级加密 ID 生成方案。本文将详解该库在 OpenHarmony 上的适配要点。 前言 什么是 hashids2?当你在 URL 中展示 user/123 时,攻击者很容易通过猜测 124 或 125 来爬取你的数据。hashids2 能够根据你设定的盐值(Salt)。将整数 123 转换为类似

By Ne0inhk
单双序列问题——动态规划

单双序列问题——动态规划

文章目录 * 一、最长递增子序列 * 二、等差数列划分II-子序列 * 三、最长公共子序列 * 四、正则表达式匹配 动态规划是解决复杂算法问题的利器,本文将聚焦于单序列与双序列两类经典问题,通过分析最长递增子序列、正则表达式匹配等典型案例,深入剖析动态规划的状态定义与转移方程构建思路。 在阅读该文章时最好对基础的动态规划有所了解,因为在此不会讲解动态规划基础的细节,大家可以通过阅读下文进行学习:基础dp——动态规划多状态dp——动态规划子数组问题——动态规划 单序列问题往往具备两个关键特征,使其特别适合用动态规划求解。 * 问题解决路径需拆解为多个步骤,每个步骤都存在多种选择,最终目标是计算可行解的总数,或是找到满足条件的最优解。 * 问题的输入数据通常呈现为序列形态,比如一维数组、字符串等典型的线性数据结构。 根据题目的特点找出该元素对应的最优解(或解的数目)和前面若干元素(通常是一个或两个)的最优解(或解的数目)的关系,并以此找出相应的状态转移方程。一旦找出了状态转移方程,只要注意避免不必要的重复计算,问题就能迎刃而解。 下面讲解两个适合运用动态规划的单序

By Ne0inhk
数据结构:kmp算法,Trie树,以及并查集的干货详解---小白也能看懂

数据结构:kmp算法,Trie树,以及并查集的干货详解---小白也能看懂

🎬 博主名称:个人主页 🔥 个人专栏: 《算法通关》,《Java讲解》 ⛺️心简单,世界就简单 序言 昨晚数据结构写了一半,做图太累了,文章写的比较慢,这篇应该就是第二篇,后面还有一篇,太困了,真不行了 今天讲一下 kmp算法,Trie, 并查集 目录 序言 KMP算法 原理 next数组 匹配过程 Trie树 并查集 KMP算法 这里说一下kmp的大致情况 用于处理字符串匹配问题,他也是十分的抽象                给一个S[]主串(比较长的那个),P[]为模板串,kmp我们一般用下标1来开始遍历 接下来我们需要去思考的是 1,暴力去怎么做 2,怎么去优化 下面是暴力的模板代码 大概意思就是,每当我们匹配到不一样的部位,我们的P就要从头开始再跟刚刚s的起点+1位置重新匹配,        这样就会造成串的长度很长时候,就会超时,所以我们就要从这个过程中找性质了

By Ne0inhk