代码随想录——刷题笔记

该博客持续更新LeetCode算法题解,涵盖数组、链表、哈希表等数据结构,以及回溯、贪心、动态规划等算法。针对每道题给出思路,如数组题用双指针,链表题注意指针移动和节点赋值,还介绍了各算法的局部与全局最优解思路及动态规划的五步走方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

持续更新中~~~

数组

有序数组的平方(LeetCode977)

原地算法外加双指针

class Solution {
    public int[] sortedSquares(int[] nums) {
        int n=nums.length;
        int[] res=new int[n];
        for(int i=0,j=n-1,k=n-1;i<=j;){
            if(Math.abs(nums[i])<=Math.abs(nums[j])){
                res[k]=nums[j]*nums[j];
                j--;k--;
            }
            else{
                res[k]=nums[i]*nums[i];
                i++;k--;
            }
        }
        return res;
    }
}

长度最小的子数组(LeetCode209)

如果具备单调性,则用双指针拉框

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int res=Integer.MAX_VALUE;
        for (int i=0,j=0,sum=0;i<nums.length; i ++ ) {
            sum += nums[i];
            while (sum-nums[j] >= target) sum-=nums[j ++ ];
            if (sum >= target) res=Math.min(res, i - j + 1);
        }
        return res==Integer.MAX_VALUE?0:res;
    }
}

螺旋矩阵||(LeetCode59)

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] con={{0,1},{1,0},{0,-1},{-1,0}};
        int[][] res= new int[n][n];
        for(int i=1,x=0,y=0,k=0;i<=n*n;i++){
            res[x][y]=i;
            int a=x+con[k][0],b=y+con[k][1];
            if(a<0||a>=n||b<0||b>=n||res[a][b]!=0){
                k=(k+1)%4;
            }
            x+=con[k][0];
            y+=con[k][1];
        }
        return res;
    }
}

链表

设计链表(LeetCode707)

反转链表(LeetCode206)

两个指针移动就好,唯一要注意的是head.next节点要重新赋值为null

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null||head.next==null) return head;
        ListNode before=head,after=head.next;
        while(after!=null){
            ListNode temp=after.next;
            after.next=before;
            before=after;
            after=temp;
        }
        head.next=null;
        return before;
    }
}

 递归解法

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head || !head->next) return head;
        ListNode *tail = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return tail;
    }
};

 两两交换链表中的节点(LeetCode24)

头结点会变,所以用虚拟头结点,三个指针搞定

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode res=new ListNode(0);
        res.next=head;
        ListNode node=res;
        while(node.next!=null&&node.next.next!=null){
           ListNode a=node.next,b=node.next.next;
           a.next=b.next;
           node.next=b;
           b.next=a;
           node=a;
        }
        return res.next;
    }
}

K个一组翻转链表(LeetCode25)

先判断是否有K个,先内部翻转,再将外部连接。

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy=new ListNode(0);
        dummy.next=head;
        ListNode start=dummy;
        while(start!=null){
            ListNode end=start;
            for(int i=0;i<k&&end!=null;i++)    end=end.next;
            if(end==null)  break;
            ListNode a=start.next,b=a.next;
            for(int i=0;i<k-1;i++){
                ListNode temp=b.next;
                b.next=a;
                a=b;
                b=temp;
            }
            ListNode temp=start.next;
            temp.next=b;
            start.next=a;
            start=temp;
        }
        return dummy.next;
    }
}

环形链表||

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head==null)  return null;
        ListNode fast=head.next,slow=head;
        while(fast!=slow){
            if(fast==null&&slow==null)  return null;
            
        }
        return fast;
    }
}

哈希表

两个数组的交集(LeetCode349)

统计两个数组找出两个数组中均存在的元素。长度有限用数组,无限用hash,去重用hashset

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        int[] hash1 = new int[1002];
        int[] hash2 = new int[1002];
        for(int i : nums1)
            hash1[i]++;
        for(int i : nums2)
            hash2[i]++;
        List<Integer> resList = new ArrayList<>();
        for(int i = 0; i < 1002; i++)
            if(hash1[i] > 0 && hash2[i] > 0)
                resList.add(i);
        int index = 0;
        int res[] = new int[resList.size()];
        for(int i : resList)
            res[index++] = i;
        return res;
    }
}

 快乐数(LeetCode202)

按照逻辑写循环代码,终止条件为得到结果为1或出现重复项。

class Solution {
    public boolean isHappy(int n) {
        Set<Integer> record = new HashSet<>();
        while (n != 1 && !record.contains(n)) {
            record.add(n);
            n = getNextNumber(n);
        }
        return n == 1;
    }

    private int getNextNumber(int n) {
        int res = 0;
        while (n > 0) {
            int temp = n % 10;
            res += temp * temp;
            n = n / 10;
        }
        return res;
    }
}

两数之和(LeetCode1)

我是傻逼,必须一边遍历一边找,不然会有重复项搞心态

public int[] twoSum(int[] nums, int target) {
    int[] res = new int[2];
    if(nums == null || nums.length == 0){
        return res;
    }
    Map<Integer, Integer> map = new HashMap<>();
    for(int i = 0; i < nums.length; i++){
        int temp = target - nums[i];   // 遍历当前元素,并在map中寻找是否有匹配的key
        if(map.containsKey(temp)){
            res[1] = i;
            res[0] = map.get(temp);
            break;
        }
        map.put(nums[i], i);    // 如果没找到匹配对,就把访问过的元素和下标加入到map中
    }
    return res;
}

四数相加II(LeetCode454)

把四个数分两组,一组记录次数,另一组来与前一组匹配

