1、时间复杂度、空间复杂度
时间复杂度:表示代码的运行时间,通过代码的执行次数来表示
1 T(n) = 3n,时间复杂度是 T(n) = o(n),例如:一个 for 循环
2 T(n) = 5logn,时间复杂度是 T(n)=O(logn),例如:每次都/2,逐级递减
3 T(n) = 2,时间复杂度是 T(n) = O(1)
4 T(n) = 0.5 n n + 0.5n,时间复杂度是 T(n) = O(n²),例如:2 个 for 循环
5 T(n) = nlogn,时间复杂度为 O(nlogn),例如:每次/2,外层再来个 for 循环
递归一般都是 O(n)
空间复杂度:对一个算法在运行过程中临时占用存储空间大小的一个量度,反映的对内存占用的趋势,而不是具体内存
详解时间复杂度和空间复杂度
1、数组
1.1、二分查找
1 | function binarySearch(arr, target) { |
1.2 快排
1.3 归并排序
利用双指针 while,一个数组一个指针
例题
有序数组内两个数字和为 target,并且返回索引值(167)
方法一:暴力解法双循环
方法二:利用二分查找法+target-arr[i]
方法三:对撞指针
找到有序数组中最短的连续子数组,子数组之和>=s(209)
注意是连续的
解法一:暴力解法,遍历所有的子数组
解法二:滑动窗口,必须是有序的,两个指针不停+1,取>s 的数组,并且和上一次满足跳转的数组长度做比较
一个字符串中寻找没有重复字母的最长子串(3)
解法一:暴力解法,遍历出所有的子串
解法二:滑动窗口,出现重复,将左侧指针指向重复的索引位置
找出字符串 p,是 s 的子串的索引(438)
注意:可以非连续子串
如: s=”abab” p=”ba” 返回[0,1,2]
解法一:暴力解法,双循环,第二个循环从第 n 位开始(子串长度)
解法二:滑动窗口
给定一个字符串 S 和 T,在 S 中寻找最短的子串,包含 T 中的所有字符(76)
如 S=”ADOBECODEBANC” T=”ABC” 结果为”BANC”
解法一: 滑动窗口
2、查找(map 和 set)
C++中:
map 和 set 底层实现为平衡二叉树
unordered_map 和 unordered_set 为哈希表
2.1 给一个数组(可以无序),返回两个值等于 target 的索引(1)
例如: numbs = [2,7,11,15] target = 9 返回 [0,1]
方法一:暴力解法,遍历所有数据对求和
方法二:先排序,后对撞指针,但是注意保留之前的索引值(存到对象内 key:0,value:值)
方法三:将所有元素放到查找表(有坑,只遍历一次),之后对每个 a,查找 target-a 是否存在就可以了,注意遍历的时候,遍历到第二个相同的元素的时候的处理,不要一上来就都遍历一遍都放到表中(v4-3)
2.2 寻找一个数组中的三个值,使得 a+b+c=0 (15)(18)(16)
这个是 3sum 题,如果改成 4sum 呢 或者更接近值而不是等于
如: numbs = [-1,0,1,2,-1,-4], 结果为[[-1.0,1],[-1,-1,2]]
方法一:暴力解法:遍历 2 遍/3 遍/4 遍循求 2sum 3sum 4sum,注意遍历中索引不能相同
方法二:将其中一遍放到 map 中,减少一层遍历,和 target-sum 对比
方法三:如果是 4sum,先遍历 2 遍(从 3,4 位开始),将两个数的和放到一个 map 中,然后再遍历 2 遍,其中对比 map 中是否有符合要求的
2.3 给出四个数组,寻找 A[i]+B[j]+C[k]+D[l]=0 (454)
拆分成两个双层遍历
第一个双层,将和放到 map 中
第二个双层,判断 map 中是否有 0-A[i]-B[j]
2.4 通过颠倒字符顺序仙的单词进行分组(49)
[“eat”,”tea”,”tan”,”ate”,”nat”,”bat”] 返回 [[“eat”,”tea”,”ate”],[“nat”,”tan”],[“bat”]]
方法一:遍历,将单词排序,然后将 sort 后的顺序最为 key 存到 map 中, 之后每个都排序,在 map 中查找
2.5 有 n 个点,取其中 3 个点使两边相同,求排列数(447)(149)
例如:给 3 个点[[0,0],[1,0],[2,0]] 分别为 i,j,k 要求 ij ==ik
结果:[[1,0],[0,0],[2,0]]和[[1,0],[2,0],[0,0]]
方法一:暴力解法 遍历 3 层
方法二:设定一个为 i 点,然后分别求剩下的点到 i 的距离 v-4-3
1 | var map = { |
2.6 一个数组,是否存在索引 i 和 j,使 numbs[i] == numbs[j]且 i 和 j 之间的差不超过 k(219)
方法一:暴力解法,循环 2 次
方法二:滑动窗口,先遍历 j 到 k 的长度,如果都没有找到,则滑动窗口进行寻找
3、链表
注意:所有的链表都能转成数组来解决!!!!!!
3.1 每两个相邻的链表交换位置(24)
1、先创建虚拟节点 p
2、标注 node1、node2、next 节点
3、p 挪动到下一次要移动的前边就是 node1
1 | // 迭代-视频解法 |
3.2 链表排序
可以用上边的迭代方法来进行比较互换
插入排序(147)
归并排序(148)
3.3 删除某个节点(237)
正常是知道前一个节点,直接用前一个节点 next 指向下一个节点就可以了
但是:现在那不到前一个节点
解法:把当前节点直接改成下一个节点,然后将指向下下个节点
3.5 删除倒数第 n 个节点(19)
通常删除节点,要创建一个虚拟节点,避免第一个就是要删除节点
方法一:改为数组实现
方法二:遍历一遍得到长度,然后再遍历一次进行删除
方法三:滑动窗口,虚拟节点(对应要删除前一个节点),q 节点对应 null,一次遍历直到 q 变为 null
3.6 L(0)→L(1)→L(2)转换为 L(0)→L(n)→L(1)→L(n-1)(143)
方法一:转化成数组做
方法二:截取一半,将后半部分反转,之后进行拼接
3.7 判断是回文链表(234)
方法一:转换为数组
方法二:快慢指针,取到中间位置,反转后边链表,和前边比较
3.7 链表反转
方法一:转换为数组
方法二:就地反转(迭代)
方法三:递归
方法四:新建一个链表
1 | // 就地反转-迭代 |
4.栈和队列
4.1 判断括号是否合法(20)
方法一:利用栈,判断当前是否是闭合标签,和 pop 出来的最后一个值看是或否配对
方法二:双指针回文串
4.2 简化路径(71)
方法一: 利用栈,创建一个新数组,当发现..的时候,将最后一项 pop 出来
4.3 递归算法 - 树的遍历 - 深度优先遍历 - 前中后序遍历(144,94,145)
除了利用递归外,还可以利用栈来遍历模拟递归过程,类似于广度遍历,创建一个[],然后进行 while+push 操作
4.4 扁平化数组(341)
1 | function flatten(arr, result = []) { |
4.5 队列
基本应用:广度优先遍历(bfs)
树:层序遍历
图: 无权图的最短路径
4.6 二叉树的层序遍历(102)(107)
每次取都取队首的,然后判断是否有子节点,有的话就 push 到队列中,一直循环
4.7 二叉树之子顺序遍历(103)
4.8 返回右侧可以看见的节点(199)
4.9 图的最短路径问题
4.10 寻找最少的完全平方数,使他们的和为 n(279)
例如:
12=4+4+4; 13=4+9
利用图论解决:创建节点和边
建模:具体题解思路
从 n 到 0,每个数字表示一个节点
如果两个数字 x 到 y 相差一个完全平方数,则连接一条边
我们得到一个无权图
原题转化为无权图中从 n 到 0 的最短路径问题
leetcode 建模:
树的数据结构
5
4(5-11) 1(5-22)第二层
3(4-11) 0(4-22) 0(1-1*1) 第三层
利用广度优先遍历,遍历的时候记得存储层数
1 | var numSquares = function (n) { |
4.11 单词最短变换路径,单词接龙 (127) (127II)
[‘hot’, ‘dot’, ‘dog’, ‘lot’, ‘log’, ‘cog’,]
hit→cog
方法一:尝试暴力解法,将所有组合都匹配出来
方法二:转化为图,bfs
4.12 优先队列
寻找最大值或最小值,按顺序出列
4.13 给出前 k 个出现频率最高的元素(347)
[1,1,1,2,2,3] k=2 返回[1,2]
方法一:利用 obj 来计数,然后进行排序
方法二:维护优先队列
4.14 k 个有序数组,归并为一个有序数组(23)
5 二叉树和递归
递归条件:(一定要找到这两点)
1、递归终止条件
2、递归过程
利用递归的返回值
5.1 二叉树类型
1、满二叉树:每一层节点数达到最大
2、完全二叉树:除了最后一层,其余都是满的,最后一层还要从左开始排列
3、二叉查找树:左小,右大
4、平衡二叉树:是一颗二叉查找树,并且左右两个子树高度不能超过 1
参考资料
5.2 求一颗二叉书最高的深度(104)
1 | var maxDepth = function (root) { |
5.3 求一颗二叉树的最低深度(111)
方法一:广度优先遍历
方法二: 递归所有路径高度,比较深度
5.4 反转二叉树(226)
4 → 4
2 7 → 7 2
1 | var invertTree = function (root) { |
5.5 给两颗二叉树,判断这两颗二叉树是否完全一样(100)
5.6 一颗二叉树,判断是否左右对称(101)
5.7 求完全二叉树的节点个数(222)
5.8 判断一颗二叉树是否是平衡二叉树(110)
5.9 判断二叉树和为 sum 的路径,必须包含子叶,必须到底(112)
方法一:递归终止条件,root==null 和 sum-nodeVal = 0
1 | const hasPathSum = (root, sum) => { |
5.10 求出二叉树所有左叶子和(404)
5.11 返回所有表示根节点到叶子节点路径的字符串(257)
递归,利用返回结果
终止条件:一般都是下边这个
1 | if (root == null) return res; // 遍历到null节点, 只有首次传入null才会到这里 |
1 | var binaryTreePaths = function (root) { |
5.12 返回所有从根节点到叶子节点的路径,和为 sum(113II)
和上边一样就是字符串变为数组
5.13 每个节点只包含数字 0-9,求根节点到叶子节点的路径组装成一个数,所有路径的和(129)
1
2 3
就是 12+13=25
5.14 路径和为 sum,不一定为根节点开始,不一定叶子节点结束(437III)
思考:
node 分两种情况,在这个路径内,和不在这个路径内
pathSum 内 page 代表包含,sum1 和 sum2 为不包含的处理
1 | var pathSum = function (root, sum) { |
5.15 二分搜索树
插入、查找、删除
最大值、最小值
前驱、后继
上界、下界
某个元素的排名
寻找第 k 大元素
5.16 寻找二分搜索树两个节点的最近公共祖先(235)
如果一个小于等于 node 一个大于等于 node 则是公共祖先
1 | const lowestCommonAncestor = (root, p, q) => { |
5.17 给二叉树,让验证是一颗二分搜索树(98)
5. 补1 将有序数组转换为二叉搜索树(108)
5.18 删除二分搜索树上其中一个节点(450)
5.19 将有序数组转化为一颗平衡二分搜索树(108)
5.20 给一个二分搜索树,求这颗树上第 k 小的元素(230)
5.21 找非二分搜索树的最近公共祖先 LCA(236)
6. 递归和回溯
回溯:结果往上返回到顶层-也是暴力写法的一种
排列问题–全排列
组合问题–组合总和
都转成树形问题
二维平面上使用回溯法
floodfill 算法 经典问题 深度优先遍历
6.1 手机键盘,数字字符串,给出所有字母组合(17)
输入:”23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”]
可以先看成树形
a b c
def def def
树形都能递归解决
方法一:回溯
方法二:树形结构都能 dfs 和 bfs 进行遍历
1 | //回溯 |
6.2 给数字加三个点,生成合法 ip(93)
输入:s = “25525511135”
输出:[“255.255.11.135”,”255.255.111.35”]
6.3 拆分字符串,使子串都是回文字符串(131)
输入:s=aab (一个字符也是回文字符串)
输出:[[‘aa’, b],[a,a,b]]
6.4 给一个数组,返回所有排列可能-全排列(46)
输入: [1,2,3]
输出: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
1 | const permute = (nums) => { |
6.5 给一个数组,返回所有排列可能(47)
输入: [1,1,2]
输出: [[1,1,2],[1,2,1],[2,1,1]]
6.6 给 n 个数,求 k 个数字的所有组合,不管顺序(77)
n=4 k=2
[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
1 | const combine = (n, k) => { |
6.7 给一个集合,从集合中取几个数(可使用多次)让和为 T,求这样的子集合(39)
输入:nums=[2,3,6,7] T=7
输出: [[7],[2,2,3]]
6.8 与上题不同,其中元素可以相同,一个元素只能使用一次(40)
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出: [[1, 7],[1, 2, 5],[2, 6],[1, 1, 6]]
6.9 1-9 中选出 k 个数字,每个数字只能使用一次,和为 n(216)
输入: k = 3, n = 7
输出: [[1,2,4]]
6.10 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)(78)
说明:解集不能包含重复的子集。
输入: nums = [1,2,3]
输出:
[[3],[1],[2],[1,2,3],[1,3],[2,3],[1,2],[]]
6.11 和上题一样,区别就是可以重复(90)
输入: [1,2,2]
输出:[[2],[1],[1,2,2],[2,2],[1,2],[]]
6.12 二进制手表,n 个灯是亮的,打印所有时间可能(401)
忽略
6.13 二维平面回溯法
6.14 给定一个二维网格和一个单词,找出该单词是否存在于网格中(79)
说明:
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
board =
[
[‘A’,’B’,’C’,’E’],
[‘S’,’F’,’C’,’S’],
[‘A’,’D’,’E’,’E’]
]
给定 word = “ABCCED”, 返回 true
给定 word = “SEE”, 返回 true
给定 word = “ABCB”, 返回 false
1 | var exist = function (board, word) { |
6.15 floodfill 算法 经典问题 深度优先遍历
6.16 给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。(200)
说明:岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
输入:grid = [
[“1”,”1”,”0”,”0”,”0”],
[“1”,”1”,”0”,”0”,”0”],
[“0”,”0”,”1”,”0”,”0”],
[“0”,”0”,”0”,”1”,”1”]
]
输出:3
1 | var numIslands = function (grid) { |
6.17 被包围(130)
给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。
找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充
示例:
X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:
X X X X
X X X X
X X X X
X O X X
6.18 回溯法是经典的人工智能的基础
6.19 N 皇后问题(51)+ 数独(37)
7 动态规划
递归是自上而下(从最大往回推)
动态规划是自下而上(从 0/1 起始开始往后)
递归问题 → 重叠子问题/(最优子结构) → 记忆化搜索(自顶向下)
→→→→→→→→→→→→→→→→→→→→ 动态规划 (自底向上)
7.1 斐波那切数列
1 | function fib(n) { |
7.2 楼梯 n 节,可以走 1/2 节楼梯(70)
走 1 步,剩下爬 n-1 阶台阶
走 2 步,剩下爬 n-2 阶台阶
1 | // 和菲波那切数列一样,需要存储优化 |
7.3 一个三角形数组,自底向上一条路径和最小(120)
7.4 m*n 的矩阵,从左上角向右下角走,和最小(64)
7.5 正整数 n,分割至少 2 个数,返回最大乘机(343)
10=(3+3+4) 乘机为 36
方法一:暴力解决
分割 4
4→1
→→2→1
→→3→2
分割 n
1….n-1
相当于从 n-1 一直到 1
1 | // 递归 |
7.6 给一个正整数,找最少完全平方数,使和为 n(279)
12 = 4+4+4
13=4+9
方法一:之前有用图轮处理过,参考 4.10
7.7 a-z 对应 1-26,给一个值,看有多少种方法可以解析这个数字字符串(91)
给 12 有 AB 和 L 两种情况
返回 2
7.8 机器人,m*n 矩阵,从左上向右下,只能右和下,有多少种走法(62)
7.9 和上题相同但是中间多了障碍物(63)
输入:obstacleGrid = [
[0,0,0],
[0,1,0],
[0,0,0]
]
输出:2
7.10 打劫问题(198)
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
方法一:暴力解法 求出所有的组合
方法二:动态规划
偷取 0 考虑 2..n-1 → 再偷取 2 考虑 4..n-1/再偷取 3 考虑 5..n-1 …..
偷取 1 考虑 3..n-1 → 再偷取 3 考虑 5..n-1/再偷取 4 考虑 6..n-1 …..
状态定义:
考虑偷取[x…n-1]范围里的房子(函数的定义)
这里是考虑,并不是一定要偷
状态转移方程:
f(0) = max(v(0)+f(2),v(1)+f(3)….v(n-3)+f(n-1),v(n-2),v(n-1))
其实还可以定义其他状态:
考虑偷取[0,x]范围里的房子(函数的定义)
可以尝试做一下
1 | // 迭代-记忆化搜索-自顶向下 |
7.11 和上题相同,唯一不同是环形街道(213II)
7.12 和上题相同,唯一不同是二叉树形状的社区(337)
7.13 买卖股票问题(309)
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
7.14 0-1 背包问题
方法一:暴力解法,放进背包中,不放进背包中
参数个数:约束条件个数
F(n,C) n 个物品放入容量为 C 的最大价值
状态方程:
F(i,c) = Max( F(i-1, c), v(i) + F(i-1, c-w(i)))
Max(不放入+ 放入)
1 | // 递归 |
优化:
方法一:只能优化空间复杂度
现在是 n 行,其实可以只用 2 行就可以了,利用%来判断奇偶数,来互相覆盖
方法二:极致优化
可以优化到 1 行,因为只看左侧和上侧的值,所以可以从右侧开始处理
7.15 背包变种
7.16 完全背包-每个物品可以无限使用
但是容量是有限的,转化成有限问题
7.17 多重背包-每个物品有 num(i)个
类似完全背包
7.18 多维费用背包问题-考虑体积和重量,两个维度
多了一个状态,创建三位数组
1 | var arr = [ |
7.19 约束-互相排斥,放入 A 就不能放入 B
7.20 约束-依赖,放入 A 就必须放入 B
7.21 背包实战-分割等和子集(416)
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
思路:
如果可以拆分,那必定含有一些元素相加后等于总和/2
这就是一个限制条件
转化为给 n 个物品,填满 c 的背包
1 | // 迭代 |
7.22 零钱兑换(322)
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
7.23 组合总和 Ⅳ(377)
给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。
nums = [1, 2, 3]
target = 4
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。
因此输出为 7。
7.24 单词拆分(139)
给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以被拆分成 “leet code”。
7.25 目标和(494)
给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
输入:nums: [1, 1, 1, 1, 1], S: 3
输出:5
解释:
-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3
一共有 5 种方法让最终目标和为 3。
7.26 一和零(474)
7.28 最长子序列问题
7.29 最长递增子序列(300)
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
方法一:暴力解法-给出所有组合
方法二: 动态规划
LIS(i) 第 i 个数为结尾的最长上升子序列
转化为下边更好理解
LIS(i) 表示[0…i]的范围内,选择数字 nums[i]可以获得最长上升子序列
1 | // 动态规划 自底向下 |
7.30 摆动序列(376)
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
主要就是高低交替!
示例 1:
输入: [1,7,4,9,2,5]
输出: 6
解释: 整个序列均为摆动序列。
7.31 最长公共子序列 LCS(1143)
像基因工程,相似度
S1 和 S2
S1=ABCD
S2=AEBD
公共子序列为 ABD
两个维度
LCS(m,n) S1[0..m]和 S2[0…n]的最长公共子序列的长度
其实和上一到题相同
方法一:递归思想 自顶向下
S1[m] == S2[n]
LCS(m,n) = 1+ LCS(m-1, n-1)
S1[m] != S2[n]
LCS(m,n) = Math.max(LCS(m, n-1), LCS(m-1, n))
方法二:自底向上
1 |
7.32 dijktra 单元最短路径算法
之前用图论解的
也可以尝试用动态规划
shortPath(i) 为从 start 到 i 的最短路径长度
shortPath(x) = min(shortPath(a)+w(a→x))
7.33 求完全解
即不仅仅查找到最大值,也要打印出相应的值或者数组
解法思路
1.1 双索引
1.1.1 对撞指针
1、回文串问题
2、数组翻转问题 –设置一个缓存变量来,交换左右两个位置
3、有序数组两数字和为 target(167)
4、求两面墙容积最大(11)
1.1.2 滑动窗口
while
1、有序数组中连续子数组求和(209)
2、查找不汉重复字符的子串(3)V-3-8
3、找出包含所有子串字符的索引(438) V-3-8
4、链表(19) v-5-4
2.1 多层遍历
1、4 层拆成 2 个 2 层
3.1 Map 和 Set
1、3sum 4sum 的值,充分利用 target-a
2、相同字符串,可以颠倒顺序
4、查找边和点的次数个数问题
4.1 补虚拟头节点+node1+node2+next 节点
next 可以不要
1、链表交换
4.2 遍历一遍找到链表中间值
快慢指针-基本思想:
设置两个指针 p,q,初始时,分别指向头结点,循环使 p 后移两位,q 后移一位,当单链表遍历完毕时,q 的位置就是中间位置。
5.1 最短路径
树/图论/动态规划
其他算法
1.1 旋转图像
首页将输入
1 2 3
4 5 6
7 8 9
通过交换 matrix[i][j], matrix[j][i] 得到
1 4 7
2 5 8
3 6 9
最后将得到每组数组倒序排列即可
7 4 1
8 5 2
9 6 3
1 | var rotate = function (matrix) { |