个人文档
  • AI编程Cursor
  • GPT使用笔记
  • npm常用库合集
  • 同步用
  • 小Demo们
  • 工具网站教程集合
  • HTML、CSS 工具方法集合
    • HTML 全局属性
    • css常用功能
    • font-face 字体|子集相关
    • iframe父子页面传值
    • input输入优化
    • loading状态
    • nodejs使用谷歌邮箱发邮件
    • 为 Dom 自定义事件监听
    • 初始html的head标签配置
    • 拼音输入中文汉字的事件监听
    • 文字颜色效果
    • 文档片段范围 Range
    • 移动端开发-rem
    • 等宽字体推荐
    • 网站SEO优化注意点
    • 邮件html模板
  • JS 工具方法集合
    • Axios 简单使用
    • Axios 简单封装
    • Gitbook的安装和使用
    • Github 登录开发
    • HTML转为纯文本
    • JS 中强大的操作符
    • cookie 操作
    • js 动态加载js资源
    • js 常用功能语句
    • js取代trycatch的方法封装
    • js接口下载二进制
    • script 标签的异步属性
    • 判断当前是移动端还是pc端
    • 刷新token队列管理
    • 前端多线程 Web Worker
    • 加密-AES对称加密
    • 加密-node进行rsa加密解密
    • 地区省市区三级联动的地址数据 + 功能
    • 复制插件
    • 开发时环境变量
    • 得到随机图片
    • 数字格式整理集合
    • 数学计算插件
    • 时间格式整理
    • 获取ip地址
    • 获取url传参
    • 进制转换和位运算符
    • 页面隐藏|激活|关闭的监听
  • JS 知识点研究
    • Babel 历史和原理
    • Babel 配置和使用
    • Function 的 apply、call、bind
    • HTTP浏览器缓存粗解
    • Source map 文件还原为源码
    • TS常用技巧
    • js 的加载和模块化
    • js 的新数据类型 Symbol
    • js的代理对象 proxy 和 defineProperty
    • js的原型链 prototype
    • vite 打包体积优化
    • webpack 可视化打包文件大小插件
    • webpack 基础使用配置
    • webpack 版本5的报错
    • yeoman 开发脚手架的工具
    • 同步异步和微任务宏任务
    • 移动端调试---谷歌工具+eruda+vconsole
    • 转换-Blob URL
    • 转换-FileReader
    • 转换-Js文件类型和转换
    • 转换-前端开发的URL的编码和解码
    • 转换-字符串和Base 64的转换
  • Node 和 Npm 相关
    • Node 开发环境配置
    • express + jwt 校验
    • node 常用方法
    • node后台服务器-PM2
    • node基本使用
    • npm 中依赖的版本问题
    • npm 功能使用
    • npm指令说明和其他对比
    • nvm版本管理+自动切换node版本
  • React 学习
    • React Hook
    • React 项目基础开发
    • React.memo 和 React.PureComponent
    • React懒加载进阶
    • useContext Hook
    • useEffect Hook
    • useMemo 和 useCallback - Hook
    • useRef Hook
    • useState Hook
    • 同步修改变量功能封装 useVal for react
    • 轻便的传值组件
  • Rust 语言相关
    • Rust 基本
    • Rust 基础学习
    • Rust 调用 Object-C 的API
    • Tauri 基本使用
    • Tauri 是什么
  • VUE 学习
    • Vue3 使用
    • Vue3使用hook
    • Vue开发小技术点
    • vue路由切换时的动画效果
    • 花式引入组件和资源-打包时拆包减少js体积
  • Web3相关
    • Web3.0开发上-准备和概念理解
    • Web3.0开发下-功能代码示例
    • 以太坊区块链和Web3.0
    • 开发智能合约
  • python
    • pyenv版本管理工具
    • python初始化
    • python基本概念
    • venv虚拟环境
  • 个人其他
    • Steam Deck的基本设置和插件
  • 其他编程相关
    • Git教程和常用命令
    • Java开发-JDK和Maven的安装和卸载
    • Jenkins安装和基本使用
    • Linux系统指令
    • Mac 使用2K屏幕开启缩放
    • Mac 使用VS code打开项目
    • Mac 安装 Homebrew
    • Mac 的终端 shell 与 zsh
    • Mac 软件和插件
    • MacBook使用建议
    • Mac升降级到指定版本的系统
    • Mac安装Zsh
    • Mac安装软件各种提示
    • Mac系统脚本语言 AppleScript 的使用
    • Mac终端代理工具
    • Markdown(md)文档开发-Typora
    • Mysql 的安装和使用
    • Nginx 安装和基础使用
    • Nginx 稍微高深的配置
    • Slate - Api 的文档开发工具
    • Sublime配置
    • Ubuntu的 apt-get 使用
    • VScode配置
    • Windows 软件和插件
    • curl 工具使用
    • github 网站访问优化
    • host 文件
    • inquirer 终端中和用户交互
    • uTools的插件开发教程
    • vim 文本编辑功能
    • 使用 Github Pages 免费部署网站
    • 压缩指令 zip 和 unzip
    • 油猴的安装和开发(Tampermonkey)
    • 阿里云简略使用
  • 微信开发
    • 微信小程序开发
    • 微信开发必读
    • 微信开发提前购买域名
    • 微信手机打开的页面中授权登录
    • 微信扫码登录
    • 微信服务号登录+推送服务提醒
    • 自定义分享卡片-node.js实现
  • 数据结构与算法
    • KMP算法
    • Wildcard字符串分析算法
    • 二叉树
    • 字典树
    • 时间复杂度浅析
    • 算法神器——动态规划
