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

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

目录

【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

区块链|WEB3:时间长河共识算法(Time River Consensus Algorithm)

区块链|WEB3:时间长河共识算法(Time River Consensus Algorithm)

区块链|WEB3:时间长河共识算法(Time River Consensus Algorithm)(原命名为时间证明公式算法(TCC)) 本共识算法以「时间长河」为核心设计理念,通过时间节点服务器按固定最小时间间隔打包区块,构建不可篡改的历史数据链,兼顾区块链的金融属性与信用属性,所有优化机制形成完整闭环,无核心逻辑漏洞,具体总结如下: 一、核心机制(闭环无漏洞) 1. 节点准入与初始化:候选时间节点需先完成全链质押,首个时间节点由所有质押节点投票选举产生,彻底杜绝系统指定带来的初始中心化问题,实现去中心化初始化。 2. 时间节点推导与防作弊:下一任时间节点通过共同随机数算法从上一区块推导(输入参数:上一区块哈希、时间戳、固定数据顺序),推导规则公开可验证;时间节点需对数据顺序签名,任一节点发现作弊(篡改签名、操控随机数等),该节点立即失去时间节点资格并扣除全部质押。质押的核心目的是防止节点为持续获取区块打包奖励作弊,作弊损失远大于收益,确保共同随机数推导百分百不可作弊。 3. 节点容错机制:每个时间节点均配置一组合规质押节点构成的左侧顺邻节点队列(队列长度可随全网节点规

详解RabbitMQ工作模式之通配符模式

详解RabbitMQ工作模式之通配符模式

目录 通配符模式 概述 应用场景 优势 代码案例 引入依赖 常量类 编写生产者代码 编写消费者1代码 编写消费者2代码 运行代码 通配符模式 概述 通配符模式是一种灵活的消息传递模式,可以根据消息的路由键(routing key)和绑定(binding)模式来实现精确的消息过滤和匹配。在RabbitMQ中,路由键由生产者定义,用于标识消息的目的地;而绑定则由消费者定义,用于指定消息的接收规则。 路由模式的升级版, 在routingKey的基础上,增加了通配符的功能, 使之更加灵活. Topics和Routing的基本原理相同,即:⽣产者将消息发给交换机,交换机根据RoutingKey将消息转发给与RoutingKey匹配的队列. 类似于正则表达式的⽅式来定义Routingkey的模式. 不同之处是:routingKey的匹配⽅式不同,Routing模式是相等匹配,topics模式是通配符匹配. 应用场景 RabbitMQ的通配符模式在需要根据消息的特定属性进行路由和过滤的场景中非常有用。例如,在一个日志系统中,可以使用通配符模式来将不同级别的日

Windows三种网络类型详解:域网络、专用网络与公用网络的区别及对eNSP实验的影响

前言 在使用eNSP进行网络实验时,很多同学(比如我)都遇到过这样的问题:拓扑连接正确,IP配置也没问题,但设备之间就是ping不通。这很可能是因为Windows防火墙根据网络类型拦截了通信。(就好像手机声音开到最大发现还是没有声音,以为是手机坏了,检查了半天发现连的是蓝牙) 本文将详细解析Windows三种网络类型的区别,并提供针对eNSP实验的解决方案。 一、三种网络类型概述 Windows操作系统将网络连接分为三种类型,每种类型对应不同的安全级别和防火墙规则: 网络类型信任级别典型场景默认防火墙规则域网络🔒 最高企业内网、公司域环境相对宽松专用网络🔓 中等家庭网络、可信办公网络中等限制公用网络🚫 最严格咖啡厅、机场、酒店WiFi最严格限制 二、详细区别解析 2.1 域网络 (Domain Network) 适用场景: * 企业办公环境 * 已加入Windows域的计算机 * 受IT部门管理的网络 防火墙特性: bash 默认允许的服务较多 - 文件和打印机共享 - 远程桌面 - 网络发现 - 域控制器通信 安全策略: 由域组策略统

Spring Boot 事务详解

Spring Boot 事务详解 引言 在现代应用程序中,事务管理是确保数据一致性和完整性的重要机制。Spring Boot 提供了强大的事务管理功能,使得开发者可以轻松地定义和管理事务。本文将详细介绍 Spring Boot 中的事务管理,包括事务传播行为、事务属性以及声明式和编程式事务管理。 声明式事务管理 声明式事务管理是通过注解的方式来管理事务,最常用的注解是 @Transactional。这种方式简单直观,适合大多数场景。 示例 importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;@ServicepublicclassUserService{@TransactionalpublicvoidcreateUser(User user){// 数据库操作}} 编程式事务管理 编程式事务管理是通过编程的方式来手动控制事务,通常使用 PlatformTransactionManager 和 Transa