Leetcode376. 摆动序列

Every day a Leetcode

题目来源:376. 摆动序列

解法1:动态规划

约定:

  1. 某个序列被称为「上升摆动序列」,当且仅当该序列是摆动序列,且最后一个元素呈上升趋势。
  2. 某个序列被称为「下降摆动序列」,当且仅当该序列是摆动序列,且最后一个元素呈下降趋势。
  3. 特别地,对于长度为 1 的序列,它既是「上升摆动序列」,也是「下降摆动序列」。
  4. 序列中的某个元素被称为「峰」,当且仅当该元素两侧的相邻元素均小于它。
  5. 序列中的某个元素被称为「谷」,当且仅当该元素两侧的相邻元素均大于它。
  6. 特别地,对于位于序列两端的元素,只有一侧的相邻元素小于或大于它,我们也称其为「峰」或「谷」。
  7. 对于序列中既非「峰」也非「谷」的元素,我们称其为「过渡元素」。

状态数组:

  1. up[i]:表示以前 i 个元素中的某一个为结尾的最长的「上升摆动序列」的长度。
  2. down[i]:表示以前 i 个元素中的某一个为结尾的最长的「下降摆动序列」的长度。

最终的状态转移方程为:

在这里插入图片描述

最终的答案即为 up[n−1] 和 down[n−1] 中的较大值,其中 n 是序列的长度。

代码:

