「观感度:五颗星」

「口味:东北一锅出」

「烹饪时间:10min」

本文已收录在 Github github.com/Geekhyt,感谢Star。

数据结构与算法系列专栏第四弹来袭,往期专栏链接如下:

  • 前端如何搞定数据结构与算法
  • JavaScript算法时间、空间复杂度分析
  • 你真的懂递归吗?

初学者一听到算法思想,就会觉得它们高深莫测,只能望而却步。

但如果你看过《事实》这本书,你就不会被大脑中的惯性思维所影响。 只要我们理解算法思想的关键点,多做题练习并加深理解记忆。其实算法思想就像切菜一样简单。

上一篇算法系列专栏中我们搞明白了递归。其实递归这种编程技巧是很多算法的基础。

还看过的同学建议先移步这篇专栏你真的懂递归吗?

比如本文讲到的这几种算法思想,大部分都是基于递归思想基础上的。

一句话理解四种算法思想

分治:分而治之,先解决子问题,再将子问题的解合并求出原问题。

贪心:一条路走到黑,选择当下局部最优的路线,没有后悔药。

回溯:一条路走到黑,手握后悔药,可以无数次重来。(英雄联盟艾克大招无冷却)。

动态规划:上帝视角,手握无数平行宇宙的历史存档,同时发展出无数个未来。

接下来我们一起庖丁解牛,将这几种算法思想一锅炖。

分治算法 Divide and Conquer

分治算法思想很大程度上是基于递归的,也比较适合用递归来实现。顾名思义,分而治之。一般分为以下三个过程:

  • 分解:将原问题分解成一系列子问题。
  • 解决:递归求解各个子问题,若子问题足够小,则直接求解。
  • 合并:将子问题的结果合并成原问题。

比较经典的应用就是归并排序 (Merge Sort) 以及快速排序 (Quick Sort) 等。我们来从归并排序理解分治思想,归并排序就是将待排序数组不断二分为规模更小的子问题处理,再将处理好的子问题合并起来。

上代码。

const mergeSort = function(arr) {const len = arr.length;if (len > 1) {// 对半分解const middle = Math.floor(len / 2);const left = arr.slice(0, middle);const right = arr.slice(middle, len);let i = 0; let j = 0;let k = 0;// 分别对左右进行排序mergeSort(left);mergeSort(right);while(i < left.length && j < right.length) {if (left[i] < right[j]) {arr[k] = left[i];i++;} else {arr[k] = right[j];j++;}k++;}// 检查余项while(i < left.length) {arr[k] = left[i];i++;k++;}while(j < right.length) {arr[k] = right[j];j++;k++;}}return arr;
}

复杂度分析

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)

动态规划 Dynamic Programming

LeetCode真题

70. 爬楼梯

虽然动态规划的最终版本 (降维再去维) 大都不是递归,但解题的过程还是离开不递归的。新手可能会觉得动态规划思想接受起来比较难,确实,动态规划求解问题的过程不太符合人类常规的思维方式,我们需要切换成机器思维。

使用动态规划思想解题,首先要明确动态规划的三要素。

动态规划三要素

  • 重叠子问题
  • 最优子结构
  • 状态转移方程

重叠子问题

切换机器思维,自底向上思考。

爬第 n 阶楼梯的方法数量,等于两部分之和:

  • 爬上 n-1 阶楼梯的方法数量
  • 爬上 n-2 阶楼梯的方法数量

最优子结构

子问题的最优解能够推出原问题的优解。

状态转移方程

dp[n] = dp[n-1] + dp[n-2]

具备三要素,确认边界条件,初始化状态,开始切菜:

  • dp[0] = 1
  • dp[1] = 1
const climbStairs = function(n) {const dp = [];dp[0] = 1;dp[1] = 1;for (let i = 2; i <= n; i++) {dp[i] = dp[i-1] + dp[i-2];}return dp[n];
};

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

优化

在此基础上,我们还可以通过压缩空间来对算法进行优化。因为 dp[i]只与 dp[i-1]dp[i-2] 有关,没有必要存储所有出现过的 dp 项,只用两个临时变量去存储这两个状态即可。

