• 学习更新2021年11月


    2021-11-04

    有的时候真的是坚持做一件事非常难,再而衰,三而竭。

    L75. 颜色分类

    L75. 颜色分类
    双指针解法,最开始写了一个双指针解法,发现除了点儿问题。没考虑到二次交换的问题

    //[1,2,0]
        public void sortColors(int[] nums) {
            //0的指针
            int z = 0;
            //2的指针
            int t = nums.length - 1;
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] == 0) {
    
                    //而前面0后面必定跟的是1,比如0,0,1,2,0
                    // 当前i如果指向2,则2会交换到后面去,所以当前位置不可能出现交换后
                    // 当前元素只有是0的时候会和前面的1交换,此时因为只可能是1,所以不存在二次交换的问题
    
                    swap(nums, z++, i);
                } else if (nums[i] == 2 && i < t) {
                    swap(nums, t--, i);
                    //因为新换过来的数可能是0,那么久还可能再交换一次
                    i--;
                }
            }
        }
    
        public void swap(int[] nums, int i, int j) {
            if (i == j) {
                return;
            }
            nums[i] = nums[i] ^ nums[j];
            nums[j] = nums[i] ^ nums[j];
            nums[i] = nums[i] ^ nums[j];
        }
    

    2021-11-05

    今天刷了两道简单题,不过都挺有意思的

    543. 二叉树的直径

    P543. 二叉树的直径

    第一次见到这道题的时候,觉得很复杂,没啥思路,今天认真想了一下,你会发现要想使得路径最长,一定是一个点为基准,分别向左子树方向和右子树方向走出的最长路径,再加起来。因为dfs只能递归高度给上层调用,那么就还需要另外一个遍历记录最大路径。如果是java支持多返回值,或许就不用全局变量来记录最大路径的值了。

    class Solution {
        int max = 0;
        public int diameterOfBinaryTree(TreeNode root) {
            diameterOfBinaryTreeDepth(root);
            return max;
        }
         public int diameterOfBinaryTreeDepth(TreeNode root) {
            if (root == null ) {
                return 0;
            }
            int r = diameterOfBinaryTreeDepth(root.right);
            int l = diameterOfBinaryTreeDepth(root.left);
            max = Math.max(max, r+l);
            return Math.max(l, r) + 1;
        }
    }
    

    283. 移动零

    283. 移动零
    自己想的最直接的方法肯定是把0直接往尾部较换,但因为其他元素得保持位置,所以想了半天还是没相处指针的解法。该题目明确说了不能复制数组,后来实在没找,想了一个map,其实还是复制数组,这个方法的原始思路就是,你能明确的知道最终每个元素的索引都是什么位置了,但完全没用,得开一个新数组来赋值,或者就是map了。算是不太满足要求。

    class Solution {
        public void moveZeroes(int[] nums) {
            //int index[] = new int[nums.length];
            Map<Integer,Integer> map= new HashMap<>();
    
            //普通位置的偏移量
            int flow = 0;
            //高位0的占位偏移
            int t = nums.length - 1;
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] == 0) {
                    flow++;
                    map.put(t--,nums[i]);
                } else {
                    map.put(i - flow,nums[i]);
                }
            }
    
            for (int i = 0; i < nums.length; i++) {
                nums[i]= map.get(i);
            }
    
        }
    }
    

    看了其他网友得解。思路是:把不是0的数字往前换,换第一个0,如果前面没有0都可以不换。h指针记录第一个0,但如果第一个不是0,可以不换,也可以换,但h指针只要遇到不是指针就往后移动

    class Solution {
        public void moveZeroes(int[] nums) {
    
            //h需要通第一个元素较换的非0元素
            int h = 0;
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] != 0) {
                    swap(nums, i, h);
                    h++;
                }
            }
        }
    
        public void swap(int[] nums, int i, int j) {
            if (i == j) {
                return;
            }
            nums[i] = nums[i] ^ nums[j];
            nums[j] = nums[i] ^ nums[j];
            nums[i] = nums[i] ^ nums[j];
        }
    }
    

    上面这个还有个技巧,因为总是和0较换,所以可以直接把所有不为0的元素往前移动,最后补充后面的0就可以了。这里要记录需要补多少0,较换完所有的元素h指针指向第一个要赋值为0的位置

    public void moveZeroes(int[] nums) {
            int h = 0;
            int i = 0;
            while (i < nums.length) {
                if (nums[i] != 0) {
                    nums[h++] = nums[i];
                }
                i++;
            }
            for(;h<nums.length;h++){
                nums[h]=0;
            }
        }
    

    2021-11-05

    1218. 最长定差子序列

    1218. 最长定差子序列

    做过最长递增子序列的同学肯定想都不想就写出了下面的解法。

    class Solution {
        public int longestSubsequence(int[] arr, int difference) {
            if (arr == null) {
                return 0;
            }
            int dp[] = new int[arr.length];
            int max = 1;
            for (int i = 0; i < arr.length; i++) {
                dp[i] = 1;
                for (int j = 0; j < i; j++) {
                    if (arr[i] - arr[j] == difference) {
                        dp[i] = Math.max(dp[j] + 1, dp[i]);
                    }
                }
                max = Math.max(dp[i], max);
            }
            return max;
        }
    }
    

    提交之后,用例超时,这里注意到整个时间复杂度还是O(n2),数据量大,题目下方还有数据规模说明。在没有想到其他方法的情况下我用优先级队列,来做中间这个循环的优化,这样不用比较所有的前面的元素了

    class Solution {
       public int longestSubsequence(int[] arr, int difference) {
            if (arr == null) {
                return 0;
            }
            PriorityQueue<Integer[]> queue = new PriorityQueue<Integer[]>((o1, o2) -> o2[1] - o1[1]);
            int dp[] = new int[arr.length];
            int max = 1;
            for (int i = 0; i < arr.length; i++) {
                dp[i] = 1;
                PriorityQueue<Integer[]> cp = new PriorityQueue<Integer[]>(queue);
                while (!cp.isEmpty()) {
                    Integer[] e = cp.poll();
                    int j = e[0];
                    if (arr[i] - arr[j] == difference) {
                        dp[i] = Math.max(dp[j] + 1, dp[i]);
                    }
                }
                queue.add(new Integer[]{i, dp[i]});
                max = Math.max(dp[i], max);
            }
            return max;
        }
    }
    

    完全没用。考虑到这里因为有的用例基本上没有连续等差子序列,这种优先级排序基本没多大的优化效果。最后看了一下map的解法.

    我是之前从dp数组来的,这题感觉固定思维了,开始写了个连续递增序列的思路超时了,就是没想到这里等差,还能运用和两束之和的思想。两数之和,两数之差,等差,都能map降维

    class Solution {
        public int longestSubsequence(int[] arr, int difference) {
            Map<Integer, Integer> dpm = new HashMap<>();
            //引入dp数组,只为更贴近之前做的dp,方便理解一些
            int dp[] = new int[arr.length];
            int max = 1;
            for (int i = 0; i < arr.length; i++) {
                int v = arr[i];
                if (dpm.containsKey(v)) {
                    dp[i] = dpm.get(v);
                } else {
                    dp[i] = 1;
                }
                dpm.put(v + difference, dp[i] + 1);
                max = Math.max(dp[i], max);
            }
            return max;
        }
    }
    

    2021-11-05

    最大子矩阵

    最大子矩阵

    public class Solution {
        /**
         * @param matrix: the given matrix
         * @return: the largest possible sum
         */
        public int maxSubmatrix(int[][] matrix) {
            // write your code here
            if(matrix==null||matrix.length==0|| matrix[0].length==0)
                return 0;
    
            int h = matrix.length;
            int w = matrix[0].length;
            int[][] m = new int[h][w+1];
    
            for (int i = 0; i < h; i++) {
                for (int j = 1; j <=w; j++) {
                    m[i][j] = m[i][j - 1] + matrix[i][j-1];
                }
            }
    
    
            int max = 0;
            for (int f = 0; f < h; f++) {
                for (int l = f+1; l <=w; l++) {
                    int childMax = 0;
                    for (int i = 0; i < h; i++) {
                        int x = m[i][l] - m[i][f];
                        if(x+childMax<=0){
                            childMax=0;
                        }else{
                            childMax= childMax+x;
                            max= Math.max(childMax,max);
                        }
                    }
                }
            }
            //System.out.println(max);
            return max;
        }
    }
    

    这题是一本通中的一道动态规划,咋看这个题的题解是没啥dp的样子,但用到了前缀和,前缀和是dp的一个知识点。这题大的结构上还用到了贪心策略。这个解法有个小问题,就是如果所有元素都是负数,这个答案是有问题的,还需要调整一下。

    2021-11-17

    剑指 Offer II 052. 展平二叉搜索树

    剑指 Offer II 052. 展平二叉搜索树

    class Solution {
       TreeNode h = new TreeNode(-1);
        TreeNode cur =h;
    
        public TreeNode increasingBST(TreeNode root) {
            dfs(root);
            cur.left=null;
            return h.right;
        }
        public void dfs(TreeNode root) {
            if (root == null) {
                return;
            }
            if (root.left != null) {
                dfs(root.left);
            }
            cur.left = null;
            cur.right = root;
            cur = root;
            if (root.right != null) {
                dfs(root.right);
            }
        }
    }
    

    这题如果用普通的解法,就直接遍历构造。而上面这个解法是比较秀的。感觉很像连表迭代。但这个题还要注意一点,dfs后,一定要把cur.left赋值为null,不然可能出现环。比如如下树。因为最后遍历的节点是4,遍历完之后就直接跳出dfs了,而此时4的右边节点还是引用着3节点。

       2
      / 
    1    4
       /
      3
    

    2021-11-18

    二叉搜索树中两个节点之和

    二叉搜索树中两个节点之和
    这道题第一种比较好想得解法是,先遍历所有节点,把所有节点放到数组里,这样这道题就转换为两数之和了。下面这种方法是两数之和的变形,避免了重复计算的问题,最多的情况一次遍历搞定。

    class Solution {
        public boolean findTarget(TreeNode root, int k) {
    
            //Map<Integer,Integer> map= new HashMap<>();
            Set<Integer> set = new HashSet<>();
            return dfs(root,k,new HashSet<>());
        }
    
        public boolean dfs(TreeNode root, int k, Set<Integer> set) {
            if (root == null)
                return false;
            if (set.contains(root.val)) {
                return true;
            }
            set.add(k - root.val);
            return dfs(root.left, k, set)||dfs(root.right, k, set);
        }
    
    }
    

    剑指 Offer 34. 二叉树中和为某一值的路径

    剑指 Offer 34. 二叉树中和为某一值的路径
    dfs和回溯法,不断收集定点到根节点的路径

    class Solution {
        public List<List<Integer>> pathSum(TreeNode root, int target) {
            List<List<Integer>> st = new ArrayList<>();
            List<Integer> c = new ArrayList<>();
            dfs(root,target,st,c,0);
            return st;
        }
    
         public void dfs(TreeNode root, int target, List<List<Integer>> st, List<Integer> c, int sum) {
            if (root == null) {
                return;
            }
            if (root.left == null && root.right == null) {
                if (sum + root.val == target) {
                    ArrayList<Integer> path = new ArrayList<>(c);
                    path.add(root.val);
                    st.add(path);
                }
                return;
            }
            c.add(root.val);
            if (root.left != null) {
                dfs(root.left, target, st, c, sum + root.val);
            }
            if (root.right != null) {
                dfs(root.right, target, st, c, sum + root.val);
            }
            //回溯后一定要移除当前节点,因为当前节点在最后一个,所以移除最后一个
            c.remove(c.size()-1);
        }
    }
    

    2021-11-25

    92 · 背包问题

    92 · 背包问题
    这道最原始背包问题,在我之前写过一下,以前对动态规划理解不够深,代码写得比较复杂了。经过大规模的训练之后,能把这块写的相对简单易于理解了。

    • 1.dp[i]代表重量和为i是否能够拼出来,则dp[0]=true是一个初始化
    • 2.每次拿着当前已经拼凑好的dp[i]去和新的物品凑新重量,因为这里dp[i]=true的情况只能在当前这轮的最大值以内产生,所以边界值是curMax=max
    • 3.遍历dp[j]时,j一定不能从0到curMax,一定是从大到小的,不然将会出现一个物品被塞入两次的情况。比如【5,1,3,3】的物品,当i=3时,因为dp[1]=true推导出dp[4]=true,接下来又会遍历到dp[4]=true推导出dp[7]=true,事时上dp[4]是当前这个3加入背包后产生的,再次加入就相当于加入两次。
    public class Solution {
        /**
         * @param m: An integer m denotes the size of a backpack
         * @param A: Given n items with size A[i]
         * @return: The maximum size
         */
         public int backPack(int m, int[] A) {
            // write your code here
            //n个物件的重量放在 A数组中
    
            boolean[] dp = new boolean[m + 1];
            dp[0] = true;
    
            int max = 0;
            for (int i : A) {
                int curMax = max;
                for (int j = curMax; j >= 0; j--) {
                    if (dp[j] && j + i <= m) {
                        max = Math.max(j + i, max);
                        dp[j + i]=true;
                    }
                }
            }
            return max;
        }
    }
    

    2021-11-26

    125 · 背包问题(二)

    125 · 背包问题(二)
    本道题才是背包问题的本来的面目。也就是经典的0,1背包问题。前面的背包问题,仅仅考虑重量。
    这里考虑dp[i] 代表 背包装i重量的体积的最大价值是多少。

    往后推:dp[j + A[i]] = Math.max(dp[j + A[i]], dp[j] + V[i]);

    public class Solution {
        /**
         * @param m: An integer m denotes the size of a backpack
         * @param A: Given n items with size A[i]
         * @param V: Given n items with value V[i]
         * @return: The maximum value
         */
          public int backPackII(int m, int[] A, int[] V) {
            //write your code here
            int dp[] = new int[m + 1];
            int mv = 0;
            for (int i = 0; i < A.length; i++) {
                int cw = A[i];
                for (int j = m; j >= 0; j--) {
                    if (j == 0 || dp[j] > 0 && j + cw <= m) {
                        dp[j + cw] = Math.max(dp[j + cw], dp[j] + V[i]);
                        mv = Math.max(mv, dp[j + cw]);
                    }
                }
            }
            return mv;
        }
    }
    

    另外一种往前推的 状态转移
    dp[j]= dp[j-A[i]]+ V[i] 满足的条件是j这个体积够装A[i] 这个重量

    public class P1267_3 {
        public int backPackII(int m, int[] A, int[] V) {
            // write your code here
            int dp[] = new int[m + 1];
            //dp[j]表示选择了第i个元素时, 重量为j时的最大价值
            dp[0] = 0;
            int max=0;
            for (int i = 0; i < A.length; i++) {
                for (int j = m; j >= 0; j--) {
                    if (j >= A[i]){
                        dp[j] = Math.max(dp[j - A[i]] + V[i], dp[j]);
                        max=Math.max(max,dp[j]);
                    }
                }
            }
            return max;
        }
    }
    
  • 相关阅读:
    三剑客之grep命令
    expect
    信号控制
    数组
    LaTex: Cetx +Winedit之文献引用---Elsevier模板
    vue系列--【animate.css、过滤器、组件基础】
    vue系列--【生命周期、侦听器watch、计算属性、jsonp解决跨域】
    vue系列--【动态样式、表单数据绑定、表单修饰符、事件处理、$set】
    vue系列--【vue核心、vue实例、指令】
    node系列--【socket.io框架】
  • 原文地址:https://www.cnblogs.com/mxjhaima/p/15506447.html
Copyright © 2020-2023  润新知