class Solution {
    public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
        Map<Integer, Integer> countAB = new HashMap<Integer, Integer>();
        for (int u : A) {
            for (int v : B) {
                countAB.put(u + v, countAB.getOrDefault(u + v, 0) + 1);
            }
        }
        int ans = 0;
        for (int u : C) {
            for (int v : D) {
                if (countAB.containsKey(-u - v)) {
                    ans += countAB.get(-u - v);
                }
            }
        }
        return ans;
    }
}

 三数之和(LeetCode15)

因为需要去重,所以哈希表作用不大。进行去重遍历,再用双指针找值即可

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > 0) { 
                return result;
            }

            if (i > 0 && nums[i] == nums[i - 1]) {  // 去重a
                continue;
            }

            int left = i + 1;
            int right = nums.length - 1;
            while (right > left) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum > 0) {
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
		    // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;
                    
                    right--; 
                    left++;
                }
            }
        }
        return result;
    }
}

四数之和(LeetCode18)

比三数之和多一重for循环

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
       
        for (int i = 0; i < nums.length; i++) {
            // nums[i] > target 直接返回, 剪枝操作
            if (nums[i] > 0 && nums[i] > target) {
                return result;
            }
		
            if (i > 0 && nums[i - 1] == nums[i]) {    // 对nums[i]去重
                continue;
            }
            
            for (int j = i + 1; j < nums.length; j++) {

                if (j > i + 1 && nums[j - 1] == nums[j]) {  // 对nums[j]去重
                    continue;
                }

                int left = j + 1;
                int right = nums.length - 1;
                while (right > left) {
		    // nums[k] + nums[i] + nums[left] + nums[right] > target int会溢出
                    long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum > target) {
                        right--;
                    } else if (sum < target) {
                        left++;
                    } else {
                        result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        // 对nums[left]和nums[right]去重
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;

                        left++;
                        right--;
                    }
                }
            }
        }
        return result;
    }
}

字符串

反转字符串II(LeetCode541)
 

class Solution {
    public String reverseStr(String s, int k) {
        int n = s.length();
        char[] arr = s.toCharArray();
        for (int i = 0; i < n; i += 2 * k) {
            reverse(arr, i, Math.min(i + k, n) - 1);
        }
        return new String(arr);
    }

    public void reverse(char[] arr, int left, int right) {
        while (left < right) {
            char temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
            left++;
            right--;
        }
    }
}

反转字符串中的单词(LeetCode151)

从后往前,分割单词放入队头

class Solution {
    public String reverseWords(String s) {
        s = s.trim();                                    // 删除首尾空格
        int j = s.length() - 1, i = j;
        StringBuilder res = new StringBuilder();
        while (i >= 0) {
            while (i >= 0 && s.charAt(i) != ' ') i--;     // 搜索首个空格
            res.append(s.substring(i + 1, j + 1) + " "); // 添加单词
            while (i >= 0 && s.charAt(i) == ' ') i--;     // 跳过单词间空格
            j = i;                                       // j 指向下个单词的尾字符
        }
        return res.toString().trim();                    // 转化为字符串并返回
    }
}

找出字符串中第一个匹配项的下标(LeetCode28)

KMP,朴素做法也能解但是如果是好的公司,用朴素做法容易凉

class Solution {
    // KMP 算法
    // ss: 原串(string)  pp: 匹配串(pattern)
    public int strStr(String ss, String pp) {
        if (pp.isEmpty()) return 0;
        
        // 分别读取原串和匹配串的长度
        int n = ss.length(), m = pp.length();
        // 原串和匹配串前面都加空格,使其下标从 1 开始
        ss = " " + ss;
        pp = " " + pp;

        char[] s = ss.toCharArray();
        char[] p = pp.toCharArray();

        // 构建 next 数组,数组长度为匹配串的长度(next 数组是和匹配串相关的)
        int[] next = new int[m + 1];
        // 构造过程 i = 2,j = 0 开始,i 小于等于匹配串长度 【构造 i 从 2 开始】
        for (int i = 2, j = 0; i <= m; i++) {
            // 匹配不成功的话,j = next(j)
            while (j > 0 && p[i] != p[j + 1]) j = next[j];
            // 匹配成功的话,先让 j++
            if (p[i] == p[j + 1]) j++;
            // 更新 next[i],结束本次循环,i++
            next[i] = j;
        }

        // 匹配过程,i = 1,j = 0 开始,i 小于等于原串长度 【匹配 i 从 1 开始】
        for (int i = 1, j = 0; i <= n; i++) {
            // 匹配不成功 j = next(j)
            while (j > 0 && s[i] != p[j + 1]) j = next[j];
            // 匹配成功的话,先让 j++,结束本次循环后 i++
            if (s[i] == p[j + 1]) j++;
            // 整一段匹配成功,直接返回下标
            if (j == m) return i - m;
        }

        return -1;
    }
}

KMP代码模板:

public boolean kmp(String query, String pattern) {
        int n = query.length();
        int m = pattern.length();
        int[] fail = new int[m];
        Arrays.fill(fail, -1);
        for (int i = 1; i < m; ++i) {
            int j = fail[i - 1];
            while (j != -1 && pattern.charAt(j + 1) != pattern.charAt(i)) {
                j = fail[j];
            }
            if (pattern.charAt(j + 1) == pattern.charAt(i)) {
                fail[i] = j + 1;
            }
        }
        int match = -1;
        for (int i = 1; i < n - 1; ++i) {
            while (match != -1 && pattern.charAt(match + 1) != query.charAt(i)) {
                match = fail[match];
            }
            if (pattern.charAt(match + 1) == query.charAt(i)) {
                ++match;
                if (match == m - 1) {
                    return true;
                }
            }
        }
        return false;
    }