/** @lc app=leetcode.cn id=376 lang=cpp** [376] 摆动序列*/// @lc code=start
class Solution
{
public:int wiggleMaxLength(vector<int> &nums){// 特判if (nums.size() <= 1)return nums.size();int n = nums.size();// 状态数组// up[i]:表示以前i个元素中的某一个为结尾的最长的「上升摆动序列」的长度vector<int> up(n + 1, 0);// down[i]:表示以前i个元素中的某一个为结尾的最长的「下降摆动序列」的长度vector<int> down(n + 1, 0);// 初始化// 对于长度为1的序列,它既是「上升摆动序列」,也是「下降摆动序列」up[1] = down[1] = 1;// 状态转移for (int i = 2; i <= n; i++){if (nums[i - 1] > nums[i - 2]){up[i] = max(up[i - 1], down[i - 1] + 1);down[i] = down[i - 1];}else if (nums[i - 1] < nums[i - 2]){up[i] = up[i - 1];down[i] = max(up[i - 1] + 1, down[i - 1]);}else{up[i] = up[i - 1];down[i] = down[i - 1];}}return max(up[n], down[n]);}
};
// @lc code=end

结果:

在这里插入图片描述

复杂度分析:

时间复杂度:O(n),其中 n 是序列的长度。我们只需要遍历该序列一次。

空间复杂度:O(n),其中 n 是序列的长度。我们需要开辟两个长度为 n 的数组。

空间优化

代码:

/** @lc app=leetcode.cn id=376 lang=cpp** [376] 摆动序列*/// @lc code=start// 动态规划// class Solution
// {
// public:
//     int wiggleMaxLength(vector<int> &nums)
//     {
//         // 特判
//         if (nums.size() <= 1)
//             return nums.size();
//         int n = nums.size();
//         // 状态数组
//         // up[i]:表示以前i个元素中的某一个为结尾的最长的「上升摆动序列」的长度
//         vector<int> up(n + 1, 0);
//         // down[i]:表示以前i个元素中的某一个为结尾的最长的「下降摆动序列」的长度
//         vector<int> down(n + 1, 0);
//         // 初始化
//         // 对于长度为1的序列,它既是「上升摆动序列」,也是「下降摆动序列」
//         up[1] = down[1] = 1;
//         // 状态转移
//         for (int i = 2; i <= n; i++)
//         {
//             if (nums[i - 1] > nums[i - 2])
//             {
//                 up[i] = max(up[i - 1], down[i - 1] + 1);
//                 down[i] = down[i - 1];
//             }
//             else if (nums[i - 1] < nums[i - 2])
//             {
//                 up[i] = up[i - 1];
//                 down[i] = max(up[i - 1] + 1, down[i - 1]);
//             }
//             else
//             {
//                 up[i] = up[i - 1];
//                 down[i] = down[i - 1];
//             }
//         }
//         return max(up[n], down[n]);
//     }
// };// 动态规划-空间优化class Solution
{
public:int wiggleMaxLength(vector<int> &nums){// 特判if (nums.size() <= 1)return nums.size();int n = nums.size();int up = 1, down = 1;// 状态转移for (int i = 1; i < n; i++){if (nums[i] > nums[i - 1])up = max(up, down + 1);else if (nums[i] < nums[i - 1])down = max(down, up + 1);}return max(up, down);}
};
// @lc code=end

结果:

在这里插入图片描述

解法2:贪心

观察这个序列可以发现,我们不断地交错选择「峰」与「谷」,可以使得该序列尽可能长。证明非常简单:如果我们选择了一个「过渡元素」,那么在原序列中,这个「过渡元素」的两侧有一个「峰」和一个「谷」。不失一般性,我们假设在原序列中的出现顺序为「峰」「过渡元素」「谷」。如果「过渡元素」在选择的序列中小于其两侧的元素,那么「谷」一定没有在选择的序列中出现,我们可以将「过渡元素」替换成「谷」;同理,如果「过渡元素」在选择的序列中大于其两侧的元素,那么「峰」一定没有在选择的序列中出现,我们可以将「过渡元素」替换成「峰」。这样一来,我们总可以将任意满足要求的序列中的所有「过渡元素」替换成「峰」或「谷」。并且由于我们不断地交错选择「峰」与「谷」的方法就可以满足要求,因此这种选择方法就一定可以达到可选元素数量的最大值。

这样,我们只需要统计该序列中「峰」与「谷」的数量即可(注意序列两端的数也是「峰」或「谷」),但需要注意处理相邻的相同元素。

在实际代码中,我们记录当前序列的上升下降趋势。每次加入一个新元素时,用新的上升下降趋势与之前对比,如果出现了「峰」或「谷」,答案加一,并更新当前序列的上升下降趋势。

代码:

/** @lc app=leetcode.cn id=376 lang=cpp** [376] 摆动序列*/// @lc code=start// 动态规划// class Solution
// {
// public:
//     int wiggleMaxLength(vector<int> &nums)
//     {
//         // 特判
//         if (nums.size() <= 1)
//             return nums.size();
//         int n = nums.size();
//         // 状态数组
//         // up[i]:表示以前i个元素中的某一个为结尾的最长的「上升摆动序列」的长度
//         vector<int> up(n + 1, 0);
//         // down[i]:表示以前i个元素中的某一个为结尾的最长的「下降摆动序列」的长度
//         vector<int> down(n + 1, 0);
//         // 初始化
//         // 对于长度为1的序列,它既是「上升摆动序列」,也是「下降摆动序列」
//         up[1] = down[1] = 1;
//         // 状态转移
//         for (int i = 2; i <= n; i++)
//         {
//             if (nums[i - 1] > nums[i - 2])
//             {
//                 up[i] = max(up[i - 1], down[i - 1] + 1);
//                 down[i] = down[i - 1];
//             }
//             else if (nums[i - 1] < nums[i - 2])
//             {
//                 up[i] = up[i - 1];
//                 down[i] = max(up[i - 1] + 1, down[i - 1]);
//             }
//             else
//             {
//                 up[i] = up[i - 1];
//                 down[i] = down[i - 1];
//             }
//         }
//         return max(up[n], down[n]);
//     }
// };// 动态规划-空间优化// class Solution
// {
// public:
//     int wiggleMaxLength(vector<int> &nums)
//     {
//         // 特判
//         if (nums.size() <= 1)
//             return nums.size();
//         int n = nums.size();
//         int up = 1, down = 1;
//         // 状态转移
//         for (int i = 1; i < n; i++)
//         {
//             if (nums[i] > nums[i - 1])
//                 up = max(up, down + 1);
//             else if (nums[i] < nums[i - 1])
//                 down = max(down, up + 1);
//         }
//         return max(up, down);
//     }
// };// 贪心class Solution
{
public:int wiggleMaxLength(vector<int> &nums){// 特判if (nums.size() <= 1)return nums.size();int n = nums.size();int preDiff = nums[1] - nums[0];int ans = preDiff != 0 ? 2 : 1;for (int i = 2; i < n; i++){int diff = nums[i] - nums[i - 1];if ((diff > 0 && preDiff <= 0) || (diff < 0 && preDiff >= 0)){ans++;preDiff = diff;}}return ans;}
};
// @lc code=end

结果:

在这里插入图片描述

复杂度分析:

时间复杂度:O(n),其中 n 是序列的长度。我们只需要遍历该序列一次。

空间复杂度:O(1)。我们只需要常数空间来存放若干变量。

第二种贪心思路:统计变化次数

代码:

// 贪心2class Solution
{
public:int wiggleMaxLength(vector<int> &nums){// 特判if (nums.size() <= 1)return nums.size();int n = nums.size();int direction = 0; //-1表示下降,1表示上升int count = 0;     // 变化次数for (int i = 1; i < n; i++){if (nums[i] > nums[i - 1]){if (direction != 1){direction = 1;count++;}}else if (nums[i] < nums[i - 1]){if (direction != -1){direction = -1;count++;}}}// 因为统计的是变化的次数,最终的结果是序列的长度,所以需要+1return count + 1;}
};

结果:

在这里插入图片描述

复杂度分析:

时间复杂度:O(n),其中 n 是序列的长度。我们只需要遍历该序列一次。

空间复杂度:O(1)。我们只需要常数空间来存放若干变量。

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

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

相关文章

C++vector模拟实现

vector模拟实现 1.构造函数2.拷贝构造3.析构赋值运算符重载4.iterator5.modifiers5.1push_back5.2pop_back5.3empty5.4insert5.5erase5.6swap 6.Capacity6.1size6.2capacity6.3reserve6.4resize6.5empty 7.Element access7.1operator[]7.2at 8.在谈reserve vector官方库实现的是…

SQL11 高级操作符练习(1)

描述 题目&#xff1a;现在运营想要找到男性且GPA在3.5以上(不包括3.5)的用户进行调研&#xff0c;请你取出相关数据。 示例&#xff1a;user_profile iddevice_idgenderageuniversitygpa12138male21北京大学3.423214male复旦大学4.036543female20北京大学3.242315female23浙…

向量范数及其Python代码

【向量范数】 向量由于既有大小又有方向&#xff0c;所以不能直接比较大小。 向量范数通过将向量转化为实数&#xff0c;然后进行向量的大小比较。 所以&#xff0c;向量范数是用于度量“向量大小”的量。 设向量 &#xff0c;则有&#xff1a; ● 向量的 范数&#xff1a; ●…

Python计算机Python二级知识点整理

1. 此时我们这里首先解析一下这个d[A]N,根据ASCII表&#xff0c;我们可以看出字符A对应的十进制数字是65&#xff0c;ord()函数是把字符转换为相对应的ASCII码&#xff0c;chr()函数是ord()函数的逆运算&#xff0c;所以ord("A")65 ,chr(65)A,题目中首先定义了d为一…

C++毕业设计基于QT实现的超市收银管理系统源代码+数据库

C毕业设计基于QT实现的超市收银管理系统源代码数据库 编译使用 编译完成后&#xff0c;需要拷贝 file目录下的数据库 POP.db文件到可执行程序目录下 登录界面 主界面 会员管理 完整代码下载地址&#xff1a;基于QT实现的超市收银管理系统源代码数据库

笔记本多拓展出一个屏幕

一、首先要知道&#xff0c;自己的电脑有没有Type-c接口&#xff0c;支持不支持VGA 推荐&#xff1a; 自己不清楚&#xff0c;问客服&#xff0c;勤问。 二、显示屏与笔记本相连&#xff0c;通过VGA 三、连接好了&#xff0c;需要去配置 网址&#xff1a;凑合着看&#xff…

LLM 02-大模型的能力

LLM 02-大模型的能力 我们将深入探讨GPT-3——这个具有代表性的大型语言模型的能力。我们的研究主要基于GPT-3论文中的基准测试&#xff0c;这些测试包括&#xff1a; 标准的自然语言处理&#xff08;NLP&#xff09;基准测试&#xff0c;例如问题回答&#xff1b;一些特殊的一…

【OpenCV • c++】图像噪音 | 椒盐噪音 | 高斯噪音

文章目录 一、什么是图像噪音二、椒盐噪声三、高斯噪声 一、什么是图像噪音 图像噪声是图像在获取或是传输过程中受到随机信号干扰&#xff0c;妨碍人们对图像理解及分析处理的信号。很多时候将图像噪声看做多维随机过程&#xff0c;因而描述噪声的方法完全可以借用随机过程的描…

PyCharm中使用matplotlib.pyplot.show()报错MatplotlibDeprecationWarning的解决方案

其实这只是一个警告&#xff0c;忽略也可。 一、控制台输出 MatplotlibDeprecationWarning: Support for FigureCanvases without a required_interactive_framework attribute was deprecated in Matplotlib 3.6 and will be removed two minor releases later. MatplotlibD…

Java“牵手”微店商品列表页数据采集+微店商品价格数据排序,微店API接口申请指南

微店平台创立于2011年5月&#xff0c;是北京口袋时尚科技开发的应用&#xff0c;2014年1月"微店"APP正式上线。微店已经从小微店主首选的开店工具转型为助力创业者发展兴趣、创立品牌、玩成事业的系统及基础设施。 微店商品列表数据包含商品名称、价格、销量、详情、…

微信小程序 通过 pageScrollTo 滚动到界面指定位置

我们可以先创建一个page 注意 一定要在page中使用 因为pageScrollTo控制的是页面滚动 你在组件里用 他就失效了 我们先来看一个案例 wxml 代码如下 <view><button bindtap"handleTap">回到指定位置</button><view class "ControlHeight…

js 小数相乘后,精度缺失问题,记录四舍五入,向下取整

在做项目的时候&#xff0c;有一个计算金额的&#xff0c;结果发现计算的金额总是缺失0.01&#xff0c;发现相乘的时候&#xff0c;会失去精度&#xff0c;如图所示。被这整的吐血&#xff0c;由于计算逻辑由前端计算&#xff0c;所以传值后端总出错(尽量后端计算)。 还发现to…

9月12日作业

作业代码 #include <iostream>using namespace std;class Shape { protected:double cir;double area; public://无参构造Shape() {cout<<"无参构造"<<endl;}//有参构造Shape(double c, double a):cir(c), area(a){cout<<"有参构造&quo…

IDEFICS 简介: 最先进视觉语言模型的开源复现

我们很高兴发布 IDEFICS ( Image-aware Decoder Enhanced la Flamingo with Ininterleaved Cross-attention S ) 这一开放视觉语言模型。IDEFICS 基于 Flamingo&#xff0c;Flamingo 作为最先进的视觉语言模型&#xff0c;最初由 DeepMind 开发&#xff0c;但目前尚未公开发布…

极简B站直播录制工具 录播姬 2.9.0,支持自动批量录制、弹幕录制等

录播姬 是一个简单好用免费开源的直播录制工具&#xff0c;支持自动批量录制、弹幕录制、实时监控直播间状态&#xff0c;直接获取直播流&#xff0c;非录制屏幕&#xff0c;没有二次压制 软件特点 使用简单&#xff1a;粘贴房间号或房间链接即可开录 自动录制&#xff1a;主…

LeetCode 28. 找出字符串中第一个匹配项的下标

文章目录 一、题目二、C# 题解 一、题目 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 点击此处跳转…

Java 内存模型(JMM)

1.概述 JMM&#xff0c;全称 Java Memory Model&#xff0c;中文释义Java内存模型 对于 Java 程序员来说&#xff0c;在虚拟机自动内存管理机制下&#xff0c;不再需要像C/C程序开发程序员那样为每一个 new 操作去写对应的 delete/free操作&#xff0c;不容易出现内存泄漏和内…

LeetCode 2596. 检查骑士巡视方案【数组,模拟】1448

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

LeetCode 53. 最大子数组和

题目链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目解析 使用动态规划的方法进行解决&#xff0c;我们创建一个dp表&#xff0c;用来记录以该下标为结尾的最大子数组。然后每次存dp表的时候&#xff0c;进行取最大值。最终返回最大值。 由…

二叉排序树(BST)的算法分析以及基本操作(结点的查询,插入,删除)

1.二叉排序树的定义 二叉排序树&#xff0c;又称二叉查找树&#xff08;BST&#xff0c;Binary Search Tree) 默认不允许两个结点的关键字相同。 1.二叉排序树的性质: 任意一棵二叉排序树的子树的结点大小都满足“左小右大”。 左子树上所有结点的关键字均小于根结点的关键…