【八】【算法分析与设计】双指针(2)

11. 盛最多水的容器

给定一个长度为 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 <= 10(5)

  • 0 <= height[i] <= 10(4)

暴力求解:

 
class Solution {
public:
    int maxArea(vector<int>& height) {
        int n=height.size();
        int max=INT_MIN;
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                max=fmax(max,(j-i)*min(height[i],height[j]));
            }
        }

        return max;
    }
};

双指针优化暴力解法:

我们首先研究一小段区间[left,right]内最大的盛水体积。

先计算leftright的最大盛水体积,记为ret。如果height[left]<=height[right],那么对于left与任意其他下标组合,计算出来的体积都一定小于ret。因为left与其他任意下标组合(记为right1),right1-left一定小于rifht-left。而计算体积的高度是取决于高度小的那一个,高度h=min(height[right1],height[left])。体积v=(right1-left)*h。高度h=height[left],或者h<height[left]。所以体积v一定小于ret。也就是left与其他位置下标的组合都可以不用计算。

 
class Solution {
public:
    int maxArea(vector<int>& height) {
        int left = 0, right = height.size() - 1, ret = 0;
        while (left < right) {
            int v = (right - left) * fmin(height[left], height[right]);
            ret = max(ret, v);

            if (height[left] <= height[right]) {
                left++;
            } else
                right--;
        }

        return ret;
    }
};

函数接收一个整数类型的向量 height,向量的每个元素代表一个竖直线的高度,这些竖直线的宽度假设为1,函数返回由这些线形成的容器可以容纳的最大水量。

int left = 0, right = height.size() - 1, ret = 0;

初始化三个整数变量:left 设置为0,表示向量的起始索引;right 设置为 height.size() - 1,即向量的最末索引;ret 初始化为0,用于存储最大的水容量。

while (left < right) {

使用一个循环来遍历所有可能的容器。当 left 索引小于 right 索引时,循环继续。

int v = (right - left) * fmin(height[left], height[right]);

计算当前 leftright 索引之间形成的容器可以容纳的水量。水量由两个竖直线之间的距离(right - left)和两条线中较短一条的高度(fmin(height[left], height[right]))的乘积得出。

ret = max(ret, v);

更新 ret,使其始终保持为迄今为止找到的最大水容量。

if (height[left] <= height[right]) { left++; } else right--;

根据 leftright 索引处的高度比较结果,移动 leftright 索引。如果 left 处的高度小于等于 right 处的高度,则 left 索引向右移动(left++);否则,right 索引向左移动(right--)。这是基于贪心算法的思想,即保留较高的竖直线以期望找到更大的水容量。

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

时间复杂度:O(n),其中 n 是向量 height 的长度。这是因为我们只需要一次遍历就可以找到最大的水容量,left 从向量的起始位置向右移动,right 从向量的末尾位置向左移动,直到它们相遇。

空间复杂度:O(1),因为我们只使用了常数级别的额外空间,即几个变量来存储索引和计算结果。

611. 有效三角形的个数

给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

示例 1:

输入: nums = [2,2,3,4] 输出: 3 解释:有效的组合是: 2,3,4 (使用第一个 2) 2,3,4 (使用第二个 2) 2,2,3

示例 2:

输入: nums = [4,2,3,4] 输出: 4

提示:

  • 1 <= nums.length <= 1000

  • 0 <= nums[i] <= 1000

暴力枚举:

判断a,b,c三个数是否可以组成三角形,只需要满足a+b>c,a+c>b,b+c>a三个条件即可。如果a,b,c三个数满足a<=b<=c有序的条件,我们只需要判断a+b>c满足即可组成三角形。原因是a+c>bb+c>a是一定成立的,因为c已经大于等于ab了,再加上一个正数不等式一定成立。

所以我们的思路是先把nums数组排序,排序后再暴力枚举三个数。满足条件就ret++

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

        return ret;
    }
};

双指针优化暴力枚举:

先对nums数组排序。

研究[a,c]区间所有可以组成三角形的情况。首先固定最大的数c。记left=0,right=c-1

对于left,right,c三个数的组合,如果nums[left]+nums[right]>c说明可以组成三角形。对于left+1,right,c三个数,一定也可以组成三角形。因为left+1>left,而函数是递增的。所以nums[left+1]>nums[left],nums[left+1]+nums[left]>c。以此类推,对于与right的组合,一共有right-left个数可以组成三角形。考虑完与right的组合所有情况,就right--,不考虑right的组合情况了。

