前言
总结一些常见的算法题目,每一个题目写一行思路,方便大家复习。具体题目的来源是下面的网站。
文章目录
- 前言
- 二叉树非递归遍历
- 牛客
-
- [JZ31 栈的压入、弹出序列 (8/4)](#JZ31 栈的压入、弹出序列 (8/4))
- [JZ4 二维数组中的查找](#JZ4 二维数组中的查找)
- [JZ11 旋转数组中的最小数字](#JZ11 旋转数组中的最小数字)
- JZ44数字序列中某一位的数字
- [JZ42 连续子数组的最大和](#JZ42 连续子数组的最大和)
- [leetcode 100题思路整理](#leetcode 100题思路整理)
- [leetcode 150题](#leetcode 150题)
- 牛客
二叉树非递归遍历
前序遍历方法一
- 直接右边放入栈,然后左边放入栈。
java
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer>ans = new ArrayList<>();
if (root == null) return ans;
Stack<TreeNode>st = new Stack<>();
st.add(root);
while (!st.empty()) {
TreeNode node = st.pop();
ans.add(node.val);
if (node.right != null) st.add(node.right);
if (node.left != null) st.add(node.left);
}
return ans;
}
java
// 方法二
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer>ans = new ArrayList<>();
Stack<TreeNode>st = new Stack<>();
TreeNode node = root;
while (node != null || !st.empty()) {
while (node != null) {
ans.add(node.val);
st.add(node);
node = node.left;
}
node = st.pop();
node = node.right;
}
return ans;
中序遍历
- 首先把root放进去
- 尽可能往左走,并且入栈
- 出栈,统计结果,往右走
java
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer>ans = new ArrayList<>();
Stack<TreeNode>st = new Stack<>();
while (root != null || !st.empty()) {
while (root != null) {
st.add(root);
root = root.left;
}
root = st.pop();
ans.add(root.val);
root = root.right;
}
return ans;
}
后续遍历
- 增加pre ,防止再重新入栈
- 首先把root放进去
- 尽可能往左走,并且入栈
- 出栈,如果右边没有了或者右边已经遍历过了 :输出,更新pre,root置空
- 否则,右边入栈。
java
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer>ans = new ArrayList<>();
Stack<TreeNode>st = new Stack<>();
TreeNode pre = null;
while (root != null || !st.empty()) {
while (root != null) {
st.add(root);
root = root.left;
}
root = st.pop();
if (root.right == null || root.right == pre) {
ans.add(root.val);
pre = root;
root = null;
} else {
st.add(root);
root = root.right;
}
}
return ans;
}
}
牛客
JZ31 栈的压入、弹出序列 (8/4)
【2,1,0】【1,2,0】是true,但是如果使用if进行出栈,就会成false。
每次只要栈不空,并且栈顶和当前的pop一致,就应该直接出栈。
- 先出栈
- 后入栈
- 最后除了循环再判断
JZ4 二维数组中的查找
- 左上角开始
JZ11 旋转数组中的最小数字
- 如果相等,最小的一定可以是m,所以r一定可以进行缩小。
- 需要和r进行比较
这个可以由以下三种情况总结出来
- 如果逆序:前面的都小于,所以l一直加
- 如果顺序: 没有大于nums[n - 1]的,所以r一直减。
- 如果前顺,后顺
前面的最小值,大于nums[n - 1]。所以当大于,l加,当小于r减。
综合上面三种情况,可以使用二分查找。
- 当大于的时候l加
- 当小于的时候l减。
然后等于的时候
- 如果是r, m在[l, r - 1]中
- 如果不是r, 那么r就可以直接向前移动一个。因为r排除了。
JZ44数字序列中某一位的数字
- 首先判断是几位数字
- 然后计算是第几个数字 (n - 1) /digit
- 最后计算第几位 (n - 1) % digit
JZ42 连续子数组的最大和
- ans = INT_MIN
- s = 0, minv = 0;// 这两个是对应给sun[0]的值。
- 先更新ans,再更新minv
leetcode 100题思路整理
前10题
-
两数之和:
-
使用hash,需要保证两者下标不相同。使用排序处理唯一的就很简单
-
如果需要寻找多个,那么hash还是会简单。边存,边处理。
-
排序寻找多个的情况下,相等一定出现在中间,但是不一定就一组。所以while之后,不能break,而是i++, j--继续寻找下一组相等的。
-
-
两数相加:链表中创建新结点,需要使用p,并且p需要每次移动。如果创建新结点就不用了
-
无重复字符的最长子串:双指针,java使用int[]数组更快
-
最长回文子串
-
正则表达式匹配:*的时候,小优化,类似于完全背包问题的优化,可以考虑直接使用dfs进行求解。只有当j不变的时候,匹配多个。
- j - 1, i - 1表示当前字符
- i= 0的时候s是空串
-
盛水最多的容器:经典双指针。
-
三数之和。排序、保证数字相等的时候continue就行了。
10-19题
- 电话号码的字母组合:简单dfs,使用string保存映射
- 删除链表倒数第N个结点。从nhead向前走N + 1个,然后删除下一个结点
- 有效的阔号组合:简单stack应用
- 合并两个有序链表,p,并且p不断移动。就算最后 = null的时候也需要移动
- 括号生成:简单dfs。使用curl或者sum进行flag标志
- 合并k个升序列表:Priority_Queue的排序函数重写。如果放入队列的为空指针,会报错。
- 下一个排列: Arrays.sort(nums, j + 1, n) , 第一个比这个数字大的数字
- 找到第一个可以增大的位数
- 找到大于他的最小的数字
- 从它后一位往后进行排序,这样就是最小的了。
- 数字序列中某一位数字:第k位开始数字start, 第k位数字个数sum。
n / k
,n % k
就是对应的数字和对应的位数 - 最长有效阔号:不会空间优化。stack +dp进行解决。stack中存储下标。f:表示以i结尾的最大的值。 ans = max(f[i])
- 搜索旋转排序数组:直接和nums[0]进行比较,小于一定是右边的。
20-29题
- 排序数组中查找元素的第一个和最后一个位置-lower_bound,upper_bound
- 组合总数-dfs , 从本层的最后一个数字开始,也就是从i开始
- 接雨水:ans += 左右最大的最小的-cur
- 全排列-简单dfs
- 旋转图像-矩形,所以旋转是(n / 2), (n + 1) / 2就行了。另外对应关系是行变列,列变行。然后数量关系是n - j - 1,另一个是i。或者两另一个是n - i - 1, 另一个是j。试一下就行了。
- 字母异位词分组:使用cnt进行计数,使用toString()转换成字符串
- 最大子数组和:求前缀和的最小值。更新的时候sum、ans, minv依次更新就行了
- 跳跃游戏:空间优化cur,就行了
- 合并区间:当前区间的第二维,应该是ans.get[ans.size() - 1]第二维和当前区间第二维的最大值。
- 不同路径:初始化f(0, 0) = 1, 然后让f(i, j) += f(i - 1, j) + f(i, j - 1);
30-39题
- 最小路径和:摘樱桃
- 爬楼梯:斐波那契
- 编辑距离:直接dfs做就行了。参照正则表达式匹配
- 颜色分类:快慢双指针。i永远指向第一个不为0的,j找到下一个为0的,与他交换
- 最小覆盖子串:滑动窗口,满足贪心的双指针,如果窗口中没有,就没有必要继续了。
- 子集:二进制枚举
- 单词搜索:
- 柱状图中最大矩形:单调栈,找左右小于它的第一个坐标,然后就可以求宽度了。
- 最大矩形:前缀和 + 单调栈
- 二叉树的中序遍历:简单
40-50 题
- 不同二叉搜索树
leetcode 150题
数组/字符串
- 合并两个有序数组
- 删除有序数组的重复项II。(nums[i] != nums[j - 1] || nums[i] != nums[j - 2])swap(nums[j++], nums[i])
- 轮转数组(整体轮转,[0, k), [k, n))
- O(1) 时间插入、删除和获取随机元素: map保存下标,删除最后一个元素
- 左右文本对齐
双指针/滑动窗口
- 子串。子数组。
- 最小覆盖子串
- h指数
- 串联所有单词的子串
矩阵
- 矩阵置0,但标志法,逆序处理每一行。最后一行可以直接进行处理。
- 矩阵旋转:上下,然后主对角线。注意i,j的取值范围。
- 螺旋矩阵:最简的办法,就是直接改变当前的矩阵。但是可能会给别的函数造成问题。+ 101,然后减去101就行了。
- 生命游戏:当前矩阵进行编码,进行转换
- 数读游戏:可以三个矩阵 + hash运算进行判断。然后可以使用Space数组保存空格,然后把空格进行填充,使用bool dfs就行了。
30-40题
牛客
动态规划
- 跳台阶扩展:最后一步可以跳1,2...i- 1。不能最后一步不跳,所以是1~n,最少跳一步。
- 矩形覆盖:类似兔子月。f[0] = 0, f[1] = 1, f[2] = 2; 注意n <= 1的情况。让f开大一点
- 礼物最大价值:直接简单dp。
- 把数字翻译成字符串:特殊情况"10", "100"。
- 如果当前为0,不能加上f[i - 1]
- 如果前面为0,或者组成数字大于26,不能加上f[i - 2]
回溯
-
矩阵的路径:
- 字符相同的时候才能继续向下寻找。
- 回溯,需要让vis变成0。
-
JZ13 机器人的运动范围
- 思路出错,应该是直接从(0, 0)dfs,结果想成了遍历整个矩阵了。
- 正确的思路应该是从(0, 0)开始dfs或者bfs。