重复的子字符串(LeetCode459)

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        return kmp(s + s, s);
    }

    public boolean kmp(String query, String pattern) {
        int n = query.length();
        int m = pattern.length();
        int[] fail = new int[m];
        Arrays.fill(fail, -1);
        for (int i = 1; i < m; ++i) {
            int j = fail[i - 1];
            while (j != -1 && pattern.charAt(j + 1) != pattern.charAt(i)) {
                j = fail[j];
            }
            if (pattern.charAt(j + 1) == pattern.charAt(i)) {
                fail[i] = j + 1;
            }
        }
        int match = -1;
        for (int i = 1; i < n - 1; ++i) {
            while (match != -1 && pattern.charAt(match + 1) != query.charAt(i)) {
                match = fail[match];
            }
            if (pattern.charAt(match + 1) == query.charAt(i)) {
                ++match;
                if (match == m - 1) {
                    return true;
                }
            }
        }
        return false;
    }
}

双指针

移除元素(LeetCode27)

双指针,左边指针找等于val的,右边指针找不等于val的。长度就是left的位置

class Solution {
    public int removeElement(int[] nums, int val) {
        int l=0;
        int r=nums.length-1;
        while(l<=r){
            while(l<=r&&nums[l]!=val) l++;
            while(l<=r&&nums[r]==val) r--;
            if(l<r) nums[l++]=nums[r--];
        }
        return l;
    }
}

栈与队列

有效的括号(LeetCode20)

考虑三种情况,用栈压入

class Solution {
    public boolean isValid(String s) {
        Deque<Character> deque = new LinkedList<>();
        char ch;
        for (int i = 0; i < s.length(); i++) {
            ch = s.charAt(i);
            //碰到左括号,就把相应的右括号入栈
            if (ch == '(') {
                deque.push(')');
            }else if (ch == '{') {
                deque.push('}');
            }else if (ch == '[') {
                deque.push(']');
            } else if (deque.isEmpty() || deque.peek() != ch) {
                return false;
            }else {//如果是右括号判断是否和栈顶元素匹配
                deque.pop();
            }
        }
        //最后判断栈中元素是否匹配
        return deque.isEmpty();
    }
}

删除字符串中所有相邻的重复项(LeetCode1047)

这道题队列,栈都可以。放在这里就是熟悉一下StringBuffer的写法
 

class Solution {
    public String removeDuplicates(String s) {
        StringBuffer stack = new StringBuffer();
        int top = -1;
        for (int i = 0; i < s.length(); ++i) {
            char ch = s.charAt(i);
            if (top >= 0 && stack.charAt(top) == ch) {
                stack.deleteCharAt(top);
                --top;
            } else {
                stack.append(ch);
                ++top;
            }
        }
        return stack.toString();
    }
}

二叉树

左叶子之和(LeetCode404)

遍历,难点在于如何判断该节点为左叶子节点

class Solution {  
    int res = 0;  
    public int sumOfLeftLeaves(TreeNode root) {  
        dfs(root);  
        return res;  
    }  
    public void dfs(TreeNode root) {  
        if (root == null) return;  
        if (root.left != null) {  
            if (root.left.left == null && root.left.right == null) {  
                res += root.left.val;  
            }  
        }  
        dfs(root.left);  
        dfs(root.right);  
    }  
}

 找树左下角的值(LeetCode513)

通过maxd记录深度,最左侧的值恰好是突破当前记录的最大深度的第一个值。

class Solution {
    int ans=0;  
    int maxd=0;  
    public int findBottomLeftValue(TreeNode root) {  
        dfs(root, 1);  
        return ans;  
    }  
  
    private void dfs(TreeNode root, int d) {  
        if (root == null)   return;  
        if (d > maxd) {  
            maxd = d;  
            ans = root.val;  
        }  
        dfs(root.left, d + 1);  
        dfs(root.right, d + 1);  
    } 
} 

 

二叉树的所有路径(LeetCode257)

要用到StringBuffer函数,终止条件是左右同时为null,使用中序遍历

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> paths = new ArrayList<String>();
        constructPaths(root, "", paths);
        return paths;
    }

    public void constructPaths(TreeNode root, String path, List<String> paths) {
        if (root != null) {
            StringBuffer pathSB = new StringBuffer(path);
            pathSB.append(Integer.toString(root.val));
            if (root.left == null && root.right == null) {  // 当前节点是叶子节点
                paths.add(pathSB.toString());  // 把路径加入到答案中
            } else {
                pathSB.append("->");  // 当前节点不是叶子节点,继续递归遍历
                constructPaths(root.left, pathSB.toString(), paths);
                constructPaths(root.right, pathSB.toString(), paths);
            }
        }
    }
}

路径总和(LeetCode112)

class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        if (root == null) {
            return false;
        }
        if (root.left == null && root.right == null) {
            return sum == root.val;
        }
        return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
    }
}

路径总和II(LeetCode113)

用队列,满足就插入不满足就将当前值弹出

class Solution {
    List<List<Integer>> ret = new LinkedList<List<Integer>>();
    Deque<Integer> path = new LinkedList<Integer>();

    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        dfs(root, targetSum);
        return ret;
    }

    public void dfs(TreeNode root, int targetSum) {
        if (root == null) {
            return;
        }
        path.offerLast(root.val);
        targetSum -= root.val;
        if (root.left == null && root.right == null && targetSum == 0) {
            ret.add(new LinkedList<Integer>(path));
        }
        dfs(root.left, targetSum);
        dfs(root.right, targetSum);
        path.pollLast();
    }
}

最大二叉树(LeetCode654)

找到最大值,然后把最大值分为左右两块进行递归

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return construct(nums, 0, nums.length - 1);
    }

    public TreeNode construct(int[] nums, int left, int right) {
        if (left > right) {
            return null;
        }
        int best = left;
        for (int i = left + 1; i <= right; ++i) {
            if (nums[i] > nums[best]) {
                best = i;
            }
        }
        TreeNode node = new TreeNode(nums[best]);
        node.left = construct(nums, left, best - 1);
        node.right = construct(nums, best + 1, right);
        return node;
    }
}

合并二叉树(LeetCode617)