如果nums[left]+nums[right]<=c,说明不可以组成三角形。对于left,right-1,c三个数,一定也不可以组成三角形。因为right-1<right,而函数是递增的。所以nums[right-1]<nums[right],nums[right-1]+nums[left]<c。以此类推,对于与left的组合,都不可以组成三角形。考虑完与left的组合所有情况,就left++,不考虑left的组合情况了。

 
class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());

        int ret = 0, n = nums.size();
        for (int i = n - 1; i >= 2; i--) {
            int left = 0, right = i - 1;
            while (left < right) {
                if (nums[left] + nums[right] > nums[i]) {
                    ret += right - left;
                    right--;
                } else {
                    left++;
                }
            }
        }
        return ret;
    }
};

首先对数组 nums 进行排序,这样我们可以利用三角形两边之和大于第三边的性质来简化问题。

int ret = 0, n = nums.size();

声明并初始化变量 ret 为0,它将用来计数可以构成三角形的三元组数量。变量 n 存储数组 nums 的大小。

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

从数组的最后一个元素开始向前遍历,直到第三个元素(索引为2)。这是因为我们至少需要三个数来构成一个三角形。这个遍历的是最大的边长的所有可能。

int left = 0, right = i - 1;

在每次循环中,初始化两个指针 leftright,分别指向当前考察的三元组中最小元素的索引和次大元素的索引。

while (left < right) {

left 指针小于 right 指针时,执行循环体内的操作。这个循环用于找出在固定最大边 nums[i] 的情况下,有多少对 (nums[left], nums[right]) 能与 nums[i] 构成三角形。

if (nums[left] + nums[right] > nums[i]) { ret += right - left; right--; } else { left++; }

如果 nums[left]nums[right] 之和大于 nums[i],则说明找到了一组有效的三元组,因为数组已经排序,所以从 leftright 之间的所有数都可以与 nums[right]nums[i] 构成三角形。因此,将 right - left 的值累加到 ret 中,然后将 right 指针向左移动一位,以考察下一对可能的三元组。否则,如果两数之和不大于 nums[i],与left的所有组合都无法组成三角形,则将 left 指针向右移动一位,增大两数之和的值。

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

时间复杂度:O(n^2),其中 n 是数组 nums 的长度。这是因为我们首先对数组进行了排序,这需要 O(n log n) 的时间。之后,我们有一个外层循环(O(n))和一个内层循环(O(n)),外层和内层循环的组合将会产生 O(n^2) 的时间复杂度。

空间复杂度:O(log n),这是排序所需要的空间复杂度。除了排序之外,我们只使用了几个变量来存储索引和计算结果。

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

购物车内的商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。

示例 1:

输入:price = [3, 9, 12, 15], target = 18 输出:[3,15] 或者 [15,3]

示例 2:

输入:price = [8, 21, 27, 34, 52, 66], target = 61 输出:[27,34] 或者 [34,27]

提示:

  • 1 <= price.length <= 10^5

  • 1 <= price[i] <= 10^6

  • 1 <= target <= 2*10^6

暴力枚举:

 
class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target) {
        int n = price.size();
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (price[i] + price[j] == target)
                    return {price[i], price[j]};
            }
        }

        return {-1, -1};
    }
};

双指针优化暴力枚举:

只考虑[left,right]区间内的数据。

如果price[left]+price[right]>target,那么对于right与其他位置的数据的组合,一定也大于target,因为函数是递增的。因此对于right与其他位置的数据不需要再考虑。right--。

如果price[left]+price[right]<target,那么对于left与其他位置的数据的组合,一定也小于target,因为函数是递增的。因此对于left与其他位置的数据不需要再考虑,left++。

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

        return {-1, -1};
    }
};

int left = 0, right = price.size() - 1;

初始化两个指针 leftright,分别指向数组 price 的开始和结束位置。