const climbStairs = function(n) {let a1 = 1;let a2 = 1;for (let i = 2; i <= n; i++) {[a1, a2] = [a2, a1 + a2];}return a2;
}

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

贪心算法 Greedy

最近某音很火的贪心土味情话
喂,不是吧。今天喝了脉动啊,吃了果冻啊,但是,还是忍不住对你心动啊。

回到算法中,贪心算法动态规划算法的一个子集,可以更高效解决一部分更特殊的问题。实际上,用贪心算法解决问题的思路,并不总能给出最优解。因为它在每一步的决策中,选择目前最优策略,不考虑全局是不是最优。

LeetCode真题

LeetCode 455. 分发饼干

思路

贪心算法+双指针求解。

  • 给一个孩子的饼干应当尽量小并且能满足孩子,大的留来满足胃口大的孩子
  • 因为胃口小的孩子最容易得到满足,所以优先满足胃口小的孩子需求
  • 按照从小到大的顺序使用饼干尝试是否可满足某个孩子
  • 当饼干 j >= 胃口 i 时,饼干满足胃口,更新满足的孩子数并移动指针 i++ j++ res++
  • 当饼干 j < 胃口 i 时,饼干不能满足胃口,需要换大的 j++

关键点

将需求因子 g 和 s 分别从小到大进行排序,使用贪心思想配合双指针,每个饼干只尝试一次,成功则换下一个孩子来尝试。

复杂度分析

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)
const findContentChildren = function (g, s) {g = g.sort((a, b) => a - b);s = s.sort((a, b) => a - b);let gi = 0; // 胃口值let sj = 0; // 饼干尺寸let res = 0;while (gi < g.length && sj < s.length) {if (s[sj] >= g[gi]) {gi++;sj++;res++;} else {sj++;}}return res;
};

回溯算法 Backtracking

回溯算法本质上就是枚举,使用摸着石头过河的查找策略,还可以通过剪枝少走冤枉路。

LeetCode真题

LeetCode 17.电话号码的字母组合

思路

使用回溯法进行求解,回溯是一种通过穷举所有可能情况来找到所有解的算法。如果一个候选解最后被发现并不是可行解,回溯算法会舍弃它,并在前面的一些步骤做出一些修改,并重新尝试找到可行解。究其本质,其实就是枚举。

如果没有更多的数字需要被输入,说明当前的组合已经产生。

如果还有数字需要被输入:

  • 遍历下一个数字所对应的所有映射的字母
  • 将当前的字母添加到组合最后,也就是 str + tmp[r]

关键点

在for循环中调用递归。

复杂度分析

N+M 是输入数字的总数

  • 时间复杂度:O(3^N * 4^M)
  • 空间复杂度:O(3^N * 4^M)
terCombinations = function (digits) {if (!digits) {return [];}const len = digits.length;const map = new Map();map.set('2', 'abc');map.set('3', 'def');map.set('4', 'ghi');map.set('5', 'jkl');map.set('6', 'mno');map.set('7', 'pqrs');map.set('8', 'tuv');map.set('9', 'wxyz');const result = [];function generate(i, str) {if (i == len) {result.push(str);return;}const tmp = map.get(digits[i]);for (let r = 0; r < tmp.length; r++) {generate(i + 1, str + tmp[r]);}}generate(0, '');return result;
};