构造二叉树的基本代码

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1==null&&root2==null) return null;
        if(root1==null) return root2;
        if(root2==null) return root1;
        TreeNode node=new TreeNode(root1.val+root2.val);
        node.left=mergeTrees(root1.left,root2.left);
        node.right=mergeTrees(root1.right,root2.right);
        return node;
    }
}

二叉搜索树中的搜索(LeetCode700)

我是傻逼

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if(root==null)  return null;
        if(root.val==val)   return root;
       return searchBST(val < root.val ? root.left : root.right, val);
    }
}

二叉搜索树的最小绝对差(LeetCode530)

把节点的值按从小到大排序,计算最小的差值即可。因为是二叉搜索树,所有可以一边遍历一边计算,二叉搜索树一般使用中序遍历

class Solution {
    TreeNode pre;// 记录上一个遍历的结点
    int result = Integer.MAX_VALUE;
    public int getMinimumDifference(TreeNode root) {
       if(root==null)return 0;
       traversal(root);
       return result;
    }
    public void traversal(TreeNode root){
        if(root==null)return;
        traversal(root.left);
        if(pre!=null){
            result = Math.min(result,root.val-pre.val);
        }
        pre = root;
        traversal(root.right);
    }
}

二叉搜索树中的众数(LeetCode501)

和上一题的解法一样

class Solution {
    ArrayList<Integer> resList;
    int maxCount;
    int count;
    TreeNode pre;

    public int[] findMode(TreeNode root) {
        resList = new ArrayList<>();
        maxCount = 0;
        count = 0;
        pre = null;
        findMode1(root);
        int[] res = new int[resList.size()];
        for (int i = 0; i < resList.size(); i++) {
            res[i] = resList.get(i);
        }
        return res;
    }

    public void findMode1(TreeNode root) {
        if (root == null) {
            return;
        }
        findMode1(root.left);

        int rootValue = root.val;
        // 计数
        if (pre == null || rootValue != pre.val) {
            count = 1;
        } else {
            count++;
        }
        // 更新结果以及maxCount
        if (count > maxCount) {
            resList.clear();
            resList.add(rootValue);
            maxCount = count;
        } else if (count == maxCount) {
            resList.add(rootValue);
        }
        pre = root;

        findMode1(root.right);
    }
}

二叉树的最近公共祖先(LeetCode236)

如果找到了就返回节点,当同时找到两个节点时返回节点值
值得思考的是为什么用后序遍历,自顶向下用前序遍历,自底向上用后序,面对二叉搜索树时中序遍历得到递增的数组

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) { // 递归结束条件
            return root;
        }

        // 后序遍历
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);

        if(left == null && right == null) { // 若未找到节点 p 或 q
            return null;
        }else if(left == null && right != null) { // 若找到一个节点
            return right;
        }else if(left != null && right == null) { // 若找到一个节点
            return left;
        }else { // 若找到两个节点
            return root;
        }
    }
}

二叉搜索树的最近公共祖先(LeetCode235)

因为是二叉搜索树所以本身就是有序的,也不用处理中间节点,值在p,q之间的节点就返回。

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
        if (root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
        return root;
    }
}

删除二叉搜索树中的节点(LeetCode450)

将删除节点与右子树的左叶子节点交换即可

class Solution {
  public TreeNode deleteNode(TreeNode root, int key) {
    if (root == null) return root;
    if (root.val == key) {
      if (root.left == null) {
        return root.right;
      } else if (root.right == null) {
        return root.left;
      } else {
        TreeNode cur = root.right;
        while (cur.left != null) {
          cur = cur.left;
        }
        cur.left = root.left;
        root = root.right;
        return root;
      }
    }
    if (root.val > key) root.left = deleteNode(root.left, key);
    if (root.val < key) root.right = deleteNode(root.right, key);
    return root;
  }
}

修剪二叉搜索树(LeetCode669)

超出范围就递归到下一层

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if (root == null) {
            return null;
        }
        if (root.val < low) {
            return trimBST(root.right, low, high);
        }
        if (root.val > high) {
            return trimBST(root.left, low, high);
        }
        // root在[low,high]范围内
        root.left = trimBST(root.left, low, high);
        root.right = trimBST(root.right, low, high);
        return root;
    }
}

把二叉搜索树转换为累加树(LeetCode538)

写不出来

class Solution {
    int sum;
    public TreeNode convertBST(TreeNode root) {
        sum = 0;
        convertBST1(root);
        return root;
    }

    // 按右中左顺序遍历,累加即可
    public void convertBST1(TreeNode root) {
        if (root == null) {
            return;
        }
        convertBST1(root.right);
        sum += root.val;
        root.val = sum;
        convertBST1(root.left);
    }
}

回溯算法

组合(LeetCode77)

通过遍历将数字加入,递归完就将这个数字弹出。加入结果时,不能写成res.add(list);而必须写成res.add(new ArrayList<>(list))。res.add(list);是将list的地址加入,res.add(new ArrayList<>(list))表示加入一个新的对象。

class Solution {
    List<List<Integer>> res=new ArrayList<List<Integer>>();
    List<Integer> list=new ArrayList<Integer>();
    public List<List<Integer>> combine(int n, int k) {
        back(n,k,1);
        return res;
    }
    public void back(int n,int k,int startIndex){
        if(list.size()==k){
            res.add(new ArrayList<>(list));
            return;
        }
        for(int i=startIndex;i<=n;i++){
            list.add(i);
            back(n,k,i+1);
            list.remove(list.size()-1);
        }
    }
}

组合总和III(LeetCode216)

和上题一样,没什么说的,再加一个条件而已

class Solution {
    List<List<Integer>> res=new ArrayList<List<Integer>>();
    List<Integer> list=new ArrayList<Integer>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        back(n,k,1,0);
        return res;
    }
    public void back(int n,int k,int startIndex,int sum){
        if(list.size()==k && sum==n){
            res.add(new ArrayList<>(list));
            return;
        }
        for(int i=startIndex;i<=9;i++){
            list.add(i);
            sum+=i;
            back(n,k,i+1,sum);
            list.remove(list.size()-1);
            sum-=i;
        }
    }
}