while (left < right) {

使用一个循环来查找两个数。当 left 指针小于 right 指针时,循环继续。

int sum = price[left] + price[right];

计算 leftright 指针指向的两个数的和。

if (sum > target) { right--;

如果计算出的和大于 target,则将 right 指针向左移动,以减小和的值。对于right与其他任何位置的组合,sum一定也大于target,所以不需要再考虑与right有关的组合。

} else if (sum < target) { left++;

如果计算出的和小于 target,则将 left 指针向右移动,以增大和的值。对于left与其他任何位置的组合,sum一定也小于target,所以不需要再考虑与left有关的组合。

} else return {price[left], price[right]};

如果计算出的和等于 target,则返回一个包含这两个数的数组。

return {-1, -1};

如果在循环结束时没有找到符合条件的两个数,则返回 {-1, -1}。这个操作是为了迎合编译器,因为题目意思显然是一定存在两个数之和为target。因此在while语句中一定会返回,但是如果不在外面写return,编译器会认为这个代码可能存在无返回值。因此在外面也写return是为了迎合编译器。

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

时间复杂度:O(n),其中 n 是数组 price 的长度。这是因为我们使用了双指针方法遍历数组,每个元素最多被访问一次。

空间复杂度:O(1),因为我们只使用了常数级别的额外空间,即几个变量来存储索引和计算结果,不需要额外的空间来存储数据。

结尾

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pytorch 入门基础知识一(Pytorch 01)

一 深度学习基础相关 深度学习三个主要的方向&#xff1a;计算机视觉&#xff0c;自然语言&#xff0c;语音识别。 机器学习核心组件&#xff1a;1 数据集(data)&#xff0c;2 前向传播的model(net)&#xff0c;3 目标函数(loss)&#xff0c; 4 调整模型参数和优化函数的算法…

Ascend C编程入门课:编程基础与Hello World

Ascend C编程入门课 一、基础概念 1.Ascend C&#xff1a;是昇腾异构计算架构CANN针对算子开发场景推出的编程语言&#xff0c;通过多层接口抽象、自动并行计算、孪生调试等关键技术&#xff0c;极大提高算子开发效率。 2.使用Ascend C自定义开发算子的优势&#xff1a; (1…

Golang协程详解

一.协程的引入 1.通过案例文章引入并发,协程概念 见:[go学习笔记.第十四章.协程和管道] 1.协程的引入,调度模型&#xff0c;协程资源竞争问题 通过上面文章可以总结出Go并发编程原理: 在一个处理进程中通过关键字 go 启用多个协程&#xff0c;然后在不同的协程中完成不同的子任…

【网络编程基础(一)】网络基础和SOCKET

这里写目录标题 1、网络三要素2、IPV4和IPV6区别3、网络交互3.1、交互模型图3.2、基础通信协议3.3、OSI参考模型与TCP/IP参考模型对应关系 4、SOCKET网络套接字4.1、SOCKET分类4.2、基于流式套接字的编程流程4.3、网络通信雏形4.4、socket函数4.4.1、socket函数示例 4.5、bind函…

部署一个本地的ChatGPT(Ollama)

一 下载Ollama Ollama下载地址&#xff1a;https://ollama.com/download 下载完后 二 安装运行 双击下载好的OllamaSetup.exe开发 安装Ollama: 安装完成后&#xff0c;多了一个Ollama的菜单如下图 &#xff1a; Ollama安装好默认是配置开机运行&#xff0c;如果没有运行可以在…

高效使用 JMeter 生成随机数:探索 Random 和 UUID 算法

在压力测试中&#xff0c;经常需要生成随机值来模拟用户行为。JMeter 提供了多种方式来生成随机值&#xff0c;本文来具体介绍一下。 随机数函数 JMeter 提供了多个用于生成随机数的函数&#xff0c;其中最常用的是__Random函数。该函数可以生成一个指定范围内的随机整数或浮…

pytorch 实现线性回归(Pytorch 03)

一 线性回归框架 线性模型的四个模块&#xff1a;训练的数据集&#xff0c;线性模型&#xff0c;损失函数&#xff0c;优化算法。 1.1 数据集 使用房价预测数据集&#xff0c;我们希望根据房屋的面积和房龄等来估算房屋价格。 1.2 线性模型 预测公式&#xff0c; 价格 权重…

智慧公厕建设的重要性

智慧公厕建设一直被视为提升城市管理水平的重要举措。作为一个关键的城市基础设施&#xff0c;智慧公厕不仅可以改善公共卫生管理&#xff0c;还能提升城市居民的社会民生生活质量。此外&#xff0c;智慧公厕建设还能促进城市的可持续发展&#xff0c;降低能源消耗&#xff0c;…

rust学习笔记(1-7)

原文 8万字带你入门Rust 1.包管理工具Cargo 新建项目 1&#xff09;打开 cmd 输入命令查看 cargo 版本 cargo --version2&#xff09; 使用 cargo new 项目名 在文件夹&#xff0c;按 shift 鼠标右键 &#xff0c;打开命令行&#xff0c;运行如下命令&#xff0c;即可创建…

【SQL】1193. 每月交易 I 【年月日(日期)拼接相关函数】

前述 知识点学习&#xff1a; SQL 日期函数 day() 、month()、year() 各种使用方法mysql 两个字符年月拼接 题目描述 leetcode题目&#xff1a;1193. 每月交易 I 思路 先按照年月排&#xff0c;再按照country排列 日期拼接相关的函数 year(): 截取年份&#xff1b;month…

linuxOPS基础_linux命令合集

uname查看操作系统信息 命令&#xff1a;uname [参数] 作用&#xff1a;获取计算机操作系统相关信息 参数&#xff1a;-a&#xff0c;选项-a代表all&#xff0c;表示获取全部的系统信息&#xff08;类型、全部主机名、内核版本、发布时间、开源计划&#xff09; 用法一&…

将OpenCV与gcc和CMake结合使用

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV4.9.0开源计算机视觉库在 Linux 中安装 下一篇&#xff1a; 引言&#xff1a; 近年来&#xff0c;计算机视觉技术在图像处理、目标检测和机器人等方面得到了广泛的应用…

基于FPGA的光纤通信系统设计

文章目录 光纤通信系统的组成发送端FPGA端口定义状态机设计代码示例 接收端功能模块端口定义状态机设计 光纤通信系统的组成 发送端FPGA 发送控制逻辑、数据编码、校验码生成、缓存控制、时钟控制 端口定义 状态机设计 代码示例 接收端功能模块 接收端控制逻辑、数据解码、…

前世档案(不用二叉树语法秒杀版c++)

网络世界中时常会遇到这类滑稽的算命小程序&#xff0c;实现原理很简单&#xff0c;随便设计几个问题&#xff0c;根据玩家对每个问题的回答选择一条判断树中的路径&#xff08;如下图所示&#xff09;&#xff0c;结论就是路径终点对应的那个结点。 现在我们把结论从左到右顺序…

综合知识篇06-软件架构设计考点(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html案例分析篇00-【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例…

javaEE——线程的等待和结束

文章目录 Thread 类及常见方法启动一个线程中断一个线程变量型中断调用 interrupt() 方法来通知观察标志位是否被清除 等待一个线程获取当前线程引用休眠当前线程 线程的状态观察线程的所有状态观察 1: 关注 NEW 、 RUNNABLE 、 TERMINATED 状态的切换 多线程带来的风险为什么会…

java serlvet 高校学生画像平台系统Myeclipse开发mysql数据库web结构java编程计算机网页项目echarts图形展现

一、源码特点 java serlvet 高校学生画像平台系统是一套完善的java web信息管理系统 系统采用serlvetdaobean 模式开发本系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCA…

Selenium基础

1. selenium简介 用于实现自动化测试的 python 包&#xff0c;使用前需要安装对应浏览器驱动 from time import sleep from selenium import webdriver option webdriver.ChromeOptions() # 指定chrome存储路径的二进制形式 option.binary_locationD:\Chrome\Google\Chrome\Ap…

Android VINF

周末搞这玩意欲仙欲死&#xff0c;没办法只有看看。VINTF是供应商接口对象&#xff08;VINTF 对象&#xff09;&#xff0c;准确的说&#xff0c;这个是属于兼容性矩阵概念。。。有点想起了以前看过的一个电影&#xff0c;异次元杀阵。。。下面是谷歌官方的图。 本质上其实就是…

【分布式websocket 】前端vuex管理客户端消息crud!使用localStorage来存储【第19期】

前言 聊天系统客户端是要存储消息的&#xff0c;因为所有所有的历史消息都从服务器拉的话一方面服务器压力大&#xff0c;另一方面也耗费用户流量。所以客户端存储消息是势在必行的。如何存储呢上一篇文章也写了&#xff0c;大概就是浏览器的话是localStorage或者IndexedDB。然…