Leetcode376. 摆动序列

news2024/7/30 7:12:07

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)。我们只需要常数空间来存放若干变量。

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

代码:

// 贪心2

class 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++;
                }
            }
        }
        // 因为统计的是变化的次数,最终的结果是序列的长度,所以需要+1
        return count + 1;
    }
};

结果:

在这里插入图片描述

复杂度分析:

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

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

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

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

相关文章

再聊Java Stream的一些实战技能与注意点

大家好&#xff0c;又见面了。 在此前我的文章中&#xff0c;曾分2篇详细探讨了下JAVA中Stream流的相关操作&#xff0c;2篇文章收获了累计 10w阅读、2k点赞以及 5k收藏的记录。能够得到众多小伙伴的认可&#xff0c;是技术分享过程中最开心的事情。 不少小伙伴在评论中提出了…

前端开发工程师:职业前景、工资、 具体工作

该篇适用于从零基础学习前端的小白 一、职业前景 前端这两年也是非常火的&#xff0c;就业的前景也是非常不错的。 1.需求持续增长&#xff1a; 随着互联网和移动设备的普及&#xff0c;越来越多的企业和组织需要建立和维护网站、应用程序和在线平台。这导致了对具有前端开发…

Spring Cloud Eureka:服务注册与发现

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Spring Cloud Eureka&#xff1a;服务注册与发现 Spring Cloud Eureka是Spring Cloud生态系统中的一个组件&#xff0c;它是用于实现服务注册与发现的服务治理组件。在…

VMware16安装ghost版win7

文章目录 准备工作GHO 文件装机工具 新建虚拟机配置虚拟机还需要一个 CD/DVD PE 安装步骤分区还原挂载 CD/DVD开始还原 还原之后 准备工作 GHO 文件 可以去百度搜索这种文件&#xff0c;我这里是从系统之家下载的deepin win7 ghost 系统 装机工具 因为下载的 ghost 版的 w…

查找:分块查找算法分析

数据分块存储,分块查找特点:块内无序、块间有序。 1.分块查找的算法思想 1.使用顺序查找查索引 设置一个索引表&#xff0c; 索引表数据结构设计&#xff1a; //索引表 typedef struct {ElemType maxValue;int low,high; }Index;//顺序表存储实际元素 ElemType List[100] ;分…

圆的反演 hdu 4773

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 题目大意 http://acm.hdu.edu.cn/showproblem.php?pid4773 给定2个不相交的圆以及圆外1点P。求过P并且与另两个圆相切&#xff08;外切&#xff09;的圆&#xff0c…

使用TortoiseGit导出两次提交时间之间的差异文件

同时选择两个提交时间&#xff0c;右键后点击Compare revisions 多选需要导出的待发布的文件&#xff0c;然后右键点击Export selection to... 在弹窗中选择文件夹&#xff08;导出待发布的文件&#xff09; 导出效果&#xff08;目录&#xff09; 导出效果&#xff08;文件&am…

MGR新节点RECOVERING状态的分析与解决:caching_sha2_password验证插件的影响

起因 在GreatSQL社区上有一位用户提出了“手工构建MGR碰到的次节点一直处于recovering状态”&#xff0c;经过排查后&#xff0c;发现了是因为新密码验证插件caching_sha2_password导致的从节点一直无法连接主节点&#xff0c;帖子地址&#xff1a;(https://greatsql.cn/threa…

基于Android系统图书管理系统

摘要 随着移动终端使用率的快速增加&#xff0c;Android智能产品已日益成为越来越多的人们选择的移动终端产品。伴随着Android智能手机与平板电脑已经在我们生活大量的使用&#xff0c;越来越多的基于Android开发平台的应用也随之产生。 便捷的图书检索和借阅&#xff1a;用户可…

Java“牵手”京东商品列表页数据采集+商品价格数据排序,商品销量排序数据,京东商品API采集方法

京东商品列表API是京东平台提供给开发者的应用程序编程接口&#xff0c;通过API可以获取京东平台上商品列表数据。 京东商品列表API可以提供多种不同的推荐商品列表API接口&#xff0c;开发者可以根据自己的需求选择适合自己的接口。其中&#xff0c;最常用的是基于用户反馈的…