电话号码的字母组合(LeetCode 17)

唯一的难点就是你能不能熟练使用Java字符串相关的函数。
 

class Solution {
    List<String> list = new ArrayList<>();

    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) {
            return list;
        }
        String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        backTracking(digits, numString, 0);
        return list;

    }
    StringBuilder temp = new StringBuilder();
    public void backTracking(String digits, String[] numString, int num) {
        if (num == digits.length()) {
            list.add(temp.toString());
            return;
        }
        String str = numString[digits.charAt(num) - '0'];
        for (int i = 0; i < str.length(); i++) {
            temp.append(str.charAt(i));
            backTracking(digits, numString, num + 1);
            temp.deleteCharAt(temp.length() - 1);
        }
    }
}

组合总和(LeetCode39)

思路没什么变化

class Solution {
    List<List<Integer>> res=new ArrayList<List<Integer>>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<Integer> list=new ArrayList<Integer>();
        back(candidates,target,list,0);
        return res;
    }

    public void back(int[] candidates,int target,List<Integer> list,int start){
        if(target==0){
            res.add(new ArrayList<Integer>(list));
            return;
        }
        for(int i=start;i<candidates.length;i++){
            if(target-candidates[i]>=0){
                list.add(candidates[i]);
                back(candidates,target-candidates[i],list,i);
                list.remove(list.size()-1);
            }
        }
    }
}

组合总和II(LeetCode40)

唯一的区别是要去重

//去重代码
if (i > startIndex && candidates[i] == candidates[i - 1]) continue;
class Solution {
    List<List<Integer>> res=new ArrayList<List<Integer>>();
    List<Integer> list=new ArrayList<Integer>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        back(candidates,target,0);
        return res;
    }

    public void back(int[] candidates,int target,int startIndex){
        if(target==0){
            res.add(new ArrayList<Integer>(list));
            return;
        }
        for(int i=startIndex;i<candidates.length;i++){
            if (i > startIndex && candidates[i] == candidates[i - 1]) continue;
            if(target-candidates[i]>=0){
                list.add(candidates[i]);
                back(candidates,target-candidates[i],i+1);
                list.remove(list.size()-1);
            }
        }
    }
}

分割回文串(LeetCode 131)

可以结合电话号码那道题,再添加一个判断回文子串的函数即可

class Solution {
    List<List<String>> lists = new ArrayList<>();
    Deque<String> deque = new LinkedList<>();

    public List<List<String>> partition(String s) {
        backTracking(s, 0);
        return lists;
    }

    private void backTracking(String s, int startIndex) {
        if (startIndex >= s.length()) {
            lists.add(new ArrayList(deque));
            return;
        }
        for (int i = startIndex; i < s.length(); i++) {
            if (isPalindrome(s, startIndex, i)) {
                String str = s.substring(startIndex, i + 1);
                deque.addLast(str);
            } else {
                continue;
            }
            backTracking(s, i + 1);
            deque.removeLast();
        }
    }
    private boolean isPalindrome(String s, int startIndex, int end) {
        for (int i = startIndex, j = end; i < j; i++, j--) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }
        return true;
    }
}

复原IP地址(LeetCode93)

class Solution {
    List<String> result = new ArrayList<>();
    public List<String> restoreIpAddresses(String s) {
        StringBuilder sb = new StringBuilder(s);
        backTracking(sb, 0, 0);
        return result;
    }
    private void backTracking(StringBuilder s, int startIndex, int dotCount){
        if(dotCount == 3){
            if(isValid(s, startIndex, s.length() - 1)){
                result.add(s.toString());
            }
            return;
        }
        for(int i = startIndex; i < s.length(); i++){
            if(isValid(s, startIndex, i)){
                s.insert(i + 1, '.');
                backTracking(s, i + 2, dotCount + 1);
                s.deleteCharAt(i + 1);
            }else{
                break;
            }
        }
    }

    private boolean isValid(StringBuilder s, int start, int end){
        if(start > end)
            return false;
        if(s.charAt(start) == '0' && start != end)
            return false;
        int num = 0;
        for(int i = start; i <= end; i++){
            int digit = s.charAt(i) - '0';
            num = num * 10 + digit;
            if(num > 255)
                return false;
        }
        return true;
    }
}

子集(LeetCode78)

子集问题只需要判断队列长度,这里空集合也要,所以索性不写

class Solution {
    List<List<Integer>> res=new ArrayList<List<Integer>>();
    List<Integer> list=new ArrayList<Integer>();
    public List<List<Integer>> subsets(int[] nums) {
        if(nums.length==0)  return res;
        back(nums,0);
        return res;
    }
    public void back(int[] nums,int startIndex){
        res.add(new ArrayList(list));
        for(int i=startIndex;i<nums.length;i++){
            list.add(nums[i]);
            back(nums,i+1);
            list.remove(list.size()-1);
        }
    }
}

子集II(LeetCode90)

去重即可

class Solution {
    List<List<Integer>> res=new ArrayList<List<Integer>>();
    List<Integer> list=new ArrayList<Integer>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        if(nums.length==0)  return res;
            back(nums,0);
        return res;
    }
     public void back(int[] nums,int startIndex){
        res.add(new ArrayList(list));
        for(int i=startIndex;i<nums.length;i++){
            if(i > startIndex && nums[i] == nums[i - 1])  continue;
            list.add(nums[i]);
            back(nums,i+1);
            list.remove(list.size()-1);
        }
    }
}

非递减子序列(LeetCode491)

