const 简单的 , 太难的 = null ;
动态规划
在解题的过程中 , 感觉最重要的就是找到状态转移方程 , 例如
或 (斐波那契数列)
跳台阶 , 实际 , 就是 , 斐波那契数列
但有时候动态转移方程并不是那么好找 , for instance :
322. 零钱兑换 - 力扣(LeetCode)
做完这道题 , 我好似悟出了一点规律 , ( 欲练此功 , 必先自宫 ?
想要得到最后一个结果 , 必先推断此前的所有结果 , 比如我们想知道f(n) 的结果 , 此前 f(0) , f(1) , f(2) 至 f(n-1) 的结果都需要正确推断
基础数学之剪绳子
啊 , 动态规划 , 而且 , 经过多次演算发现 , 第二个循环一般在前5个就能找到最大值...
64. 最小路径和 - 力扣(LeetCode)
经典呐 , 也是高频考题了 , 第一次看此题 , 递归(搜索)在脑海里盘旋 , 所有路径都试一遍(😓) , 想了一下 , 应该还是受113. 路径总和 II - 力扣(LeetCode)这道题的影响
113是深度优先搜索里很经典的一题
其实 , 在遇到统计可行路径的数量 , 或者求最小路径 , 比较容易想到的两种做法 , 一个是搜索 , 另一个就是动态规划
搜索的做法 , 在数据规模比较小的时候考虑使用
题目要求每次只能向右或向下走 , 逆向思考就是 , 最后一格 , 只能由它左边或者上面那个格子走到
f(i , j) = f(i - 1 , j) + f(i , j - 1)
当然 , 需考虑第一行中只能右行 , 第一列只能由第一格下行得到 , 这是特殊情况
动态规划中 , 由最后的状态倒推过程 , 更容易得到状态转移方程 , 一般 , 会有些特殊情况不合方程 , 须单独考虑👻
递归
机器人的运动范围_(nowcoder.com)
解题时只想着递归 , 坐标即从(0,0)->(0,1)->(0,2)->(0,n)
结束一轮后开始(1,0)->(1,1)->(1,2)->(1,n)...一直到(m,n)若符合条件,则ans++,一直到遍历完可行走的方格.
然后就错掉了
因为机器人还可以向左向右走 , 初时设计的递归只会考虑向上向下两种情况 , 正确递归应设计为
dfs(i, j+1);//上
dfs(i,j-1);//下
dfs(i-1,j);//左
dfs(i+1,j);//右
这样难免会有重复情况出现 , 因而 , 在检查一个坐标是否符合条件后应标记为已检查 , 避免重复计数
另 : 在计算条件是否符合时 , 我先想到的是 , 将数字转化为字符串 , num.toString().split("") , 再转为单个数字计算 , 其实让问题更加复杂
若是单独计算个位 , 十位 , 百位 , 当更简洁
while(num != 0){
ans += (num % 10);
num /= 10;
//如果是js , 应改为
num = Math.floor(num / 10);
}
搜索 (深度优先搜索 && 广度优先搜索)
序列化二叉树_牛客网 (nowcoder.com)
序列化二叉树可用广度优先搜索(BFS) , 也就是层序遍历实现 , 当然 , 也可以用前序 , 中序 , 后序等其余几种遍历方式实现.
函数Deserrialize(s) , 参数s , 就是Serialize()中return 的字符串 , 故而 , 序列化二叉树的字符串并不唯一
bfs , 队列储存树的节点 , 重构的时候也是. 这样 , 就可以得到每一层中要补充left ,right 子树的节点
删除"1,2,3,"中最后的","
str = str.slice(0,-1)
二分排序
快速排序
215. 数组中的第K个最大元素 - 力扣(LeetCode)
其实就是返回排序后数组nums[nums.length - k] 重要的是学习快速排序这个常考算法 , 或者是快排中限定边界递归的思想
回溯
回溯和BFS(深度优先搜索)还是很相似 , 但也又不同吧
回溯就是 , 进行某一操作进入递归, 检查是否满足条件 , 撤销操作返回上一步
46. 全排列 - 力扣(LeetCode)
全排列中比较重要的一点就是 , 交换思路 , 整个数组通过不同元素的交换实现全排列
单调栈
84.柱状图中最大的矩形
暴力枚举 , 用for循环找出左右两边比 height[i] 要小的数 , 恭喜超时
优化思路 : 先push(i) , 将索引存入栈中 , 若是递增 , 无脑存入栈中即可( 比自己小的数就在旁边不是 ) , 一旦出现递减 , 以前递增过的数开始pop() , 留下其中较小的数 , 保证栈中数只有递增 , 没有递减 (果然是 , 单调栈 , 而且接雨水不也这样)
左边的栈从0开始检索 , 右边的栈从 length-1 开始检索
栈中储存的数即为比自己小的数 , 若栈为空(第一个数或者数组中最小的数) , 当 -1 或 n 处理
位运算(考的不多)
题目要求 , 不能用+ , - , * , / 进行运算
就说说不能使用 + 时,该用哪种位运算
首先进行非进位部分 , 不考虑进位 , 两数相加 , 可以用异或实现
异或的运算规则 :
相同数相加 , 为0 ,不同数相加 , 为 1
1^1 -> 0
0^0 -> 0
1^0 -> 1
0^1 -> 1
再次进行进位部分的运算 :
这是说当两个数都为 1 , 相加时得进位 , 可以通过位与实现 a & b
位与的运算规则 :
1 & 1 -> 1
1 & 0 -> 0
0 & 1 -> 0
0 & 0 -> 0
进位需要向更高位传递 , 需要将进位部分左移一位 , 相当于 × 2 , 与之对应 , 右移就是 ÷ 2
数据结构
数组 :
let array = new Array(rows).fill(0) //一维数组
使用箭头函数创建二维数组 :
let array = Array.from({length : rows},() => Array.from({length : cols},() => 0))
//二维数组按照一维数组的第二个元素排序
arr.sort((a,b) => a[1] - b[1]);
//返回二维数组中所有一维数组里的第一个元素
return Array.from(arr , inner => inner[0]);
ArrayList<Integer> arr = new ArrayList<>();
List<List<Integer>> list = new ArrayList<List<Integer>>();
//创建ArrayList
int num = Integer.MIN_VALUE;
//表示整数里的负无穷
arr.add() //添加元素
arr.size() //ArrayList长度
Collections.sort(arr) //数组从小到大排序
arr.get(i)
int 转 double : num (类型为int) +0.0
//深拷贝
new ArrayList<>(arr);
二维数组 :
int[][] arr = new int[3][3];
//将数组中的元素都初始化为 n
Arrays.fill(arr , n);
列表
List<Integer> ans = new LinkedList<>();
ans.add();
字符串 :
StringBuider str = new StringBuilder();//字符串构造
str.append();
str.toString().trim();
str.charAt(i) //索引为i的元素
str.deleteCharAt(i)//删掉索引为 i 的字符
str.length();//字符串长度
str.isEmpty();//检验字符串是否为空
str.equals("abc");
//单个字符 ==
Integer.parseInt("1");
队列:
LinkedList 主要是通过 offer() , poll() 等方法添加 , 删除 , 访问队列元素
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(); //添加到末尾
queue.isEmpty();
queue.poll()//删掉并返回第一个
queue.peek()//返回第一个
单调栈 :
Deque<Integer> stack = new ArrayDeque<Integer>();
哈希表 :
Map<K,V> idx = new LinkedHashMap<>();
//有储存顺序
idx.isEmpty()
idx.containsKey()
idx.put()
idx.get(key)
for(K key : idx.KeySet()){}
//使用for循环遍历idx的key;
//遍历 hashmap 中的 key and value
for(let [key , value] of idx){}