小白刷LeetCode
- 零、JAVA
- 二、热题100
- 1.两数之和
- 2.异位词分组
- 3.最长连续序列
- 移动零(维持顺序,零放后)
- 盛最多水的容器
- 三数之和
- 无重复字符的最长子串
- 找到字符串中所有字母异位词
- 和为 K 的子数组
- 最大子数组和
- 合并区间
- 反转链表
- 回文链表
- 环形链表
- 环形链表Ⅱ(找环的起始节点)
- 合并两个有序链表
- 两数相加
- 删除链表的倒数第 N 个结点
- 两两交换链表中的节点
- K 个一组翻转链表
- 随机链表的复制
- 有效的括号
- 最小栈
- 字符串解码 2[a3[c]]
- 每日温度
- DFS
- BFS
- 二叉树的中序遍历
- 二叉树的最大深度
- 翻转二叉树
- 对称二叉树
- 二叉树的直径
- 将有序数组转换为二叉搜索树
- 验证二叉搜索树
- 二叉树展开为(只有右孩子的)链表
- 从前序与中序遍历序列构造二叉树
- 路径总和 III(父子和为某值的条数)
- 二叉树的最近公共祖先
- 买卖股票的最佳时机
- 跳跃游戏(能否到)
- 跳跃游戏 II(最小跳数)
- 划分字母区间
- 搜索插入位置(二分)
- 搜索二维矩阵
- 在排序数组中查找元素的第一个和最后一个位置
- 搜索旋转排序数组
- 爬楼梯
- 杨辉三角
- 打家劫舍(隔一个抢)
- 零钱兑换
- 完全平方数
- 单词拆分
- 乘积最大子数组
- 分割等和子集
- 矩阵置零
- 螺旋矩阵
- 顺时针旋转图像
- 搜索二维矩阵 II
- 腐烂的橘子
- 三、面试经典150
零、JAVA
1.数组
int[] num = new int [2];
int[] num = {1, 2};
# 可变int[][]:
List<int[]> numL = new ArrayList<int[]>();
int[][] numTrue = numL.toArray(new int[numL.size()][]);
# 不可变int[][]:
int[][] num = new int[2][2];
int[][] num = {{1,2}, {2,3}};
return new int[]{i, j};
return new int[0];
int[] newnum = Arrays.copyOfRange(arr, lid, rid); #[l, r)
System.arraycopy(old, ol, new, nl, len)# 从原数组的第ol个开始,拷贝到新数组的nl个开始,共len个。
# 变长数组
List<Integer> intList = new ArrayList<Integer>();
List<List<Integer>> intList = new ArrayList<List<Integer>>();
List<int[]> intList = new ArrayList<>();
int length = intList.size();
intList.add(val);
intList.remove(idx);
intList.get(idx);
intList.set(idx, val);
-
string->char[]:
char cArr[] = str.toCharArray(); 或 char c = str.charAt(i);
string->int:Integer.parseInt(str);
–
char[]->string:String str = new String(cArr);
StringBuffer->string:String str = sb.toString();
list->string:String str = String.join("", list);
*->string:String str = String.valueOf(t);
–
string->string[]:String strArr[]= str.split("\\s+");
(多空格)
string[]->List:List<String> strL= Arrays.asList(strArr);
List->string[]:string[] strArr=strL.toArray();
-
char[]排升序:
Arrays.sort(cArr);
string[]排升序:List<String> strL = Arrays.asList(strArr); Collections.sort(strL);
int[]排升序:Arrays.sort(intArr);
重写排序,如int[][] intArrs = [[0,1], [-1, 2]]要求以第一位升序排列:Arrays.sort(intArrs, new Comparator<int[]>(){ public int compare(int[] intArr1, int[] intArr2){ return intArr1[0] - intArr2[0]; } });
-
List直接可转hash:Set< String> set = new HashSet(list);
2. 字符串
str.length();
str.charAt(idx); str[i];
str.substring(lidx, ridx); #[lidx, ridx);
str.substr(lidx, length);
str = str.trim(); # 去掉开头结尾的空格
str1.equals(str2);
return new StringBuilder(str).reverse().toString();# 逆转字符串
# 可变字符串
StringBuffer sb = new StringBuffer();
sb.append(str);
sb.delete(lidx, ridx);
sb.toString();
String s;
s += "haha";
# string[]和collection的关系
List<String> list = Arrays.asList(strArr);
int len = list.size();
list.add(str);
list.remove(idx);
list.get(idx);
Collections.reverse(list);# 翻转字符串数组的顺序
Collections.sort(list); # 升序
String newStr = String.join(" ", list); # 拼接成新字符串
# String->StringBuffer
# String[]->Collection
2.Map
Map<Integer, Integer> hashmap = new HashMap<Integer, Integer>();
if (hashmap.containsKey(k)) {}
for(Map.entry<Integer, Integer> entry : map.entrySet()){int key = entry.getKey(); int value = entry.getValue();}
hashmap.put(k, v);
hashmap.get(k);
hashmap.remove(k);
hashmap.values();
String str = hashmap.getOrDefault(k, "");#只返回不放入
hashmap.computIfAbsent(k, key->10);#不存在会放入表中
Set<Integer> set = new HashSet<Integer> (); # 无重复数
for (int num : nums){set.add(num);}
set.remove(num);
if (set.contains(num)){}
3.栈 队列
# 用列表模拟栈
List<Character> stack = new ArrayList<Character>();
stack.add(c);
stack.remove(idx);
stack.get(idx);
top = stack.size();
# Stack
Stack<String> s = new Stack<String>();
Deque<TreeNode> s = new LinkedList<TreeNode>();
s.push(str);
while(!s.empty()){s.pop();}
s.peek();#取栈顶
# Queue
Queue<TreeNode> q = new LinkedList<TreeNode>();
q.add(n);
TreeNode n = q.remove();
TreeNode n = q.peek();
size = q.size();
3.指针
if(head==null || head.next==null{return head;}
LineNode t = null;# 空指针
while(fast!=null && fast.next!=null){slow=slow.next;fast=fast.next.next;}# 快慢指针,f=s.n
4.for循环
for(int i=0; i<n; i++){}
for(int i : integers){} // int[] integers = {1, 2, 3}
for(char c : str.toCharArray()){}
for(Object str : list){} // String[] strArr = {"a", "b"}; List<String> list = Arrays.toList(strArr);
5. 七七八八
10^9用long
10^4可用o(n^2)时间复杂度
mid = left + (right-left)/2;(左倾)
Integer.MAX_VALUE
Math.max(a, b)
Math.pow(a, b)# a的b次方
gcd(a, b)# ab的最大公约数
Random random = new Random();
int r = random.nextInt(10);# 0-10随机取
Character.isDigit(c);# 是否数字
Character.isLetter(c);# 是否是字母
Character.isLetterOrDigit(c);
int num = Integer.parseInt(str);# 字符串转数字
str.length(); intArr.length; intList.size();
str.substring(lidx, ridx); #[lidx, ridx)
str.toLowerCase();
list.add(num); list.get(i);
贝祖定理:要满足3x+4y=5,则5能整除3和4的最大公约数。
分治、动态规划、回溯、贪心一锅炖:https://zhuanlan.zhihu.com/p/148157101
6. ACM输入输出
import java.util.*;
public class Main {
// 链表结构
static class LinkNode{
int val;
LinkNode next;
public LinkNode(int val){
this.val = val;
}
}
// 树结构
static class TreeNode{
int val;
TreeNode left;
TreeNode right;
public TreeNode(){}
public TreeNode(int val){
this.val = val;
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 输入: 1 2 3
while(in.hasNextInt()){ int a = in.nextInt;}
// 输入: a bb c
while(in.hasNextLine()){
String str = in.nextLine();
String[] sArray = str.split(" ");
}
// 杂七杂八
int a = Integer.parseInt(ch);
String sNew = String.join(" ", sArray);
// 初始化链表
for(int i=0; i<len; i++){
p.next = new LinkNode(ints[i]);
p = p.next;
}
// 初始化树
}
}
7. 分治
时间复杂度:
若f(n) = O(n^d):
如:
规模变1/2 | |
---|---|
分解为一个问题 | 1+clogn = O(logn) |
分解为两个问题 | n+nlogn = O(nlogn) |
空间复杂度:层数(logn,n为数组内的元素个数)*每层用的空间
二、热题100
1.两数之和
哈希表,先存,再查。O(n)
2.异位词分组
每个单词排序,再查看哈希表里有没有一样的,存到表里。
3.最长连续序列
hashset无重复存储,依次找最小底数(无num-1),对最小底数,依次找增加的数(num++),记录最长的连续长度。
移动零(维持顺序,零放后)
一个指针遍历,另一个指针维护非零。
盛最多水的容器
双指针,短边内移,更新最大容量。
三数之和
关键在于跳过重复组合。
先排序,指针1遍历(如果下一个和自己一样,跳过),指针2和3双指针向中间(如果分别和自己一样,分别跳过)。
无重复字符的最长子串
滑动窗口。右指针一直向右遍历,对每个字母判断,若不在哈希表则存位置,若在哈希表则更新左指针位置为最靠前和更新哈希表位置。
找到字符串中所有字母异位词
String排序:
char[] subChars = sub.toCharArray();
Arrays.sort(subChars);
String ssub = new String(subChars);
和为 K 的子数组
①指针1向右遍历,指针2从1的位置开始向右遍历,计算中间的和。
②哈希表存<前缀和,个数>,当前前缀和-(当前前缀和-target)=target满足则为括号内的个数。
最大子数组和
①动态规划。遍历,是否选用=是否加上前面的更好。
②分治。最大的可能在左边、右边、跨过中间,左右边递归,跨中间算。
合并区间
重写排序,写入、判断左边、改右边。## 相交链表
①A哈希存,B找最先相同的。
②双分支指针,循环并交错转移(a+c+b和b+c+a),直到相遇。
反转链表
①前中后指针。
②递归,从后面往前归,每次归的时候要先反连 再断开原来的正连 再指向空。
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {return head;}
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
回文链表
①存字符串,字符串两端匹配。
②快慢指针+栈。
③快慢指针+反转后半部分。
环形链表
快慢指针。
环形链表Ⅱ(找环的起始节点)
①哈希表。
②先让快慢指针相遇,再让快指针指到开始,重新与慢指针相遇(s=nb和k=a+nb)。
合并两个有序链表
①双分支指针。
②递归,
两数相加
单数对应相加,记录进位。
删除链表的倒数第 N 个结点
①前后指针,相差N。
②栈,popN个。
两两交换链表中的节点
递归,3个一组,前两个交换。
K 个一组翻转链表
dummy,start,end,next,end是start的后k位。
随机链表的复制
哈希表存同位置的新和旧节点。
有效的括号
左括号=存,否则判断空/右括号。
最小栈
两个栈,另一个存当前最小值。
字符串解码 2[a3[c]]
非]则先取字母再取数字,最后新整合的字母压栈。
每日温度
单调递减(温度)栈存下标,出栈更新ans。
DFS
Deque<TreeNode> stack = new LinkedList<TreeNode>();
while(root!=null || !stack.isEmpty()){
while(root!=null){
stack.push(root);
# print(root)是前序遍历
root = root.left;
}
root = stack.pop();
# print(root)是中序遍历
root = root.right;
}
BFS
if(root == null){return null;}
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.add(root);
while(!queue.isEmpty()){
int size = queue.size();
while(size > 0){
TreeNode p = queue.remove();
if(p.left!=null){queue.add(p.left);}
if(p.right!=null){queue.add(p.right);}
size--;
}
}
二叉树的中序遍历
①inorder(treenode, list),函数inorder直接返回void而非list。
②以root为基础的深搜。while(root!=null || !stack.isEmpty()){while push left; add; right;}
二叉树的最大深度
①递归。
②广搜。每行一清。
翻转二叉树
①左=递归左,右=递归右,左右互换返回根。
②广搜。出队时换其左右。
对称二叉树
①递归(左右)。
②核心思想是左左右右相同(从队列依次提取的两个要一样)。
二叉树的直径
左高+右高,全局ans。
将有序数组转换为二叉搜索树
root为数组中间值,左右子树递归。
验证二叉搜索树
是二叉搜索树 = 中序遍历为递增。
二叉树展开为(只有右孩子的)链表
①root的右子树往左下移,root的左子树往右移,root=root.right。
②右左中。
从前序与中序遍历序列构造二叉树
根据中,分为左右数组,递归。
路径总和 III(父子和为某值的条数)
①外递归:每个节点;内递归:该节点为根时得到的条数。
②前缀和。
二叉树的最近公共祖先
是最近公共祖先 = 传上来的left和right都找到了(非空)。
买卖股票的最佳时机
前i天的最低价格,前i天的最低利润。
跳跃游戏(能否到)
在每个能到达的位置,记录当前位置的最大能力(maxR = maxR || i+len(i))。
跳跃游戏 II(最小跳数)
一开始最远能跳到哪,第二次在该范围内最远能跳到哪。在最远的地方step++,回退范围不用管step。
划分字母区间
start, max(endc, end)。
搜索插入位置(二分)
找第一个大于等于target的位置即可。
搜索二维矩阵
①行遍历,每行二分。
②从左下或右上开始找,BTS。
③矩阵转数组,matrix[pos/row][pos%col]。
在排序数组中查找元素的第一个和最后一个位置
两次二分,第一次找到target后记录first并让right=mid-1,第二次找到target后记录last并让left=mid+1。
搜索旋转排序数组
二分必有一部分有序另一部分无序,判断有序=端点有序,判断target在哪个部分然后将左右指针指过去。
动态规划:
爬楼梯
ans[i]=ans[i-1]+ans[i-2]
杨辉三角
List<List< Integer>>
打家劫舍(隔一个抢)
钱[i] = 抢前i-1个 或 抢前i-2个和当前的。
零钱兑换
i的硬币数c[i] = 1 + min{c[i - j]},如果不能组成则一定是个数大于总金额的值。
完全平方数
动态规划。如零钱兑换。
单词拆分
动态规划。如果中前为正确,且中后存在,则当前正确。
乘积最大子数组
动态规划。因为是乘法,所以仅维护dpmax不够,还要dpmin,三者同求min max。
分割等和子集
动态规划。等价于是否有数加起来为和的一半。
首先排除:奇数个、和为奇数、最大值大于和的一半。
然后背包:new dp[len][sum/2+1]、dp[i][0]=T、dp[i][num[0]]=T、如果当前numi比j大必不取numi,此时dp[i][j]=dp[i-1][j]、如果当前numi比小则取决于dp[i-1][j]和dp[i-1][j-num[i]]。
矩阵置零
用第一行第一列作标记,因为他们会被覆盖,所以先判断他们的。
螺旋矩阵
方向数组。
顺时针旋转图像
原矩阵行->新矩阵列。
搜索二维矩阵 II
z字形往左下判断。
腐烂的橘子
bfs求最短路径。
三、面试经典150
1. 合并两个有序数组
①先合并再Arrays.sort(arr);②从后往前三指针。
2. 移除元素
①两个左指针,一个找需要前移保留的的元素,一个找可以覆盖的移除元素。②两个双向指针,左边的找需要移除的,右边作为候选替代移除元素。
3. 删除有序数组里的重复项Ⅰ
两个左指针
4. 删除有序数组里的重复项Ⅱ
两个左指针,判断[front-2]?=[tail]
5. 多数元素
①哈希表存次数。
②排序后的n/2位置。
③分治:出、分、算。
6. 轮转数组
①newPos = (ind + k) % length。
②翻转3次:总、左、右。
7. 买卖股票的最佳时机Ⅰ
动态规划
8. 买卖股票的最佳时机Ⅱ
收集所有上坡差
9. 跳跃游戏Ⅰ(能否到达)
贪心,维护最大右边界,先判断是否在界内再判断其是否大于总长。
10. 跳跃游戏Ⅱ(几步到达)
贪心,维护右结束,每到达一次右结束,则用最大右边界更新右结束并步数+1。
11. H指数
排序后,被引用指数>=有多少文献。
12. O(1) 时间插入、删除和获取随机元素
可变数组、哈希表。
13. 除自身以外数组的乘积
前缀和,左右乘积列表。
14. 加油站
找到最后一个差为负的位置,若最终能走完全程,则其下一个位置为所求。
15. 分发糖果
从左数,从右数,求最大。
16. 接雨水
①动态规划,从左数,从右数,求差。
②左右指针:左/右指针都有左&右最大,但只有左指针的左最大
和右指针的右最大
是因遍历而确定的,但通过推理可知,左指针的右最大
一定比右指针的右最大
还大。所以只要左指针列的左最大
大于右指针列的右最大
,就可以说明右指针列可以积攒雨水。 同样,只要右指针列的右最大大于左指针列的左最大,就可以说明左指针列可以积攒雨水。
17. 罗马数字转整数
哈希表存取。从后往前看,若当前值小于后面的值则减去当前值,否则加上当前值。
18. 整数转罗马数字
13种类型,从大到小排,依次遍历,整数减去一个小于该整数的最大类型,StringBuffer存新增罗马。
19. 最后一个单词的长度
①str.split(" ");
②从后往前找单词。
20. 最长公共前缀
①横向遍历,维护一个前缀,每次将字符串和这个前缀对比。
②纵向遍历,依次比较所有单词的同一个位置字符是否相同,不相同或到达某单词末尾则返回前一个位置。
③分治,出、分、算。
21. 反转字符串里的单词
①s.trim(); s.split(“\s+”); Arrays.asList(sArr); Collections.reverse(sList); String.join(" ", sList);。
②从后往前,两个指针,StringBuffer存。
22. N字形变换
List<StringBuffer>
,立flag,row += flag。
28. 找出字符串中第一个匹配项的下标
KMP。
判断子序列
匹配成功两指针同时右移,否则只长指针移。