在树同一层的元素不能使用,所以加一个HashSet来进行去重判断

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        backTracking(nums, 0);
        return result;
    }
    private void backTracking(int[] nums, int startIndex){
        if(path.size() >= 2)    result.add(new ArrayList<>(path));            
        HashSet<Integer> hs = new HashSet<>();
        for(int i = startIndex; i < nums.length; i++){
            if(!path.isEmpty() && path.get(path.size() -1 ) > nums[i] || hs.contains(nums[i]))
                continue;
            hs.add(nums[i]);
            path.add(nums[i]);
            backTracking(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
}

全排列(LeetCode46)

先确定一个,再将其他的进行排序。

//排序代码
if (used[i])    continue;
used[i] = true;
//回溯
used[i] = false;
class Solution {

    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    boolean[] used;
    public List<List<Integer>> permute(int[] nums) {
        if (nums.length == 0){
            return result;
        }
        used = new boolean[nums.length];
        permuteHelper(nums);
        return result;
    }

    private void permuteHelper(int[] nums){
        if (path.size() == nums.length){
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++){
            if (used[i]){
                continue;
            }
            used[i] = true;
            path.add(nums[i]);
            permuteHelper(nums);
            path.removeLast();
            used[i] = false;
        }
    }
}

全排序II(LeetCode47)

去重即可

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        boolean[] used = new boolean[nums.length];
        Arrays.fill(used, false);
        Arrays.sort(nums);
        backTrack(nums, used);
        return result;
    }

    private void backTrack(int[] nums, boolean[] used) {
        if (path.size() == nums.length) {
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
                continue;
            }
            if (used[i] == false) {
                used[i] = true;
                path.add(nums[i]);
                backTrack(nums, used);
                path.remove(path.size() - 1);
                used[i] = false;
            }
        }
    }
}

贪心算法

分发饼干(LeetCode455)

局部最优解:一块饼干尽量喂给胃口大的

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        int i=g.length-1,j=s.length-1;
        Arrays.sort(g);Arrays.sort(s);
        int num=0;
        while(i>=0&&j>=0){
            if(s[j]>=g[i]){
                num++;
                i--;j--;
            }
            else{
                i--;
            }
        }
        return num;
    }
}

精简之后:

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        int i=g.length-1,j=s.length-1;
        Arrays.sort(g);Arrays.sort(s);
        int num=0;
        while(i>=0&&j>=0){
            if(s[j]>=g[i]){
                num++;
                j--;
            }
            i--;
        }
        return num;
    }
}

摆动序列(LeetCode376)

局部最优:删除单调坡度上的节点;整体最优:整个序列有最多的局部峰值即最长摆动序列

curDiff:当前落差;preDiff:前一个节点落差。如果满足条件就更新

class Solution {
    public int wiggleMaxLength(int[] nums) {
        if (nums.length <= 1) {
            return nums.length;
        }
        int curDiff = 0;
        int preDiff = 0;
        int count = 1;
        for (int i = 1; i < nums.length; i++) {
            curDiff = nums[i] - nums[i - 1];
            //等于0的情况表示初始时的preDiff
            if ((curDiff > 0 && preDiff <= 0) || (curDiff < 0 && preDiff >= 0)) {
                count++;
                preDiff = curDiff;
            }
        }
        return count;
    }
}

最大子数组和(LeetCode53)

一看就会,一做就废。。。

局部最优解:区间和大于0就继续,否则就跳过。全局最优解:多个大于0的局部最优解的加和

class Solution {
    public int maxSubArray(int[] nums) {
        int ans = Integer.MIN_VALUE;
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        ans = dp[0];

        for (int i = 1; i < nums.length; i++){
            dp[i] = Math.max(dp[i-1] + nums[i], nums[i]);
            ans = Math.max(dp[i], ans);
        }

        return ans;
    }
}

买卖股票的最佳时机II(LeetCode122)

把许多天拆分成相邻的两天,局部最优:收集每天的正利润,全局最优:求得最大利润。

class Solution {
    public int maxProfit(int[] prices) {
        int result = 0;
        for (int i = 1; i < prices.length; i++) {
            result += Math.max(prices[i] - prices[i - 1], 0);
        }
        return result;
    }
}

跳跃游戏(LeetCode55)

不要纠结应该跳几步,找出能跳的范围,观察这个范围能不能包含终点

局部最优解:当前能覆盖的最大区间;全局最优解:能不能覆盖到终点

class Solution {
    public boolean canJump(int[] nums) {
        int coverRange=0;
        for(int i=0;i<=coverRange;i++){
            coverRange=Math.max(coverRange,nums[i]+i);
            if(coverRange>=nums.length-1)   return true;
        }
        return false;
    }
}

跳远游戏II(LeetCode45)

局部最优解:当前能覆盖的最大区间;全局最优解:最少用多少个区间表示全局

class Solution {
    public int jump(int[] nums) {
       if (nums==null||nums.length<=1)  return 0;
       int count=0,curDistance=0,maxDistance=0;
       for(int i=0;i<nums.length;i++){
           maxDistance=Math.max(i+nums[i],maxDistance);
           if (maxDistance>=nums.length-1){
                count++;
                break;
            }
            if (i==curDistance){
                curDistance = maxDistance;
                count++;
            }
       }     
        return count;
    }
}

K次取反后最大化的数组和(LeetCode1005)

局部最优解:先将负数转变为正数;全局最优解:把负数变为正数后减去最小的数

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        Arrays.sort(nums);
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] < 0 && k > 0) {
                nums[i] = -1 * nums[i];
                k--;
            }
            sum += nums[i];
        }
        Arrays.sort(nums);
        return sum - (k % 2 == 0 ? 0 : 2 * nums[0]); 
    }
}

加油站(LeetCode134)

