Compare commits

..

3 Commits

Author SHA1 Message Date
kn5886348135 80e4f5b42a 动态规划 二叉树 贪心算法
3 years ago
kn5886348135 2d8ff7b91b 动态规划
3 years ago
kn5886348135 b54cd79a06
3 years ago

@ -56,6 +56,7 @@ public class Edge {
}
```
<br />
### 宽度优先遍历
* 利用队列实现
@ -69,7 +70,50 @@ public class Edge {
* 从源节点开始把节点按照深度放入栈,然后弹出
* 每弹出一个点,把该节点下一个没有进过栈的邻接点放入栈
* 直到栈变空
<br />
### 拓扑排序
&emsp;&emsp;拓扑排序是不唯一的
&emsp;&emsp;拓扑排序要求有向无环图,拓扑排序结果不唯一,可以用宽度优先遍历和深度优先遍历实现。应用场景有事件安排、编译顺序等
* 在图中找到所有入度为0的点输出
* 把所有入度为0的点在图中删掉继续找入度为0的点输出周而复始
* 图的所有点都被删除后,依次输出的顺序就是拓扑排序
<br />
### 最小生成树(无向图)
&emsp;&emsp;最小生成树要求无向图可以使用Kruskal和Prim算法实现Kruskal和Prim都是贪心算法。
#### Kruskal算法使用并查集
* 总是从权值最小的边开始考虑,依次考察权值依次变大的边
* 当前的边要么进入最小生成树的集合,要么丢弃
* 如果当前的边进入最小生成树的集合中不会形成环,就要当前边
* 如果当前的边进入最小生成树的集合中会形成环,就不要当前边
* 考察完所有边之后,最小生成树的集合也得到了
#### Prim算法
* 1可以从任意节点出发来寻找最小生成树
* 2某个点加入到被选取的点中后解锁这个点出发的所有新的边
* 3在所有解锁的边中选最小的边然后看看这个边会不会形成环
* 4如果会不要当前边继续考察剩下解锁的边中最小的边重复3
* 5如果不会要当前边将该边的指向点加入到被选取的点中重复2
* 6当所有点都被选取最小生成树就得到了
### 最短路径算法
&emsp;&emsp;可以使用Dijkstra算法实现最短路径。
#### 迪瑞克斯拉算法(有向、无负权重、可以有环)
1Dijkstra算法必须指定一个源点
2生成一个源点到各个点的最小距离表一开始只有一条记录即原点到自己的最小距离为0源点到其他所有点的最小距离都为正无穷大
3从距离表中拿出没拿过记录里的最小记录通过这个点发出的边更新源点到各个点的最小距离表不断重复这一步
4源点到所有的点记录如果都被拿过一遍过程停止最小距离表得到了
#### Dijkstral算法优化
&emsp;&emsp;利用加强堆
&emsp;&emsp;某个节点最短距离改变时做动态调整

@ -39,3 +39,83 @@
#### 6、反转栈不能使用额外集合只能使用递归函数
&emsp;&emsp;两个递归函数reverse使用f函数拿到栈底元素然后递归将栈底元素压栈。利用局部变量保存栈底元素系统函数调用栈保证所有元素逆序。f函数拿到栈底元素并保证其他元素顺序不变。
<br />
<br />
### 暴力递归和动态规划的关系
* 某一个暴力递归,有解的重复调用,就可以把这个暴力递归优化成动态规划
* 任何动态规划问题,都一定对应着某一个有重复过程的暴力递归
* 但不是所有的暴力递归,都一定对应着动态规划
### 暴力递归的优化
* 有重复调用同一个子问题的解,这种递归可以优化
* 如果每一个子问题都是不同的解,无法优化也不用优化
&emsp;&emsp;解决一个问题,可能有很多尝试方法,可能在很多尝试方法中,又有若干个尝试方法有动态规划的方式,一个问题可能有若干种动态规划的解法。
### 如何找到某个问题的动态规划方式?
1设计暴力递归重要原则+4种常见尝试模型
2分析有没有重复解列出调用过程的前几层就能发现是否有重复解
3用记忆化搜索 -> 用严格表结构实现动态规划:套路解决
4看看能否继续优化套路解决
### 暴力递归过程的原则
1每一个可变参数的类型一定不要比int类型更加复杂
2原则1可以违反让类型突破到一维线性结构那必须是单一可变参数
3如果发现原则1被违反但不违反原则2只需要做到记忆化搜索即可
4可变参数的个数能少则少
5面试中违反以上原则的概率很小
### 常见的4种尝试模型
* 从左往右的尝试模型
* 范围上的尝试模型
* 多样本位置全对应的尝试模型
* 寻找业务限制的尝试模型
### 暴力递归到动态规划的套路
* 你已经有了一个不违反原则的暴力递归,而且的确存在解的重复调用
* 找到哪些参数的变化会影响返回值,对每一个列出变化范围
* 参数间的所有的组合数量,意味着表大小
* 记忆化搜索的方法就是傻缓存,非常容易得到
* 规定好严格表的大小,分析位置的依赖顺序,然后从基础填写到最终解
* 对于有枚举行为的决策过程,进一步优化
动态规划的进一步优化
* 空间压缩
* 状态化简
* 四边形不等式
* 其他优化技巧
#### 机器人问题
&emsp;&emsp;假设有排成一行的N个位置记为1~NN 一定大于或等于2。开始时机器人在其中的M位置上(M一定是 1~N 中的一个)如果机器人来到1位置那么下一步只能往右来到2位置如果机器人来到N位置那么下一步只能往左来到 N-1 位置;如果机器人来到中间位置,那么下一步可以往左走或者往右走;规定机器人必须走 K 步最终能来到P位置(P也是1~N中的一个)的方法有多少种?
&emsp;&emsp;给定四个参数 N、M、K、P返回方法数。
#### 扑克问题
&emsp;&emsp;给定一个整型数组arr代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌规定玩家A先拿玩家B后拿。但是每个玩家每次只能拿走最左或最右的纸牌玩家A和玩家B都绝顶聪明。请返回最后获胜者的分数。
#### 背包问题
&emsp;&emsp;给定两个长度都为N的数组weights和valuesweights[i]和values[i]分别代表 i号物品的重量和价值。给定一个正数bag表示一个载重bag的袋子你装的物品不能超过这个重量。返回你能装下最多的价值是多少?
#### 数字和字符转化问题
&emsp;&emsp;规定1和A对应、2和B对应、3和C对应...26和Z对应。那么一个数字字符串比如"111”就可以转化为:"AAA"、"KA"和"AK"。给定一个只有数字字符组成的字符串str返回有多少种转化结果
#### 剪裁贴纸
&emsp;&emsp;给定一个字符串str给定一个字符串类型的数组arr出现的字符都是小写英文。arr每一个字符串代表一张贴纸你可以把单个字符剪开使用目的是拼出str来。返回需要至少多少张贴纸可以完成这个任务。
&emsp;&emsp;例子str= "babac"arr = {"ba","c","abcd"} ba + ba + c 3 abcd + abcd 2 abcd+ba 2 所以返回2
#### 最长公共子串
&emsp;&emsp;给定两个字符串str1和str2返回这两个字符串的最长公共子序列长度。
&emsp;&emsp;比如 str1 = “a12b3c456d”,str2 = “1ef23ghi4j56k”。最长公共子序列是“123456”所以返回长度6

@ -1,16 +1,85 @@
#### 合并K个升序链表 LeetCode2
## <center>二叉树</center>
使用PriorityQueue保存各链表的头结点每次prorityQueue.poll()弹出最小值注意单链表的next指针和中间变量。循环终止条件为优先级队列变空。
递归序 每个节点都到达三次
二叉树
先序遍历 头左右
中序遍历 左头右
后序遍历 左右头
#### 二叉树的先序、中序、后序遍历
递归序 每个节点都到达三次
先序:任何子树的处理顺序都是,先头节点、再左子树、然后右子树
中序:任何子树的处理顺序都是,先左子树、再头节点、然后右子树
后序:任何子树的处理顺序都是,先左子树、再右子树、然后头节点
#### 递归方式实现二叉树的先序、中序、后序遍历
1理解递归序
2先序、中序、后序都可以在递归序的基础上加工出来
3第一次到达一个节点就打印就是先序、第二次打印即中序、第三次即后序
#### X 祖先节点 交集
<br />
<br />
&emsp;&emsp;X是一棵二叉树的某一个节点A是二叉树先序遍历X的左边部分B是二叉树后序遍历X右边部分AB相交的结果是且仅是X的所有父节点。
1、X的所有父节点在先序遍历的左边X的所有父节点在后序遍历的右边交集一定包含所有父节点
2、X的所有子节点在先序遍历的左边X的所有子节点在后序遍历的左边交集一定不包含所有子节点
3、A不包含所有祖先节点的右兄弟节点B不包含所有祖先节点的左兄弟节点
&emsp;&emsp;X的所有祖先节点、X自己、X的子节点、X或者X的父节点作为左树的右兄节点、X或者X的父节点作为右树的左兄节点
<br />
<br />
#### 非递归方式实现二叉树的先序、中序、后序遍历
1任何递归函数都可以改成非递归
2自己设计压栈的来实现
#### 实现二叉树的按层遍历
1其实就是宽度优先遍历用队列
2可以通过设置flag变量的方式来发现某一层的结束
#### 实现二叉树的序列化和反序列化
1先序方式序列化和反序列化
2按层方式序列化和反序列化
#### 将多叉树编码为二叉树 LeetCode431
#### 打印二叉树
#### 二叉树的宽度
求二叉树最宽的层有多少个节点
#### 找出某个节点的后继节点
&emsp;&emsp;二叉树结构如下定义:
```Java
Class Node {
V value;
Node left;
Node right;
Node parent;
}
```
&emsp;&emsp;给你二叉树中的某个节点,返回该节点的后继节点。
#### 折纸
&emsp;&emsp;请把一段纸条竖着放在桌子上然后从纸条的下边向上方对折1次压出折痕后展开。此时折痕是凹下去的即折痕突起的方向指向纸条的背面。 如果从纸条的下边向上方连续对折2次压出折痕后展开此时有三条折痕从上到下依次是下折痕、下折痕和上折痕。
给定一个输入参数N代表纸条都从下边向上方连续对折N次。 请从上到下打印所有折痕的方向。
例如:N=1时打印: down N=2时打印: down down up
主要解题思路是递归
二叉树的递归套路
1假设以X节点为头假设可以向X左树和X右树要任何信息
2在上一步的假设下讨论以X为头节点的树得到答案的可能性最重要
3列出所有可能性后确定到底需要向左树和右树要什么样的信息
4把左树信息和右树信息求全集就是任何一棵子树都需要返回的信息S
5递归函数都返回S每一棵子树都这么要求
6写代码在代码中考虑如何把左树的信息和右树信息整合出整棵树的信息
#### 判断两颗二叉树是否相同 LeetCode100
#### 判断一个二叉树是否对称 LeetCode101
@ -27,6 +96,7 @@
#### 平衡二叉树 LeetCode110
给定一棵二叉树的头节点head返回这颗二叉树是不是平衡二叉树
判断二叉树是否是平衡二叉树
左子树的高度和右子树的高度相差不超过1则称为平衡二叉树。
@ -39,12 +109,52 @@
#### 达标路径总和 LeetCode113
X是一棵二叉树的某一个节点A是二叉树先序遍历X的左边部分B是二叉树后序遍历X右边部分AB相交的结果是且仅是X的所有父节点。
1、X的所有父节点在先序遍历的左边X的所有父节点在后序遍历的右边交集一定包含所有父节点
2、X的所有子节点在先序遍历的左边X的所有子节点在后序遍历的左边交集一定不包含所有子节点
3、A不包含所有祖先节点的右兄弟节点B不包含所有祖先节点的左兄弟节点
#### 二叉树的最大距离
给定一颗二叉树的头结点,任何两个节点之间都存在距离,返回整颗二叉树的最大距离。
#### 判断完全二叉树
判断二叉树是否是完全二叉树
按照二叉树的定义判断,逐行遍历
递归套路
#### 满二叉树
给定一棵二叉树的头节点head返回这颗二叉树是不是满二叉树
#### 最大的二叉搜索子树
给定一棵二叉树的头节点head返回这颗二叉树中最大的二叉搜索子树的头节点。
#### 最大的二叉搜索子树大小
给定一棵二叉树的头节点head返回这颗二叉树中最大的二叉搜索子树的大小。
#### 最低公共祖先
给定一棵二叉树的头节点head和另外两个节点a和b。返回a和b的最低公共祖先。
a、b可能在head的左右子树也可能在同一棵子树。
使用中间变量
1、遍历整棵树用map保存所有节点的父节点
2、遍历a的所有父节点用set保存
3、遍历b的所有父节点看set中是否存在
后序遍历递归套路
#### 派对的最大快乐值
&emsp;&emsp;员工信息的定义如下:
X的所有祖先节点、X自己、X的子节点、X或者X的父节点作为左树的右兄节点、X或者X的父节点作为右树的左兄节点
```Java
class Employee {
public int happy; // 这名员工可以带来的快乐值
List<Employee> subordinates; // 这名员工有哪些直接下级
}
```
给定一颗二叉树的头结点,任何两个节点之间都存在距离,返回整颗二叉树的最大距离
二叉树node的a、b两个节点的最低公共祖先可以使用递归套路也可以使用set或者map结合后序遍历实现
&emsp;&emsp;公司的每个员工都符合 Employee 类的描述。整个公司的人员结构可以看作是一棵标准的、 没有环的多叉树。树的头节点是公司唯一的老板。除老板之外的每个员工都有唯一的直接上级。 叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外,每个员工都有一个或多个直接下级。
&emsp;&emsp;这个公司现在要办party你可以决定哪些员工来哪些员工不来规则
&emsp;&emsp;1.如果某个员工来了,那么这个员工的所有直接下级都不能来
&emsp;&emsp;2.派对的整体快乐值是所有到场员工快乐值的累加
&emsp;&emsp;3.你的目标是让派对的整体快乐值尽量大
&emsp;&emsp;给定一棵多叉树的头节点boss请返回派对的最大快乐值。

@ -1,10 +1,59 @@
贪心算法
## <center>贪心算法</center>
a.b<<b.ab.c<<c.b a.c<<c.a
### 贪心算法
贪心算法不需要证明,使用对数器证明
* 1最自然智慧的算法
* 2用一种局部最功利的标准总是做出在当前看来是最好的选择
* 3难点在于证明局部最功利的标准可以得到全局最优解
* 4对于贪心算法的学习主要以增加阅历和经验为主
分割金条60长度分成10、20、30
每次切割最大长度的反例97、98 和 100、99
反例的证明就是哈夫曼编码
将所有分割后的长度放入小根堆,每次获取两个长度相加然后放回小根堆,重复
### 贪心求解的标准过程
* 1分析业务
* 2根据业务逻辑找到不同的贪心策略
* 3对于能举出反例的策略直接跳过不能举出反例的策略要证明有效性这往往是特别困难的要求数学能力很高且不具有统一的技巧性
### 贪心算法的解题套路
* 1实现一个不依靠贪心策略的解法X可以用最暴力的尝试
* 2脑补出贪心策略A、贪心策略B、贪心策略C...
* 3用解法X和对数器用实验的方式得知哪个贪心策略正确
* 4不要去纠结贪心策略的证明
### 贪心算法常见问题
#### 最小字典序
&emsp;&emsp;给定一个由字符串组成的数组strs必须把所有的字符串拼接起来返回所有可能的拼接结果中字典序最小的结果。
&emsp;&emsp;a.b&lt;b.a并且b.c&lt;c.b 得到a.c&lt;c.a比较的传递性。
#### 分割金条
&emsp;&emsp;分割金条60长度分成10、20、30。一块金条切成两半是需要花费和长度数值一样的铜板的。比如长度为20的金条不管怎么切都要花费20个铜板。 一群人想整分整块金条,怎么分最省铜板?
&emsp;&emsp;例如,给定数组{10,20,30}代表一共三个人整块金条长度为60金条要分成102030三个部分。如果先把长度60的金条分成10和50花费60; 再把长度50的金条分成20和30花费50;一共花费110铜板。但如果先把长度60的金条分成30和30花费60;再把长度30金条分成10和20 花费30;一共花费90铜板。
&emsp;&emsp;输入一个数组,返回分割的最小代价。暂不考虑切割的顺序,如果考虑切割顺序需要使用动态规划和四边形不等式。
&emsp;&emsp;暴力尝试双层循环将i和j合并递归得到花费最后将最小的花费返回。
&emsp;&emsp;将所有分割后的长度放入小根堆,每次获取两个长度相加然后放回小根堆,重复直到小根堆只剩一个元素。
&emsp;&emsp;每次都切割最大长度的反例97、98 和 100、99。正确操作是第一次切割成100+99和98+97而不是切割成100和99+98+97。<font color="red">反例的证明就是哈夫曼编码。</font>
#### 点灯
&emsp;&emsp;给定一个字符串str只由X.两种字符构成。X表示墙不能放灯也不需要点亮。.表示居民点可以放灯需要点亮。如果灯放在i位置可以让i-1i和i+1三个位置被点亮。返回如果点亮str中所有需要点亮的位置至少需要几盏灯。
#### 安排会议室
&emsp;&emsp;一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。给你每一个项目开始的时间和结束的时间,你来安排宣讲的日程,要求会议室进行的宣讲的场次最多。返回最多的宣讲场次。
#### IPO
&emsp;&emsp;输入: 正数数组costs、正数数组profits、正数K、正数M
&emsp;&emsp;costs[i]表示i号项目的花费
&emsp;&emsp;profits[i]表示i号项目在扣除花费之后还能挣到的钱(利润)
&emsp;&emsp;K表示你只能串行的最多做k个项目
&emsp;&emsp;M表示你初始的资金
&emsp;&emsp;说明: 每做完一个项目,马上获得的收益,可以支持你去做下一个项目。不能并行的做项目。
&emsp;&emsp;输出:你最后获得的最大钱数。

Loading…
Cancel
Save