项目经理如何做好跨部门的沟通与协作?

对项目经理来说沟通不良&#xff0c;会对项目造成严重影响&#xff0c;跨部门沟通更是项目管理中的难题。原本应该合作解决的问题&#xff0c;到了跨部门会议上&#xff0c;又各说各话&#xff0c;找不到共识。 在不同部门各有不同立场与利益的情况下&#xff0c;怎样才能把话…

Open Interpreter:OpenAI Code Interpreter的开源实现|本地化|可联网

如果你对这篇文章感兴趣&#xff0c;而且你想要了解更多关于AI领域的实战技巧&#xff0c;可以关注「技术狂潮AI」公众号。在这里&#xff0c;你可以看到最新最热的AIGC领域的干货文章和案例实战教程。 一、前言 今年7月&#xff0c;OpenAI发布了一个强大的插件&#xff0c;名…

什么是第三方软件测试?

测试机构 什么是软件测试&#xff1f; 软件测试是一个验证和验证应用程序功能以确定它是否满足要求的过程。这是在应用程序中发现缺陷并根据*终用户的要求检查应用程序功能的过程。 第三方软件检测机构是专门提供软件测试服务&#xff0c;其出具软件测试报告过程中可能运用到…

Object.keys和Object.values

Object.keys list:[],obj:{数据泄露: 5412, 数据传输: 3921, 数据篡改: 851392, 数据滥用: 59532 },//返回可枚举的属性数组console.log(Object.keys(this.obj)) // [数据泄露, 数据传输, 数据篡改, 数据滥用]Object.keys(this.obj).map(key>{this.list.push({title:key,val…

Java工作流系统,快速实现业务审批(源码)

前言 activiti工作流引擎项目&#xff0c;企业erp、oa、hr、crm等企事业办公系统轻松落地&#xff0c;请假审批demo从流程绘制到审批结束实例。 一、项目形式 springbootvueactiviti集成了activiti在线编辑器&#xff0c;流行的前后端分离部署开发模式&#xff0c;快速开发平…

700亿参数Llama 2训练加速195%!数据成为其提升效果的关键要素

Llama 2是Meta AI正式发布的最新一代开源大模型&#xff0c;达到了2万亿的token。精调Chat模型是在100万人类标注数据上训练。Llama 2在包括推理、编码、精通性和知识测试等许多外部基准测试中都优于其他开源语言模型。 Llama 2开启了全球范围内AI大型模型的共享新篇章。它包括…

第5章_freeRTOS入门与工程实践之模块使用说明与STM32CubeMX配置

本教程基于韦东山百问网出的 DShanMCU-F103开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id724601559592 配套资料获取&#xff1a;https://rtos.100ask.net/zh/freeRTOS/DShanMCU-F103 freeRTOS系列教程之freeRTOS入…

进销存仓库管理系统有哪些?哪些适合商户用?

进销存仓库管理系统可以帮助商家实现准确的库存控制、优化采购和销售活动&#xff0c;提升仓库操作效率&#xff0c;并提供数据分析和决策支持&#xff0c;从而解决企业在库存管理和供应链方面的问题&#xff0c;提升整体运营效率和竞争力。 进销存仓库管理系统有哪些&#xf…

SmartSQL 一款开源的数据库文档管理工具

建议直接蓝奏云下载安装 蓝奏云下载&#xff1a;https://wwoc.lanzoum.com/b04dpvcxe 蓝奏云密码&#xff1a;123 项目介绍 SmartSQL 是一款方便、快捷的数据库文档查询、导出工具&#xff01;从最初仅支持 数据库、CHM文档格式开始&#xff0c;通过不断地探索开发、集思广…

LVGL(72)-v8--滑块slider

一、slider 简介 1.1 概述 Overview Slider对象看起来像一个带有旋钮的工具条。可以拖动该旋钮来设置一个值。滑块也可以是垂直的或水平的。滑动条在前面我们介绍img控件的时候有个历程有使用到&#xff0c;哪里我们讲述设置样式实现对滑动条的一些样式的设置。 1.2 部分和风…