个人感觉这道题和贪心没关系,如果sum>0,而这个节点的左边是<0的,那么右边就应该>0。正确的答案就应该在右边找。

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int curSum = 0;
        int totalSum = 0;
        int index = 0;
        for (int i = 0; i < gas.length; i++) {
            curSum += gas[i] - cost[i];
            totalSum += gas[i] - cost[i];
            if (curSum < 0) {
                index = (i + 1) % gas.length ; 
                curSum = 0;
            }
        }
        if (totalSum < 0) return -1;
        return index;
    }
}

动态规划

当前状态由之前状态演变 记住这句话,好好体会

dp五步走:

1.确定dp数组以及下标的含义

2.确定递推公式
3.dp数组如何初始化
4.确定遍历顺序
5.举例推导dp数组

斐波那契数(LeetCode509)

class Solution {
    public int fib(int n) {
        int[] dp=new int[n+2];
        dp[0]=0;dp[1]=1;
        for(int i=2;i<=n;++i){
            dp[i]=dp[i-1]+dp[i-2];
        } 
        return dp[n]; 
    }
}

爬楼梯(LeetCode70)

class Solution {
    public int fib(int n) {
        int[] dp=new int[n+2];
        dp[0]=0;dp[1]=1;
        for(int i=2;i<=n;++i){
            dp[i]=dp[i-1]+dp[i-2];
        } 
        return dp[n]; 
    }
}

使用最小的花费爬楼梯(LeetCode746)

比较最后两个的值,因为最后两个空都可以跳出去

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int n=cost.length;
        int[] dp =new int[n+2];
        dp[0]=cost[0];dp[1]=cost[1];
        for(int i=2;i<n;++i){
            dp[i]=Math.min(dp[i-2],dp[i-1])+cost[i];
        } 
        return Math.min(dp[n-2],dp[n-1]);
    }
}

不同路径(LeetCode62)

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp=new int[m+2][n+2];
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                dp[1][j]=1;
                dp[i][1]=1;    
                if(i!=1&&j!=1) dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }
        } 
        return dp[m][n];
    }
}

不同路径II(LeetCode63)

初始化的时候要注意,遇到障碍后面就不能初始化了

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];
        if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1) return 0;
        for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
            dp[i][0] = 1;
        }
        for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {
            dp[0][j] = 1;
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = (obstacleGrid[i][j] == 0) ? dp[i - 1][j] + dp[i][j - 1] : 0;
            }
        }
        return dp[m - 1][n - 1];
    }
}

整数拆分(LeetCode343)

当前的最大乘积=之前两个数字的最大乘积相乘

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n+1];
        dp[2] = 1;
        for(int i = 3; i <= n; i++) {
            for(int j = 1; j <= i-j; j++) {
                dp[i] = Math.max(dp[i], Math.max(j*(i-j), j*dp[i-j]));
            }
        }
        return dp[n];
    }
}

01背包

结果逼近期望值的最佳组合 记住这句话,好好体会

代码

public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){
        int wLen = weight.length;
        int[] dp = new int[bagWeight + 1];
        for (int i = 0; i < wLen; i++){
            for (int j = bagWeight; j >= weight[i]; j--){
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
    }

分割等和子集(LeetCode416)

class Solution {
    public boolean canPartition(int[] nums) {
        int sum=0;
        for(int num:nums){
            sum+=num;
        }
        if(sum%2!=0)    return false;
        sum/=2;
        int[] dp=new int[sum+1]; 
        for(int i=0;i<nums.length;i++){
            for(int j=sum;j>=nums[i];j--){
                dp[j]=Math.max(dp[j],dp[j-nums[i]]+nums[i]);
            }
            if(dp[sum]==sum)   return true;
        }
        return dp[sum]==sum;
    }
}

最后一块石头的重量||(LeetCode1049)

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum=0;
        for(int num:stones){
            sum+=num;
        }
        int target=sum>>1;
        int[] dp=new int[target+2];
        for(int i=0;i<stones.length;i++){
            for(int j=target;j>=stones[i];j--){
                dp[j]=Math.max(dp[j],dp[j-stones[i]]+stones[i]);
            }
        }
        return sum-2*dp[target];
    }
}

目标和(LeetCode494)

把递归树画出来,就知道这道题用回溯可以解决。时间复杂度太高,不推荐

//回溯法,不推荐
class Solution {
    int count=0;
    public int findTargetSumWays(int[] nums, int target) {
        back(nums,target,0);
        return count;
    }
    public void back(int[] nums,int target,int startIndex){
        if(startIndex==nums.length&&target==0)  count++;
        if(startIndex<nums.length){
            back(nums,target-nums[startIndex],startIndex+1);
            back(nums,target+nums[startIndex],startIndex+1);
            }
        }
}

因为每个数只出现一次,可以把他看作是一个找和为(sum-target)/2的01背包问题

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        int diff = sum - target;
        if (diff < 0 || diff % 2 != 0) {
            return 0;
        }
        int neg = diff / 2;
        int[] dp = new int[neg + 1];
        dp[0] = 1;
        for (int num : nums) {
            for (int j = neg; j >= num; j--) {
                dp[j] += dp[j - num];
            }
        }
        return dp[neg];
    }
}

完全背包

代码

外物品内背包(组合数):

 for (int i = 0; i < weight.length; i++){ // 遍历物品
        for (int j = weight[i]; j <= bagWeight; j++){ // 遍历背包容量
            dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }

外背包内物品(排列数):

for (int i = 1; i <= bagWeight; i++){ // 遍历背包容量
        for (int j = 0; j < weight.length; j++){ // 遍历物品
            if (i - weight[j] >= 0){
                dp[i] = Math.max(dp[i], dp[i - weight[j]] + value[j]);
            }
        }
    }

零钱兑换||(LeetCode518)

递归表达式参考数梯子那题

class Solution {
    public int change(int amount, int[] coins) {
        int[] dp=new int[amount+2];
        dp[0]=1; 
        for(int i=0;i<coins.length;i++){
            for(int j=coins[i];j<=amount;j++){
                dp[j]+=dp[j-coins[i]];
            }
        }
        return dp[amount];
    }
}

组合总和IV(LeetCode377)

如果求组合数就是外层for循环遍历物品,内层for遍历背包

如果求排列数就是外层for遍历背包,内层for循环遍历物品

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target + 1];
        dp[0] = 1;
        for (int i = 0; i <= target; i++) {
            for (int j = 0; j < nums.length; j++) {
                if (i >= nums[j]) {
                    dp[i] += dp[i - nums[j]];
                }
            }
        }
        return dp[target];
    }
}

