c语言动态规划回溯的原理,「算法思想」分治、动态规划、回溯、贪心一锅炖...
观感度:?????
口味:东北一锅出
烹饪时间:10min
本文已收录在Github github.com/Geekhyt,感谢Star。
数据结构与算法系列专栏第四弹来袭,往期专栏链接如下:
初学者一听到算法思想,就会觉得它们高深莫测,只能望而却步。
但如果你看过《事实》这本书,你就不会被大脑中的惯性思维所影响。
只要我们理解算法思想的关键点,多做题练习并加深理解记忆。其实算法思想就像切菜一样简单。
上一篇算法系列专栏中我们搞明白了递归。其实递归这种编程技巧是很多算法的基础。
还看过的同学建议先移步这篇专栏你真的懂递归吗?
比如本文讲到的这几种算法思想,大部分都是基于递归思想基础上的。
一句话理解四种算法思想
分治:分而治之,先解决子问题,再将子问题的解合并求出原问题。
贪心:一条路走到黑,选择当下局部最优的路线,没有后悔药。
回溯:一条路走到黑,手握后悔药,可以无数次重来。(英雄联盟艾克大招无冷却)。
动态规划:上帝视角,手握无数平行宇宙的历史存档,同时发展出无数个未来。
接下来我们一起庖丁解牛,将这几种算法思想一锅炖。
分治算法 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真题
虽然动态规划的最终版本 (降维再去维) 大都不是递归,但解题的过程还是离开不递归的。新手可能会觉得动态规划思想接受起来比较难,确实,动态规划求解问题的过程不太符合人类常规的思维方式,我们需要切换成机器思维。
使用动态规划思想解题,首先要明确动态规划的三要素。
动态规划三要素
重叠子问题
最优子结构
状态转移方程
重叠子问题
切换机器思维,自底向上思考。
爬第 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真题
思路
贪心算法+双指针求解。
给一个孩子的饼干应当尽量小并且能满足孩子,大的留来满足胃口大的孩子
因为胃口小的孩子最容易得到满足,所以优先满足胃口小的孩子需求
按照从小到大的顺序使用饼干尝试是否可满足某个孩子
当饼干 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真题
思路
使用回溯法进行求解,回溯是一种通过穷举所有可能情况来找到所有解的算法。如果一个候选解最后被发现并不是可行解,回溯算法会舍弃它,并在前面的一些步骤做出一些修改,并重新尝试找到可行解。究其本质,其实就是枚举。
如果没有更多的数字需要被输入,说明当前的组合已经产生。
如果还有数字需要被输入:
遍历下一个数字所对应的所有映射的字母
将当前的字母添加到组合最后,也就是 str + tmp[r]
关键点
在for循环中调用递归。
复杂度分析
N+M 是输入数字的总数
时间复杂度:O(3^N * 4^M)
空间复杂度:O(3^N * 4^M)
const letterCombinations = 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.看到这里了就点个赞支持下吧,你的赞是我创作的动力。
2.关注公众号前端食堂,你的前端食堂,记得按时吃饭!
3.本文已收录在前端食堂Github github.com/Geekhyt,求个小星星,感谢Star。
c语言动态规划回溯的原理,「算法思想」分治、动态规划、回溯、贪心一锅炖...相关推荐
- 用递归与分治策略求解网球循环赛日程表_分治、动态规划、回溯、贪心一锅炖
「观感度:五颗星」 「口味:东北一锅出」 「烹饪时间:10min」 本文已收录在 Github github.com/Geekhyt,感谢Star. 数据结构与算法系列专栏第四弹来袭,往期专栏链接如下 ...
- 将分治、动态规划、回溯、贪心一锅炖
微信搜索[前端食堂]你的前端食堂,记得按时吃饭. 本文已收录在Github github.com/Geekhyt,感谢Star. 数据结构与算法系列专栏第四弹来袭,往期专栏链接如下: 前端如何搞定数据 ...
- c语言开发五轴后处理,「数控干货」基于UG CLS文件使用 C 语言制作智能后处理工具...
原标题:「数控干货」基于UG CLS文件使用 C 语言制作智能后处理工具 1 前言 UG 后处理操作是 UGCAM 数控加工工作中一个重要环节,主要任务是把在 UG 加工环境下生成的加工刀位文件转换成 ...
- 五大算法思想(三)回溯法及常见例子
文章目录 一.理论基础 1.1 基本策略 1.2 使用步骤 1.3 经典例子 二.常见例子 2.1 八皇后问题 2.2 装载问题 2.3 批量作业调度问题 2.4 背包问题 一.理论基础 回溯法作 ...
- 「算法时空」星球视频目录
本星球主打算法三部曲iPad Pro手写录制视频,精讲如下三本算法巨著: 已讲完<算法导论>(Introduction to Algorithms, 3rd edition, CLRS) ...
- 「算法学习」:贪心算法找零问题
找零算法,找零??? 假设你是一个商店老板,你需要给顾客找零n元钱,你手上有的钱的面值为:100元,50元,20元,5元,1元.请问如何找零使得所需要的钱币数量最少? 例子:你需要找零126元,则所需 ...
- 【数据结构与算法】【算法思想】分治算法
贪心算法 回溯算法 分治算法 动态规划 MapReduce本质就是分治算法,是Google大数据处理的三驾马车之一,另外两个是GFS和Bigtable.它在倒排索引,PageRank计算,网页分析等搜 ...
- 分治法的关键特征_经典算法思想2——分治(Divide-and-Conquer)
分治法,字面意思是"分而治之",就是把一个复杂的1问题分成两个或多个相同或相似的子问题,再把子问题分成更小的子问题直到最后子问题可以简单地直接求解,原问题的解即子问题的解的合并,这 ...
- 「算法介绍」我国学者声称证明了p≠np
是的,你们没有听错,最近在多个平台上都浏览到这篇文章<Eagle: A new symmetric encryption algorithm against any linear attacks ...
最新文章
- Linq 等式运算符:SequenceEqual
- Office 2007中的简繁体转换功能
- Flutter开发之ListView组件(21)
- Spring-Spring4.X 概述
- JavaScript3基础——运算符和类型转换
- Springboot直接访问templates中的html
- 实验 5 性能测试脚本录制和开发实验报告--软件功能测试与性能测试实验
- 地灾应急暨地灾危险性评估培训班学习笔记
- centos8 装docker 问题
- 面试题:为什么局部变量不赋初始值报错
- 2018年高二计算机试题,2018年上学期高二信息技术期末考试试题.pdf
- c语言编程概念与观点,从程序设计本质入手,上好C语言第一课.doc
- 作为一个Java开发者,java常用算法手册pdf
- 孙宇晨大举进驻NFT艺术的背后:区块链+艺术还能有怎样的玩法?
- 无损检测服务行业调研报告 - 市场现状分析与发展前景预测
- 加密所有事物,将数据安全存储在任何地方
- Vue-Element写一个简单的列表管理23/100统
- 亚马逊云科技启示录:创新作帆,云计算的征途是汪洋大海
- php dwg格式,无需AutoCAD,用C#生成DWG文件
- 你是如何学习一项新技术的?
热门文章
- 手机中丢失了数据怎么办
- 基于C++实现植物大战僵尸(控制台版)【100010530】
- Java中System.out.println()简单简介
- 动态代理和静态代理各自的特点
- dcl并列控件 lisp_飞诗Lisp编辑器下载
- 看不懂,为什么跑出租车的大战送外卖的,没关系,主要是你不理解大数据
- 1.1图像与数字图像
- 索尼ELF-SR2解析:27英寸、裸眼3D、4K够不够
- www.baidu.com www.topka.cn www.xiaoxiaose.com
- sql server management studio 已停止工作_便利店员工的每日工作流程