Powered by GitBook
On this page
  • 算法神器——动态规划
  • 数组中连续几项的和的最大的值
  • 买入卖出股票获利最大
  • 数组中连续几项的乘积的最大的值
  • 打家劫舍

Was this helpful?

  1. 数据结构与算法

算法神器——动态规划

title: 算法神器——动态规划 id: a101819e28e80118446b9280226e4ae0 tags: [] date: 2000/01/01 00:00:00 updated: 2023/03/04 19:29:12 isPublic: true --#|[分隔]|#--

算法神器——动态规划

动态规划在处理某类问题时,具有性能和处理速度上极佳的处理能力。

这类问题需要处理一个数组,需要在数组中找到某几项的组合,使满足最佳条件,之前的决策会影响之后的决策。

动态规划一般解题思路,是遍历数组,遍历到每一项,都取一下之前的最优解,再计算一下当前项参与时的最优解,从两个最优解中更优的,存起来继续遍历。

数组中连续几项的和的最大的值

问题: 给定只包含正负整数的数组,获取数组中连续几项的和,最大的值是多少。

思路:

如果使用暴力解,那就需要使用双重 for 循环,很消耗性能。

使用动态规划,就代码比较简单,但理解上需要费一些脑细胞。

遍历数组,遍历到某一项时,得到把前项作为新的「连续几项」的第一项时,取他们的和(其实就是当前项的值)为A,再计算把当前项为「连续几项」的最后一项时,他们的和为B,对比A和B,只保留大的那个值。

然后,再有一个变量,用来保留遍历历史上,出现过的和的最大值,当把整个数组遍历完,这个变量就是最终的产出。

解题:

声明两个变量:

  • accumulate:累积值,当遍历数组时,用它来保存以当前项为「连续几项」的最后一项时,和的最大值。

  • maxValue:保留整体最大值,因为 accumulate 需要每次遍历时计算。

// 给定只包含正负整数的数组,获取数组中连续几项的和,最大的值是多少
let maxSubArray = function(nums) {
  let accumulate = nums[0] // 累积值
  let maxValue = nums[0] // 整体最大值

  for (let i = 1; i < nums.length; i ++ ) {

    // 对比「当前项」 和 「累积值 + 当前项」,取两者中比较大的那个结果,赋值给新的「累积值」
    // 因为前几项的和可能是负值,而当前项是正值,此时就该舍弃前几项
    // 把当前项作为「连续几项」唯一的一项
    accumulate = Math.max(nums[i], accumulate + nums[i])

    // 对比「整体最大值」 和 「累积值」,取两者中比较大的那个结果,赋值给新的「整体最大值」
    // 参考 accumulate 的定义,有可能旧的 accumulate 值是正数,而 nums[i] 是负值
    // 那么 nums[i] 和 accumulate + nums[i],他们的值都比 accumulate 小
    // maxValue 就用来保存,遍历历史上,出现过的那个最大的和
    maxValue = Math.max(maxValue, accumulate)
  }

  return maxValue
}