零钱兑换(LeetCode322)

class Solution {
    public int coinChange(int[] coins, int amount) {
        int max = Integer.MAX_VALUE;
        int[] dp = new int[amount + 1];
        Arrays.fill(dp,max);
        dp[0] = 0;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                if (dp[j - coins[i]] != max) {
                    dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
                }
            }
        }
        return dp[amount] == max ? -1 : dp[amount];
    }
}

完全平方数(LeetCode279)

和零钱问题一样,都是找满足固定值的条件

class Solution {
    public int numSquares(int n) {
        int[] dp=new int[n+2];
        Arrays.fill(dp,n+1);
        dp[0]=0;
        for(int i=0;i*i<=n;i++){
            for(int j=i*i;j<=n;j++){
                dp[j]=Math.min(dp[j],dp[j-i*i]+1);
            }
        }
        return dp[n];
    }
}

单词拆分(LeetCode139)

两次都没写对

public class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> wordDictSet = new HashSet(wordDict);
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}

打家劫舍(LeetCode198)

判断当前抢还是不抢即可

class Solution {
    public int rob(int[] nums) {
        int[] dp=new int[nums.length];
        if(nums.length==1)  return nums[0];
        dp[0]=nums[0];dp[1]=Math.max(nums[0],nums[1]);
        for(int i=2;i<nums.length;i++){
            dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]);
        }
        return dp[nums.length-1];
    }
}

打家劫舍II(LeetCode213)

需要考虑成环的问题。可以将问题转化为比较nums[0]+dp[2~n-2]和dp[1~n-1]

class Solution {
    public int rob(int[] nums) {
        int n=nums.length;
        if(n==1)    return nums[0];
        if(n==2)    return Math.max(nums[0],nums[1]);
        return Math.max(f(nums,1,n),f(nums,2,n-1)+nums[0]);
    }
    public int f(int[] nums,int start,int end){
        int[] dp=new int[end+1];
        dp[start-1]=0;
        dp[start]=nums[start];
        for(int i=start+1;i<end;i++){
            dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]);
        }
        return dp[end-1];
    }
}

打家劫舍|||(LeetCode337)

状态机问题,用0,1表示状态。0为不偷,1为偷。

当前节点选择不偷:当前节点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱
当前节点选择偷:当前节点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前节点的钱数

class Solution {
   public int rob(TreeNode root) {
    int[] result = robInternal(root);
    return Math.max(result[0], result[1]);
}

public int[] robInternal(TreeNode root) {
    if (root == null) return new int[2];
    int[] result = new int[2];

    int[] left = robInternal(root.left);
    int[] right = robInternal(root.right);

    result[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
    result[1] = left[0] + right[0] + root.val;

    return result;
}
}

买卖股票的最佳时机(LeetCode121)

用res记录利润,遇到比价格低的就更新最低值,边遍历边计算利润

class Solution {
    public int maxProfit(int[] prices) {
        int low = Integer.MAX_VALUE;
        int res = 0;
        for(int i = 0; i < prices.length; i++){
            low = Math.min(prices[i], low);
            res = Math.max(prices[i] - low, res);
        }
        return res;
    }
}

买卖股票的最佳时机||(LeetCode122)

个人觉得这道题比较简单,只要第二天的价格比今天高就可以买入。运用了贪心的思想

class Solution {
    public int maxProfit(int[] prices) {
        int res=0;
        for(int i=0;i<prices.length-1;i++){
            if(prices[i]<prices[i+1])   res+=prices[i+1]-prices[i];
        }
        return res;
    }
}

买卖股票的最佳时机|||(LeetCode123)

//容易理解的版本,基于买卖股票1
class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int buy1 = -prices[0], sell1 = 0;
        int buy2 = -prices[0], sell2 = 0;
        for (int i = 1; i < n; ++i) {
            buy1 = Math.max(buy1, -prices[i]);
            sell1 = Math.max(sell1, buy1 + prices[i]);
            buy2 = Math.max(buy2, sell1 - prices[i]);
            sell2 = Math.max(sell2, buy2 + prices[i]);
        }
        return sell2;
    }
}
//更加模板化的解法,虽然看起来只是表示方式不一样
//但是这个是状态问题最标准的解法,用二维,一个表示状态一个用来dp

class Solution {
    public int maxProfit(int[] prices) {
        int[] dp = new int[4]; 
        // 存储两次交易的状态就行了
        // dp[0]代表第一次交易的买入
        dp[0] = -prices[0];
        // dp[1]代表第一次交易的卖出
        dp[1] = 0;
        // dp[2]代表第二次交易的买入
        dp[2] = -prices[0];
        // dp[3]代表第二次交易的卖出
        dp[3] = 0;
        for(int i = 1; i <= prices.length; i++){
            // 要么保持不变,要么没有就买,有了就卖
            dp[0] = Math.max(dp[0], -prices[i-1]);
            dp[1] = Math.max(dp[1], dp[0]+prices[i-1]);
            // 这已经是第二次交易了,所以得加上前一次交易卖出去的收获
            dp[2] = Math.max(dp[2], dp[1]-prices[i-1]);
            dp[3] = Math.max(dp[3], dp[2]+ prices[i-1]);
        }
        return dp[3];
    }
}

买卖股票的最佳时机IV

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艺成超爱牛肉爆大虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值