2020-12-20leetcode刷题总结

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();

    }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值