买入卖出股票获利最大

问题:

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择某一天买入这只股票,并选择在未来的某一个不同的日子卖出该股票,设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润,如果你不能获取任何利润,返回 0 。

思路:

暴力解同样上一个问题,需要双重for循环。

动态规划:遍历数组,遍历前设两个变量:

  • sellToday:今天卖出,手中剩余的钱,默认值为0。

  • buyToday:今天买入,将要花费的钱,默认认为第一天买入,值为prices[0]。

然后遍历数组,分别判断留值,最终的 sellToday 为最终结果。

解题:

let maxSubArray = function(prices) {
  let sellToday = 0 // 今天卖出,手中剩余的钱(默认为0)
  let buyToday = prices[0] // 今天买入,将要花费的钱(默认在第一天买入了)

  for (let i = 1; i < prices.length; i++) {
    // 对比「今天卖出」和「之前已经卖出」,取获利最大的值,保留
    sellToday = Math.max(prices[i] - buyToday, sellToday)
    // 对比「今天买入」和「之前已经买入」,取花费更少的值,保留
    buyToday = Math.min(prices[i], buyToday)
  }
  return sellToday
}

数组中连续几项的乘积的最大的值

问题:

给定只包含正负整数的数组,获取数组中连续几项的乘积,最大的值是多少。

看似和「连续几项的和」的那道题类似,但是,数组中可能会出现负数,一个负数的出现,可能会把一个最小值变成最大值。

思路:

遍历数组时,需要把每次遍历时,正数的最大值和负数的最小值都记录一下,以便应对后面可能发生的正负转换。

解题一:

var maxProduct = function(nums) {
  
  let upMax = 0 // 正数时的最大值
  let downMin = 0 // 负数时的最小值
  let max = 0 // 历史中出现过的最大值

  for (let i = 0; i < nums.length; i++) {
    // 把上一次的两个值先存一下,
    let _upMax = upMax, _downMin = downMin

    if (nums[i] > 0) {
      // 当前项是正值
      upMax = Math.max(nums[i], (_upMax || 1) * nums[i])
      downMin = _downMin ? _downMin * nums[i] : 0
    } else if (nums[i] < 0) {
      // 当前项是负值
      upMax = _downMin ? _downMin * nums[i] : 0
      downMin = Math.min(nums[i], (_upMax || 1) * nums[i])
    } else {
      // 当前项是 0
      upMax = 0
      downMin = 0
    }

    max = Math.max(max, upMax)
  }

  return max
}

解题二:

上面的题解代码相对更多,下面这个题解的思路是一样的,但是代码精简了:

var maxProduct = function(nums) {
  let max = nums[0]; // 最大值
  let min = nums[0]; // 最小值
  let res = nums[0]; // 历史中的最大值

  for (let i = 1; i < nums.length; i++) {
    let tmp = min; // 暂存一下上一次的最小值
    min = Math.min(nums[i], Math.min(max * nums[i], min * nums[i])); // 取最小
    max = Math.max(nums[i], Math.max(max * nums[i], tmp * nums[i])); /// 取最大
    res = Math.max(res, max);
  }
  return res;
}

打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

解题:

/**
 * @param {number[]} nums
 * @return {number}
 */
var rob = function(nums) {
  let last = nums[0] || 0 // 本次偷了
  let notLast = 0 // 本次没偷

  for (let i = 1; i < nums.length; i++) {
    let last_pre = last;
    last = notLast + nums[i]
    notLast = Math.max(notLast, last_pre)
  }
  return Math.max(last, notLast)
};
Previous时间复杂度浅析

Last updated 3 months ago

Was this helpful?