用递归与分治策略求解网球循环赛日程表_分治、动态规划、回溯、贪心一锅炖相关推荐

  1. 用递归与分治策略求解网球循环赛日程表_算法设计:分治法(比赛日程安排)...

    一.算法思路 1.思路 分治算法的思想是:对于一个规模位N的问题,若该问题可以容易解决(比如规模N较小),则直接解决,否则将其分解为M个规模较小的子问题,这些子问题互相独立,并且与原问题形式相同,递归 ...

  2. 递归与分治策略算法之循环赛日程表

    递归与分治策略算法之循环赛日程表 1.先简单的来介绍一下分治策略的思想 分治策略的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,分解出来的子问题与原问题相同,并且相互独立.通过递归去解决子 ...

  3. python实现循环赛日程表问题的算法_循环赛日程表的分治算法实现实验报告gxl.doc...

    循环赛日程表的分治算法实现实验报告gxl PAGE PAGE 2 深 圳 大 学 实 验 报 告 课程名称: 算法设计与分析 实验项目名称: 分治算法 --矩阵相乘的Strassen算法及时间复杂性分 ...

  4. python实现循环赛日程表问题的算法_循环赛日程表的分治算法实现实验报告_gxl.doc...

    循环赛日程表的分治算法实现实验报告_gxl 深 圳 大 学 实 验 报 告 课程名称: 算法设计与分析 实验项目名称: 分治算法 --矩阵相乘的Strassen算法及时间复杂性分析 或--循环赛日程表 ...

  5. c语言-网球循环赛日程表

    网球循环赛日程表 问题描述: 设有n个运动员要进行网球循环赛.设计一个满足以下要求的比赛日程表: (1)每个选手必须与其他n-1个选手各赛一次. (2)每个选手一天只能赛一次 (3)当n是偶数时循环赛 ...

  6. 2循环赛日程表(分治)

    设有n=2k个运动员要进行网球循环赛.现要设计一个满足以下要求的比赛日程表: (1)每个选手必须与其他n-1个选手各赛一次: (2)每个选手一天只能参赛一次: (3)循环赛在n-1天内结束. 按此要求 ...

  7. python【数据结构与算法】循环赛日程表(分治)

    文章目录 1 概述 2 解决 1 概述 设计一个满足以下要求的比赛日程表: 每个选手必须与其他n-1个选手各赛一次: 每个选手一天只能赛一次: 循环赛一共需要进行n-1天. 由于n=2^k个运动员,显 ...

  8. c语言动态规划回溯的原理,「算法思想」分治、动态规划、回溯、贪心一锅炖...

    观感度:????? 口味:东北一锅出 烹饪时间:10min 本文已收录在Github github.com/Geekhyt,感谢Star. 数据结构与算法系列专栏第四弹来袭,往期专栏链接如下: 初学者 ...

  9. 将分治、动态规划、回溯、贪心一锅炖

    微信搜索[前端食堂]你的前端食堂,记得按时吃饭. 本文已收录在Github github.com/Geekhyt,感谢Star. 数据结构与算法系列专栏第四弹来袭,往期专栏链接如下: 前端如何搞定数据 ...

最新文章

  1. 无线AP、WiFi、WLAN是什么关系?
  2. 杭电1430康托 bfs(java)
  3. 电子商务平台支付接口开发原理及流程
  4. Linux网络编程 | 定时事件 :Linux常见定时方法、定时器链表、空闲断开
  5. python socks5 代理服务
  6. [js] 微信小程序实现轨迹回放,微信原生小程序,基于uniapp的小程序?
  7. 属性文法和语法制导翻译
  8. python getopt_Python getopt
  9. LBP算法及其改进算法
  10. Spring之事务管理与事务的传播特性
  11. android车载桌面,车载桌面Car Launcher Pro
  12. 台式计算机配置单,最新台式电脑组装配置推荐
  13. Testin云测:深耕企业应用服务
  14. 吉林省专升本历年真题高频词汇
  15. 带有点***色彩的LanHelper网络工具的使用
  16. Java的求学之路--Day01
  17. elasticsearch之analysis
  18. java中的字符retry: 是什么?
  19. 【游戏开发】unity教程4 打飞碟小游戏
  20. 2005年10月高等教育国际金融全国统一命题考试

热门文章

  1. ARM与PowerPC的区别
  2. 勇士挂机 鸿蒙合成,勇士挂机装备攻略 装备精炼、附魔、锻造技巧[多图]
  3. 存储卡插入电脑提示磁盘有写保护怎么办
  4. 电脑上可以群发手机短信吗
  5. 3D软件这么多?新手怎么学才能有效高速学完次世代建模
  6. 请提前布局 Star Trek突破链游全新玩法,市场热度持续高涨
  7. 计算机配置审批表,北京邮电大学涉密计算机配置审批表.pdf
  8. 次世代游戏建模技巧全解
  9. vscode 配置javaweb开发环境,超级简单,纯新手带图,学会后真香.宇宙无敌第一编辑器vscode
  10. 怎样查看Oracle的版本