leetcode刷题总结
1.合并两个有序链表LeetCode 21题
题解:每次递归进来比较l1和l2两个链表当前值的大小,下一个结点永远连接比较小的值,结束条件是l1和l2有一个为null,这题需要理解并能默写出来。
public ListNode mergeTwoLists1List(ListNode l1, ListNode l2) {
if(l1==null){
return l2;
}
if(l2==null){
return l1;
}
if(l1.val<l2.val){
l1.next=mergeTwoLists1List(l1.next,l2);
return l1;
}else{
l2.next=mergeTwoLists1List(l1,l2.next);
return l2;
}
}
2.盛水最多的容器 LeetCode 11题
题解:循环从两头往中间走,i++和j--到底是该i++还是j--比较好理解的是,如果a[i]和
a[j]值做比较,因为题解是需要装最多,所有肯定移动a[i]和a[j]比较小的值往中间走,因为我也不确定往中间走会不会遇到比当前值大的所以需要每次用max记录最大的值就是最多的水,当找到最中间,max就是最大值直接返回。需要很熟悉的写出来。
public static int maxArea(int a[]) {
int max = 0;
for (int i = 0, j = a.length - 1; i < j; ) {
int moveMinBar = a[i] < a[j] ? a[i++] : a[j--];
max = Math.max(max, (j - i + 1) * moveMinBar);
}
return max;
3.移动零 LeetCode 283题
题解:第一遍循环先把nums[i]值不是0的装到一个集合里面,题目要求需要在原数组上操作,因为原数组最坏的可能是一个0都没有,所以完全满足题意,把不等于0的值按秩序依次放到nums[j]中,放完j++,也可以写成nums[j++]。第一遍循环完,原数组的长度减去重新放值的长度i-j就是有多少个0,再从j到i循环在最后面补充0。需要第一遍知道这种解法下次要能独立写出来。
public static void moveZeroes(int[] nums) {
//第一遍只要是非0的统统都赋给nums[j]
int j=0;
for (int num : nums) {
if(num!=0){
nums[j]=num;
j++;
}
}
//替换后面的末尾的元素都赋为0即可从i-j是多少就需要在结尾补充多少个0
for(int i=j;i<nums.length;i++){
nums[i]=0;
}
}
4.两数之和 LeetCode 1题
题解:暴力方法双层循环相加等于目标值,本例子用了HashMap把当前nums[i]当做key把index下标当做value存到map中去,在存的过程中判断目标值减去当前循环的值,是否已经在map中存在,如果存在那么就是map中的这个值加上当前循环的值等于目标值target,满足题意返回两个下标,map存的下标需要map.get(key),key就等于target - nums[i]目标值减去当前循环的值,当前的下标就是i,返回即可。理解并能想到用HashMap这种方法,HashMap在很多题解中超好用。
public static int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(target - nums[i])) {
return new int[]{map.get(target - nums[i]), i};
}
map.put(nums[i], i);
}
return nums;
5.三数之和 LeetCode 15题
题解:
1.先对排序再循环,因为排序完当循环到nums[i]>0的时候,后面的无须循环因为最左侧是正数,三个正数的和不可能是0,因为三数之和是nums[i]+nums[i+1]+nums[nums.length - 1]即nums[i]大于0 三数之和不可能为0即退出整个循环break
2.循环整个数组,i > 0 && nums[i - 1] == nums[i]是为了判断相同的值,发现当前值和前一个值相等直接continue
3.然后while (left < right)循环就是定义左右两个结点加上自己循环的nums[i],我们定死nums[i]让left=nums[i+1]和right=nums[nums.length - 1]往中间走。
4.大的判断,当三数之和sum==0返回,当三数之和小于0因为是有序的,肯定是左边的不够大需要left++,反之三数之和大于0就是right太大需要right--
5.再想sum==0的时候因为题意要求不止一组满足,当sum等于0的时候需要继续往中间走继续找满足的条件,很容易想到直接让left++;和right--再往中间走的时候左右两边遇见重复的直接各自left++和right--
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> array = new ArrayList<>();
if (nums == null || nums.length < 3) {
return array;
}
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
break;
}
if (i > 0 && nums[i - 1] == nums[i]) {
continue;
}
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
int temp = nums[left] + nums[right] + nums[i];
if (temp == 0) {
array.add(Arrays.asList(nums[i], nums[left], nums[right]));
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
left++;
right--;
} else if (temp < 0) {
left++;
} else if (temp > 0) {
right--;
}
}
}
return array;
6.爬楼梯 LeetCode 70题
题解:从第一层往上人肉想很难想通,如果你倒着想,你在第n阶台阶只能从第n-1阶台阶上来,或者从n-2阶台阶上来,那么有f(n)=f(n-1)+f(n-2)db方程就是db[i] = db[i - 1] + db[i - 2]。这是自顶向下,如果自底向上就是,除过第1个值和第2个值,每个值都依赖前两个值相加,那么申请三个临时变量,一直往上递推,变三个变量的值,循环完返回最后一个值。
public int climbStairs(int n) {
//1 返回1
if (n == 1) {
return 1;
}
//定义第一步
int first = 1;
//定义第二部
int second = 2;
//三步以上每前一步替换后一步,递推上去,自底向上
for (int i = 3; i <= n; i++) {
int third = first + second;
first = second;
second = third;
}
return second;
7.反转链表 LeetCode 206题
题解:见注释
public ListNode reverseList(ListNode head) {
//申请 三个变量交换
ListNode pre=null;
ListNode cur=head;
ListNode tem=null;
//当当前节点不为空时
while(cur!=null){
//临时变量是当前节点的下一个
tem=cur.next;
//当前节点指回去上一个节点
cur.next=pre;
//pre和cur节点都前进一位
//当前节点变成前一个节点
pre=cur;
//临时节点就是当前节点
cur=tem;
}
return pre;
}
8.有效的括号 LeetCode 20题
题解:我在不知道这种解法之前对这个题无从下抓,当我知道这种解法的时候,只一次我每次都能正确的写出这个题解,很好理解。
1.初始化一个栈,循环整个字符,当发现是 ( 和 [ 和 { 依次往栈里面对应的另一半也就是)和 ] 和 } 除过这三种情况判断栈是否空了,如果是空那么就是我有右括号,你栈里面没有对应的左括号,如果不为空让栈顶元素出栈,和当前元素比较,如果不相等那么就是当前这一对括号不匹配,这两种情况都返回false,反之栈不为空,栈顶元素和当前循环的值相等说明匹配上了,继续用相同的方法匹配下一个值。整个循环完之后如果栈是空的说明正好全部匹配完返回true,如果栈不是空说明有多余的左括号返回false。
public boolean isValid(String s) {
Stack<Character> stack = new Stack<Character>();
for (char c : s.toCharArray()) {
if (c == '(')
stack.push(')');
else if (c == '{')
stack.push('}');
else if (c == '[')
stack.push(']');
else if (stack.isEmpty() || stack.pop() != c)
return false;
}
return stack.isEmpty();
}
9.接雨水 LeetCode 42题
题解:见注释
public int trap(int[] height) {
//左边index
int left = 0;
//右边index
int right = height.length - 1;
//返回的最大值 初始值0
int max = 0;
//最大左最高 初始值0
int leftmax = 0;
//最大右最该 初始值0
int rightmax = 0;
//a<=b很像二分法的
//两边往中间走走的过程中更新左边的leftmax和更新右边的rightmax
while (left <= right) {
//更新从0到left的最大leftmax
leftmax = Math.max(leftmax, height[left]);
//更新right到end(结尾)的最大值rightmax
rightmax = Math.max(rightmax, height[right]);
//说明左边较小小 max+=(左边的最大值-左边的高度height[left]) 最多装的水leftmax说了算
if (leftmax < rightmax) {
max += (leftmax - height[left]);
left++;
//说明右边较小 max+=(右边的最大值-右边的高度height[right])
} else {
max += (rightmax - height[right]);
right--;
}
}
return max;
}
10.字母异或词 LeetCode 242题
题解:最大的字母是26个字母的不会超过长度256,定义数组counter,循环字符s因为s的长度和t的长度一定相等才走循环,挨个循环每个字母,让s的这个字母数字++让t的这个字母的数字--。如果两个单词是异或词就是,单词顺序不一致但是个数一样,每个单词出现的次数也是一样,一遍循环完++和--抵消了数组counter必定为0返回true。有不等于0的返回fase
public static boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
int[] counter = new int[256];
for (int i = 0; i < s.length(); i++) {
counter[s.charAt(i)]++;
counter[t.charAt(i)]--;
}
for (int count : counter) {
if (count != 0) {
return false;
}
}
return true;
11.字母异位词分组 LeetCode 49题
题解:写各个题的时候我准备自己用hashMap自己写一遍,核心思想就是循环每个值排序,因为如果是字母异或词排序完再转换成string值一定是一样的就是代码中的key是一致的。我拿一个map存,map的key是排序后的值,value是一组相同异或词的集合,当我发现map存在排序后的key字符串往这个list里面添加当前值,当map不存在直接存当前值初始化的List,在这个过程中顺便把返回值存到List<List<String>> ans = new ArrayList<>()这个数组中最后返回。
List<List<String>> ans = new ArrayList<>();
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String, List<String>> map = new HashMap();
for (int i = 0; i < strs.length; i++) {
char[] chars = strs[i].toCharArray();
Arrays.sort(chars);
String key = String.valueOf(chars);
if (map.containsKey(key)) {
List<String> temp = map.get(key);
temp.add(strs[i]);
} else {
List<String> temp = new ArrayList<>();
temp.add(strs[i]);
map.put(key, temp);
ans.add(temp);
}
}
return ans;
}
12.二叉树的前序遍历 LeetCode 144和94题
题解:这个东西记住递归模板就这样做就好,前序和中序list添加顺序不一样而已。
前序
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
if(root==null){
return ans;
}
dfs(root, ans);
return ans;
}
private void dfs(TreeNode root, List<Integer> ans) {
ans.add(root.val);
if(root.left != null) {
dfs(root.left, ans);
}
if (root.right != null) {
dfs(root.right, ans);
}
}
中序
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
if(root==null){
return ans;
}
dfs(root, ans);
return ans;
}
private void dfs(TreeNode root, List<Integer> ans) {
if(root.left != null) {
dfs(root.left, ans);
}
ans.add(root.val);
if (root.right != null) {
dfs(root.right, ans);
}
}
13.翻转二叉树 LeetCode 226题
题解:把左右节点看成很平常的两个值交换就好,你可以理解交换两个数,左右子树递归调用,最后返回,递归先左还是先右无所谓都行。
public TreeNode invertTree(TreeNode root) {
//递归函数的终止条件,节点为空时返回
if (root == null) {
return null;
}
//下面三句是将当前节点的左右子树交换
TreeNode tmp = root.right;
root.right = root.left;
root.left = tmp;
//递归交换当前节点的 左子树
invertTree(root.left);
//递归交换当前节点的 右子树
invertTree(root.right);
//函数返回时就表示当前这个节点,以及它的左右子树
//都已经交换完了
return root;
}
14.括号生成 LeetCode 22题
题解:一共有2n个括号,套用递归模板,当左括号和右括号等于n的时候,这层递归返回,注意左括号次数小于n拼接左括号,右括号需要小于左括号的次数才能拼接右括号。
List<String> ans = new ArrayList<>();
public List<String> generateParenthesis(int n) {
if (n == 0) {
return ans;
}
int left = 0;
int right = 0;
dfs("", left, right, n);
return ans;
}
public void dfs(String s, int left, int right, int n) {
if (left == n && right == n) {
ans.add(s);
return;
}
if (left < n) {
dfs(s + "(", left + 1, right, n);
}
if (right < left) {
dfs(s + ")", left, right + 1, n);
}
15.二叉树的最小深度 LeetCode 111题
说明:借leetcode王小二的解释
叶子节点定义左孩子和右孩子都为 null 时叫叶子节点
当 根结点root 节点左右孩子都为空时,返回 1
当根结点 root 节点左右孩子有一个为空时,返回不为空的孩子节点的深度,
当 根结点root 节点左右孩子都不为空时,返回左右孩子较小深度的节点值
题解:
递归:分别递归左子树,和右子树记下左子树和右子树的深度,如果左子树和右子树有一个是0结果是不为0的树的深度+1,都不为0取左子树和右子树最小的值+1。
bfs:广度优先,直接套广度优先的模板,拿个队列存TreeNode,广度一层一层下去,每一层深度depth+1,每一层判断左子树和右子树同时为null返回深度。
递归解法:
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
int left = minDepth(root.left);
int right = minDepth(root.right);
if (left == 0 || right == 0) {
return left + right + 1;
}
return Math.min(left, right) + 1;
}
bfs广度优先
public int minDepth1(TreeNode root) {
if (root == null) {
return 0;
}
//队列里面放树
Queue<TreeNode> q=new LinkedList<>();
q.offer(root);
int depth=1;
while(!q.isEmpty()){
int sz=q.size();
for(int i=0;i<sz;i++){
TreeNode cur=q.poll();
if(cur.left==null&&cur.right==null){
return depth;
}
if(cur.left!=null){
q.offer(cur.left);
}
if(cur.right!=null){
q.offer(cur.right);
}
}
depth++;
}
return depth;
16.Pow(x, n) LeetCode 50题
题解:举例算x的4次方可以邓毅x的2次方乘以x的2次方x^4=x^2*x^2那么x的5次方就是在前面的基础上再乘以x。故如果n次方n是偶数时递归的调用,每次是n=n/2。就是myPow(x * x, n / 2)如果是奇数x * myPow(x * x, n / 2)再乘以x本身就好。如果n<0的时候x=1/x。n=-n由于有溢出代码中n=-(n+1)。
public double myPow(double x, int n) {
if (n == 0)
return 1;
if (n < 0) {
return 1 / x * myPow(1 / x, -(n + 1));
}
return (n % 2 == 0) ? myPow(x * x, n / 2) : x * myPow(x * x, n / 2);
}
17.有序链表转换二叉搜索树 LeetCode 109题
题解:二叉搜索树的左子树小于跟节点,右子树大于跟节点,并且左边子树和右边子树所有的结点都满足这个规律,所以用快慢指针找中间的跟节点,初始化数让中间的节点为跟结点,依次分为左子树和右子树,为两个树,再找这个两个树的中点,依次递归找每次左子树的头从head开始到pre结束。右子树从slow.next开始到结束。最后返回树。
public TreeNode sortedListToBST(ListNode head) {
if (head == null) {
return null;
}
if (head.next == null) {
return new TreeNode(head.val);
}
//经典的快慢指针找中点
ListNode fast = head;
ListNode slow = head;
ListNode pre = null;
while (fast != null && fast.next != null) {
pre = slow;
slow = slow.next;
fast = fast.next.next;
}
//中间结尾
pre.next = null;
TreeNode node = new TreeNode(slow.val);
node.left = sortedListToBST(head);
node.right = sortedListToBST(slow.next);
return node;
}
18.递增子序列 LeetCode 491题
题解:这个和全排列类似,只不过注意添加的时候要判断当前值大于temp数组中最后一个值。先拿第一个值,递归下一层去拿第二个值,当发现满足下一个值大于等于当前值就往结果集中放,最少两个元素所以temp长度大于2的时候ans.add()
public List<List<Integer>> findSubsequences(int[] nums) {
Set<List<Integer>> ans = new HashSet<>();
List<Integer> temp = new ArrayList<>();
dfs(ans, temp, 0, nums);
return new ArrayList<>(ans);
}
private void dfs(Set<List<Integer>> ans, List<Integer> temp, int index, int[] nums) {
if (temp.size() >= 2) {
ans.add(new ArrayList<>(temp));
}
for (int i = index; i < nums.length; i++) {
if (temp.size() == 0 || temp.get(temp.size() - 1) <= nums[i]) {
temp.add(nums[i]);
dfs(ans, temp, i + 1, nums);
temp.remove(temp.size() - 1);
}
}
}
19.全排列 LeetCode 46题
题解:见注释
public List<List<Integer>> permute(int[] nums) {
//返回结果不需要每次传可以提出去
List<List<Integer>> res=new ArrayList<>();
//当前这一步已经选择的选项就是已经排列好的数组
List<Integer> temp=new ArrayList<>();
dfs(res,temp,nums);
return res;
}
private void dfs(List<List<Integer>> res, List<Integer> temp, int[] nums) {
//当排列的数组等于nums的长度的时候res.add一遍 返回上一步开始
if(temp.size()==nums.length){
res.add(new ArrayList<>(temp));
return ;
}
//循环n个值
for(int i=0;i<nums.length;i++){
//已经被选过 排列过,不选了
if(temp.contains(nums[i])){
continue;
}
//没排列过加到排列数组中来
temp.add(nums[i]);
//传到下一层去排列直到排完n个
dfs( res, temp, nums);
//每一层回来都需要删掉下去的这一个值,意思如果回到跟节点的话temp是空的
temp.remove(temp.size() - 1);
}
}
20.电话号码的字母组合 LeetCode 17题
题解:和上面的全排列一模一样的思路,全排列加回溯的思想,多了个数字字符串转字母的过程,终结条件是digits的长度和循环的深度一样就返回。其他和全排列思路一致。
LinkedList<String> ans = new LinkedList<>();
public List<String> letterCombinations(String digits) {
if (digits.isEmpty()) {
return ans;
}
String[] map = new String[]{"0", "1", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
dfs(digits, 0, new StringBuilder().append(" "), map);
return ans;
}
private void dfs(String digits, int dept, StringBuilder str, String[] map) {
if (digits.length() == dept) {
ans.add(str.toString());
return;
}
char temp = digits.charAt(dept);
int index = temp - '0';
String strtemp = map[index];
for (char c : strtemp.toCharArray()) {
str.append(c);
dfs(digits, dept + 1, str, map);
str.deleteCharAt(str.length() - 1);
}
}
21.二叉树的最近公共祖先 LeetCode 236题
题解:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//root为空或者p和q有一个结点再root结点返回root
if(root==null||root==p||root==q){
return root;
}
//左子树递归
TreeNode left=lowestCommonAncestor(root.left,p,q);
//右子树递归
TreeNode right=lowestCommonAncestor(root.right,p,q);
//每次递归完看p和q在不在root结点
if(left==null){
return right;
}
if(right==null){
return left;
}
return root;
22.组合 LeetCode 77题
题解:和排列一样注意循环是从start =1 开始
List<List<Integer>> ans = new ArrayList<>();
public List<List<Integer>> combine(int n, int k) {
dfs(new ArrayList<>(), 1, n, k);
return ans;
}
private void dfs(ArrayList<Integer> temp, int start, int n, int k) {
if (k == temp.size()) {
ans.add(new ArrayList<>(temp));
return;
}
for (int i = start; i <= n; i++) {
temp.add(i);
dfs(temp, i + 1, n, k);
temp.remove(temp.size() - 1);
}
23.Leetcode316. 去除重复字母
题解:用栈依次记录字母
1.当发现栈里面的存过相同的字符不加不相同再加到栈里面,最后栈里面的数据翻转输出。
2.按1的方法不能满足字典序最小,比如babc返回的是bac题目要求返回abc
3.所以再判断栈里面有相同的元素的时候,再判断是否比栈顶的元素小,如果小且栈顶弹出的元素后面循环还能再出现就弹出,反之不弹出,以为整个字符就他一个字符没有重复的了不能弹出
public String removeDuplicateLetters(String s) {
//放不重复的元素
Stack<Character> stk = new Stack<>();
//每个字符出现的次数数组
int[] count=new int[256];
for(int i=0;i<s.length();i++){
count[s.charAt(i)]++;
}
//判断是否加入过栈
boolean[] inStack=new boolean[256];
for(char c :s.toCharArray()){
//循环过计数要减1
count[c]--;
if(inStack[c]){
continue;
}
//当前值比栈顶小
while(!stk.isEmpty() && stk.peek() > c){
//若之后不存在栈顶元素了,则停止 pop
if(count[stk.peek()]==0){
break;
}
//弹出
char kk=stk.pop();
//设置成false下次来的整个值放进去
inStack[kk]=false;
}
//放到栈里面
stk.push(c);
//c已经放过了
inStack[c]=true;
}
//翻转输出
StringBuilder sb = new StringBuilder();
while (!stk.empty()) {
sb.append(stk.pop());
}
return sb.reverse().toString();
}