LeetCode高频200题练习记录

发布时间:2023-06-12 08:00

LeetCode必刷200题练习记录

一、数据结构相关

1. 链表

  • 1.相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。
如果两个链表没有交点,返回 null 。
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //处理特殊输入
        if(headA == null || headB == null)
            return null;
        //双指针遍历
        ListNode pA = headA;
        ListNode pB = headB;
        
        while(pA != pB){
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }

        return pA;
    }
}

思路:

假设链表A的长度为a,链表B的长度为b,公共部分长度为c,相交节点为Node。指针pA先遍历完A链再遍历B链,走到Node时,走过的长度为a+(b-c)。同理对于pB的话就是b+(a-c)。对于a+(b-c)=b+(a-c),若有相交节点Node,则c>0,pA和pB会指向同一个节点Node;若Node不存在,则c=0,两个指针都会指向null,循环也会结束。

  • 2.翻转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yWIPR7du-1630017125169)(C:\Users\11482\Desktop\找工作相关\LeetCode必刷200题练习记录.assets\1479f99ec2d8583971cc3dfb0c59e0cb.png)]

//迭代法
class Solution {
    public ListNode reverseList(ListNode head) {
        //处理特殊输入
        //head为空或者head.next为空直接返回 无需翻转
        if(head == null || head.next == null)
            return head;
        
        //指针定义在外部 避免频繁创建 对内存空间的占用    
        ListNode pre = null;
        ListNode nxt = null;
        ListNode cur = head;
        while(cur != null){
            nxt = cur.next;
            cur.next = pre;
            pre = cur;
            cur = nxt;
        }
        return pre;
    }
}
//递归法
class Solution {
    public ListNode reverseList(ListNode head) {
        //处理特殊输入
        //head为空或者head.next为空直接返回 无需翻转
        if(head == null || head.next == null)
            return head;
        
        ListNode newHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }
}
  • 3.合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //处理特殊输入
        if(l1 == null) return l2;
        if(l2 == null) return l1;

        ListNode dummyHead = new ListNode(0);
        ListNode p1 = l1;
        ListNode p2 = l2;
        int val = 0;
        ListNode cur = dummyHead;
        while(p1 != null && p2 != null){
            if(p1.val < p2.val){
                val = p1.val;
                p1 = p1.next;
            } else {
                val = p2.val;
                p2 = p2.next;
            }
            cur.next = new ListNode(val);
            cur = cur.next;
        }
        while(p1 != null){
            cur.next = new ListNode(p1.val);
            cur = cur.next;
            p1 = p1.next;
        }
        while(p2 != null){
            cur.next = new ListNode(p2.val);
            cur = cur.next;
            p2 = p2.next;
        }
        return dummyHead.next;
    }
}
  • 4.删除排序链表中的重复元素
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。

返回同样按升序排列的结果链表。
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null)
            return head;
        ListNode cur = head;
        while(cur.next != null){
            if(cur.next.val == cur.val){
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return head;
    }
}
  • 5.删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

进阶:你能尝试使用一趟扫描实现吗?
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        //处理特殊输入 暂时没想到

        //快慢指针
        ListNode fast = head;
        ListNode slow = head;

        while(n > 0){
            fast = fast.next;
            n--;
        }
        // n 大于等于 链表长了
        if(fast == null){
            return head.next;
        }

        while(fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return head;
    }
}
  • 6.两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
//迭代
class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null)
            return head;
        ListNode dummyhead = new ListNode(-1);
        dummyhead.next = head;
        ListNode cur = dummyhead;
        while(cur.next != null && cur.next.next != null){
            ListNode first = cur.next;
            ListNode last = cur.next.next;
            first.next = last.next;
            cur.next = last;
            last.next = first;
            cur = cur.next.next;
        }
        return dummyhead.next;
    }
}
//递归
//还没有理解  先空着
  • 7.两数相加II
给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        Deque<Integer> q1 = new ArrayDeque<>();
        Deque<Integer> q2 = new ArrayDeque<>();
        
        while(l1 != null){
            q1.push(l1.val);
            l1 = l1.next;
        }

        while(l2 != null){
            q2.push(l2.val);
            l2 = l2.next;
        }

        ListNode res = null;
        int num1 = 0;
        int num2 = 0;
        int temp = 0;
        int flag = 0;
        while(!q1.isEmpty() || !q2.isEmpty() || flag != 0){
            num1 = q1.isEmpty() ? 0 : q1.pop();
            num2 = q2.isEmpty() ? 0 : q2.pop();
            temp = num1 + num2 + flag;
            ListNode node = new ListNode(temp % 10);
            node.next = res;
            res = node;
            flag = temp / 10;
        }
        return res;
    }
}
  • 8.回文链表
请判断一个链表是否为回文链表。
class Solution {
    ListNode temp = null;
    public boolean isPalindrome(ListNode head) {
        //如果回文 则链表长一定是偶数 嘛?  - 不一定
        //1 2 3 2 1
        //1 2 3 3 2 1
        
        temp = head;
        return check(head);
    }

    public boolean check(ListNode node){
        if(node == null)
            return true;
        boolean res = check(node.next) && (temp.val == node.val);
        temp = temp.next;
        return res;
    }
}
  • 9.奇偶链表
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。
class Solution {
    public ListNode oddEvenList(ListNode head) {
        if(head == null || head.next == null)
            return head;
        ListNode odd = head;
        ListNode even = head.next;
        ListNode end = even;
        while(odd.next != null && even.next != null){
            odd.next = even.next;
            odd = odd.next;
            even.next = odd.next;
            even = even.next;
        }
        odd.next = end;
        return head;
    }
}
  • 10.K个一组反转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode tail = head;
        for(int i = 0; i < k; i++){
            if(tail == null){
                return head;
            }
            tail = tail.next;
        }
        ListNode newHead = reverse(head, tail);
        head.next = reverseKGroup(tail, k);
        return newHead;
    }

    public ListNode reverse(ListNode head, ListNode tail){
        ListNode pre = null;
        ListNode nxt = null;
        while(head != tail){
            nxt = head.next;
            head.next = pre;
            pre = head;
            head = nxt;
        }
        return pre;
    }
}

2. 树

  • 1、前序遍历
class Solution {
    List<Integer> res = new ArrayList<>();
    public List<Integer> preorderTraversal(TreeNode root) {
        preOrder(root);
        return res;
    }
    public void preOrder(TreeNode node){
        if(node != null){
            res.add(node.val);
            preOrder(node.left);
            preOrder(node.right);
        }
    }
}
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        preOrder(root, res);
        return res;
    }
    public void preOrder(TreeNode root, List<Integer> res){
        Stack<TreeNode> stack = new Stack<>();
        while(root != null || !stack.isEmpty()){
            while(root != null){
                res.add(root.val);
                stack.add(root);
                root = root.left;
            }
            root = stack.pop().right;
        }
    }
}
  • 2、中序遍历
class Solution {
    List<Integer> res = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        //inorder1(root);
        inorder2(root);
        return res;
    }

    //递归法
    public void inorder1(TreeNode root){
        //递归终止条件
        if(root == null) return;
        inorder1(root.left);
        res.add(root.val);
        inorder1(root.right);
    }

    //迭代法
    public void inorder2(TreeNode root){
        Stack<TreeNode> stack = new Stack<>();
        while(root != null || !stack.isEmpty()){
            while(root != null){
                stack.push(root);
                root = root.left;
            }
            TreeNode t = stack.pop();
            res.add(t.val);
            root = t.right;
        }
    }
}
  • 3、后序遍历
class Solution {
    
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        postOrder(root, res);
        Collections.reverse(res);
        return res;
    }
    public void postOrder(TreeNode root, List<Integer> res){
        Stack<TreeNode> stack = new Stack<>();
        while(root != null || ! stack.isEmpty()){
            while(root != null){
                res.add(root.val);
                stack.push(root);
                root = root.right;
            }
            root = stack.pop().left;
        }
    }
}
class Solution {
    List<Integer> res = new ArrayList<>();
    public List<Integer> postorderTraversal(TreeNode root) {
        help(root);
        return res;
    }
    public void help(TreeNode node){
        if(node == null)
            return;
        help(node.left);
        help(node.right);

        res.add(node.val);
    }
}
  • 4、层序遍历
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        if(root == null)
            return new ArrayList<>();

        List<List<Integer>> res = new ArrayList<>();
        Queue<TreeNode> q = new LinkedList<>();

        q.offer(root);
        int size = 0;
        List<Integer> list;
        while(!q.isEmpty()){
            list = new ArrayList<>();
            size = q.size();
            while(size > 0){
                TreeNode node = q.poll();
                list.add(node.val);
                if(node.left != null)
                    q.offer(node.left);
                if(node.right != null)
                    q.offer(node.right);
                size--;
            }
            res.add(list);
        }
        return res;
    }
}
  • 5.验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
class Solution {
    public boolean isValidBST(TreeNode root) {
        return cheak(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }

    public boolean cheak(TreeNode root, long min, long max){
        if(root == null)
            return true;
        if(root.val <= min || root.val >= max)
            return false;
        return cheak(root.left, min, root.val) && cheak(root.right, root.val, max);
    }
}
  • 6.恢复二叉搜索时
给你二叉搜索树的根节点 root ,该树中的两个节点被错误地交换。请在不改变其结构的情况下,恢复这棵树。
class Solution {
    TreeNode t1, t2, pre;
    public void recoverTree(TreeNode root) {
        inorder(root);
        int temp = t1.val;
        t1.val = t2.val;
        t2.val = temp;
    }

    public void inorder(TreeNode root){
        if(root == null) return;
        inorder(root.left);
        if(pre != null && pre.val > root.val){
            if(t1 == null) t1 = pre;
            t2 = root;
        }
        pre = root;
        inorder(root.right);
    }
    
}
  • 7.相同的树
class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p == null && q == null)
            return true;
        if(p == null || q == null)
            return false;
        if(p.val != q.val)
            return false;
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
}
  • 8.对称二叉树
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null)
            return true;
        return cheak(root.left, root.right);
    }

    public boolean cheak(TreeNode node1, TreeNode node2){
        if(node1 == null && node2 == null)
            return true;
        if(node1 == null || node2 == null)
            return false;
        if(node1.val != node2.val)
            return false;
        return cheak(node1.left, node2.right) && cheak(node1.right, node2.left);
    }
}
  • 9.二叉树的最大深度
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null)
            return 0;
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}
  • 10.从前序与中序遍历序列构造二叉树
class Solution {
    int[] arr;
    Map<Integer, Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        arr = preorder;
        for(int i = 0; i < inorder.length; i++){
            map.put(inorder[i], i);
        }
        return build(0, 0, preorder.length - 1);
        
    }

    public TreeNode build(int rootIndex, int left, int right){
        if(left > right) return null;
        TreeNode root = new TreeNode(arr[rootIndex]);
        int index = map.get(arr[rootIndex]);
        root.left = build(rootIndex + 1, left, index - 1);
        root.right = build(rootIndex + index - left + 1, index + 1, right);
        return root;
    }
}
  • 11.从后序与中序遍历序列构造二叉树
class Solution {
    int[] arr;
    Map<Integer, Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        arr = postorder;
        for(int i = 0; i < inorder.length; i++){
            map.put(inorder[i], i);
        }
        return build(postorder.length - 1, 0, inorder.length - 1);
    }

    public TreeNode build(int rootIndex, int left, int right){
        if(left > right) return null;
        TreeNode root = new TreeNode(arr[rootIndex]);
        int index = map.get(arr[rootIndex]);
        root.left = build(rootIndex - (right - index) - 1, left, index - 1);
        root.right = build(rootIndex - 1, index + 1, right);
        return root;
    }
}
  • 12.将有序数组转化为二叉搜索树
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        return build(nums, left, right);
    }

    public TreeNode build(int[] nums, int left, int right){
        if(left > right) return null;
        int mid = (left + right) >> 1;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = build(nums, left, mid - 1);
        root.right = build(nums, mid + 1, right);
        return root;
    }
}
  • 13.有序链表转换二叉搜索树
class Solution {
    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){
            fast = fast.next.next;
            pre = slow;
            slow = slow.next;
        }
        pre.next = null;
        TreeNode root = new TreeNode(slow.val);
        root.left = sortedListToBST(head);
        root.right = sortedListToBST(slow.next);

        return root;
    }
}
  • 14.平衡二叉树
class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null)
            return true;
        if(Math.abs(depth(root.left) - depth(root.right)) > 1)
            return false;
        return isBalanced(root.left) && isBalanced(root.right);
    }

    public int depth(TreeNode root){
        if(root == null)
            return 0;
        return Math.max(depth(root.left), depth(root.right)) + 1;
    }
}
  • 15.二叉树的最小深度
class Solution {
    public int minDepth(TreeNode root) {
        if(root == null)
            return 0;
        int left = minDepth(root.left);
        int right = minDepth(root.right);

        if(left == 0) return right + 1;
        if(right == 0) return left + 1;

        return Math.min(left, right) + 1;
    } 
}
  • 16.路径总和
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。

叶子节点 是指没有子节点的节点。
class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root == null) 
            return false;
        if(root.left == null && root.right == null)
            return targetSum - root.val == 0;
        return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
    }
}
  • 17.路径总和II
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        dfs(root, targetSum, new ArrayList<>());
        return res;
    }

    public void dfs(TreeNode root, int targetSum, List<Integer> list){
        if(root == null) return;
        list.add(root.val);
        if(root.left == null && root.right == null){
            if(targetSum == root.val){
                res.add(new ArrayList<>(list));
            }
        }
        if(root.left != null){
            dfs(root.left, targetSum - root.val, list);
        }
        if(root.right != null){
            dfs(root.right, targetSum - root.val, list);
        }
        list.remove(list.size() - 1);
    }
}
  • 18.二叉树展开为链表
class Solution {
    public void flatten(TreeNode root) {
        if(root == null) return ;
        //左右子树拉直
        flatten(root.left);
        flatten(root.right);
        //左子树
        TreeNode left = root.left;
        //右子树
        TreeNode right = root.right;

        root.right = left;
        root.left = null;
        while(root.right != null){
            root = root.right;
        }
        root.right = right;
    }
}
  • 19.求根节点到叶节点数字之和
class Solution {
    int res = 0;
    public int sumNumbers(TreeNode root) {
        dfs(root, 0);
        return res;
    }

    public void dfs(TreeNode root, int sum){
        if(root == null) return;
        int k = (sum * 10 + root.val);
        if(root.left == null && root.right == null){
            res += k;
        }
        dfs(root.left, k);
        dfs(root.right, k);
    }
}
  • 20.二叉树的所有路径
class Solution {
    List<String> res = new ArrayList<>();
    public List<String> binaryTreePaths(TreeNode root) {
        dfs(root,"");
        return res;
    }
    public void dfs(TreeNode root, String s){
        if(root == null) return;        
        s += root.val;
        if(root.left == null && root.right == null){
            res.add(s);
        }
        dfs(root.left, s + "->");
        dfs(root.right, s + "->");
    }
}
  • 21.二叉树的最近公共祖先
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || p == root || q == root){
            return root;
        }
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if(left != null && right != null){
            return root;
        }
        return left == null ? right : left;
    }
}
  • 22.二叉搜索树的最近公共祖先

  • 23.翻转二叉树

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return null;
        invertTree(root.left);
        invertTree(root.right);
        TreeNode right = root.right;
        TreeNode left = root.left;
        root.left = right;
        root.right = left;
        return root;
    }
}
  • 24.合并二叉树
class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1 == null)
            return root2;
        if(root2 == null)
            return root1;
        root1.val += root2.val;
        root1.left = mergeTrees(root1.left, root2.left);
        root1.right = mergeTrees(root1.right, root2.right);
        return root1;
    }
}
  • 25.二叉树的直径***
class Solution {
    int max = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        dfs(root);
        return max;
    }

    public int dfs(TreeNode root){
        int left = root.left == null ? 0 : dfs(root.left) + 1;
        int right = root.right == null ? 0 : dfs(root.right) + 1;
        max = Math.max(max, left + right);
        return Math.max(left, right);
    }
}
  • 26.二叉树中最大的路径和***
class Solution {
    int max = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        if(root == null)
            return 0;
        dfs(root);
        return max;
    }
    public int dfs(TreeNode root){
        if(root == null) return 0;
        int left = Math.max(0, dfs(root.left));
        int right = Math.max(0, dfs(root.right));
        max = Math.max(max, left + right + root.val);
        return Math.max(left, right) + root.val;
    }
}
  • 27.二叉树的序列化与反序列化
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        //bfs 层序
        StringBuilder builder = new StringBuilder();
        Queue<TreeNode> q = new LinkedList<>();
        if(root == null){
            return "null";
        }
        q.offer(root);
        builder.append(root.val).append(",");
        while(!q.isEmpty()){
            TreeNode node = q.poll();
            if(node.left != null){
                q.offer(node.left);
                builder.append(node.left.val).append(",");
            } else {
                builder.append("null").append(",");
            }
            if(node.right != null){
                q.offer(node.right);
                builder.append(node.right.val).append(",");
            } else {
                builder.append("null").append(",");
            }
        }
        builder.deleteCharAt(builder.length() - 1);
        return builder.toString();
    }
    //suppose to be [1, 2, 3, null, null, 4, 5];

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        String[] nodes = data.split(",");
        TreeNode root = nodes[0].equals("null") ? null : new TreeNode(Integer.parseInt(nodes[0]));
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        int i = 1;
        while(i < nodes.length){
            TreeNode node = q.poll();
            if(nodes[i].equals("null")){
                node.left = null;
            } else {
                node.left = new TreeNode(Integer.parseInt(nodes[i]));
                q.offer(node.left);
            }
            i++;
            if(nodes[i].equals("null")){
                node.right = null;
            } else {
                node.right = new TreeNode(Integer.parseInt(nodes[i]));
                q.offer(node.right);
            }
            i++;

        }
        return root;
    }
}

// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));

二、常见算法

1. 回溯算法

  • 1.电话号码的字母组合
class Solution {
    String[] dic = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    List<String> res = new ArrayList<>();
    public List<String> letterCombinations(String digits) {
        if(digits == null || digits.length() == 0){
            return new ArrayList<>();
        }
        backtrace(digits, 0, "");
        return res;
    }

    public void backtrace(String digits, int index, String path){
        if(index == digits.length()){
            res.add(path);
            return;
        }
        String temp = dic[digits.charAt(index) - '0'];
        for(char c : temp.toCharArray()){
            path += c;
            backtrace(digits, index + 1, path);
            path = path.substring(0, path.length() - 1);
        }
        
    }
}
  • 2.括号生成
class Solution {
    char[] t = {'(' , ')'};
    List<String> res = new ArrayList<>();
    int[] count = new int[2];
    public List<String> generateParenthesis(int n) {
        backtrace(n, "");
        return res;
    }

    
    public void backtrace(int n, String path){
        if(path.length() == (2 * n)){
            res.add(path);
            return;
        }
        for(int i = 0; i < t.length; i++){
            if(i == 0 && count[i] < n){
                count[i]++;
            } else if (i == 1 && count[i - 1] > 0 && count[i] < count[i - 1]){
                count[i]++;    
            } else {
                continue;
            }
            path += t[i];
            backtrace(n, path);
            path = path.substring(0, path.length() - 1);
            count[i]--;
        }
    }
}
  • 3.解数独
class Solution {
    public void solveSudoku(char[][] board) {
        backtrace(board);
    }

    public boolean backtrace(char[][] board){
        for(int i = 0; i < board.length; i++){
            for(int j = 0; j < board[0].length; j++){
                if(board[i][j] != '.'){
                    continue;
                }
                for(char c = '1'; c <= '9'; c++){
                    if(check(board, i, j, c)){
                        board[i][j] = c;
                        if(backtrace(board)) return true;
                        board[i][j] = '.';
                    }
                }
                return false;
            }
        }
        return true;
    }

    public boolean check(char[][] board, int row, int col, char c){
        //检查竖列有无重复
        for(int i = 0; i < board.length; i++){
            if(board[i][col] == c){
                return false;
            }
        }
        //检查行重复
        for(int i = 0; i < board.length; i++){
            if(board[row][i] == c){
                return false;
            }
        }
        //检查3*3内有无重复
        int startRow = (row / 3) * 3;
        int startCol = (col / 3) * 3;
        for(int i = startRow; i < startRow + 3; i++){
            for(int j = startCol; j < startCol + 3; j++){
                if(board[i][j] == c){
                    return false;
                }
            }
        }
        return true;
    }
}
  • 4.组合总和
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。

candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。 

对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        backtrace(candidates, target, new ArrayList<>(), 0);
        return res;
    }

    public void backtrace(int[] candidates, int target, List<Integer> list, int index){
        if(target == 0){
            res.add(new ArrayList<>(list));
            return;
        }

        for(int i = index; i < candidates.length; i++){
            if(candidates[i] > target){
                continue;
            }
            list.add(candidates[i]);
            backtrace(candidates, target - candidates[i], list, i);
            list.remove(list.size() - 1);
        }
    }
}
  • 5.组合总和II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

注意:解集不能包含重复的组合。 
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtrace(candidates, target, new ArrayList<>(), 0);
        return res;
    }

    public void backtrace(int[] candidates, int target, List<Integer> list, int index){
        if(target == 0){
            res.add(new ArrayList<>(list));
            return;
        }
        for(int i = index; i < candidates.length; i++){
            if(candidates[i] > target) continue;
            if(i > index && candidates[i] == candidates[i - 1]){
                continue;
            }
            list.add(candidates[i]);
            backtrace(candidates, target - candidates[i], list, i + 1);
            list.remove(list.size() - 1);
        }
    }
}
  • 6.全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> permute(int[] nums) {
        boolean[] visited = new boolean[nums.length];
        backtrace(nums, visited, new ArrayList<>());
        return res;
    }

    public void backtrace(int[] nums, boolean[] visited, List<Integer> list){
        if(list.size() == nums.length){
            res.add(new ArrayList<>(list));
            return;
        }
        for(int i = 0; i < nums.length; i++){
            if(visited[i]) continue;
            list.add(nums[i]);
            visited[i] = true;
            backtrace(nums, visited, list);
            visited[i] = false;
            list.remove(list.size() - 1);
        }
    }
}
  • 7.全排列II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        boolean[] visited = new boolean[nums.length];
        Arrays.sort(nums);
        backtrace(nums, visited, new ArrayList<>());
        return res;
    }

    public void backtrace(int[] nums, boolean[] visited, List<Integer> list){
        if(list.size() == nums.length){
            res.add(new ArrayList<>(list));
            return;
        }
        for(int i = 0; i < nums.length; i++){
            if(visited[i]) continue;
            if(i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) continue;
            list.add(nums[i]);
            visited[i] = true;
            backtrace(nums, visited, list);
            visited[i] = false;
            list.remove(list.size() - 1);
        }
    }
}
  • 8.N皇后
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
class Solution {
    List<List<String>> res = new ArrayList<>();
    public List<List<String>> solveNQueens(int n) {
        //棋盘初始化
        char[][] chessboard = new char[n][n];
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                chessboard[i][j] = '.';
            }
        }

        backtrace(chessboard, 0);
        return res;
    }

    public void backtrace(char[][] chessboard, int row){
        if(row == chessboard.length){
            res.add(ArrayToList(chessboard));
            return;
        }
        for(int col = 0; col < chessboard.length; col++){
            if(check(chessboard, row, col)){
                chessboard[row][col] = 'Q';
                backtrace(chessboard, row + 1);
                chessboard[row][col] = '.';
            }    
        }
    }


    public boolean check (char[][] chessboard, int row, int col){
        //检查同一列有无冲突
        for(int i = 0; i < row; i++){
            if(chessboard[i][col] == 'Q'){
                return false;
            }
        }

        //检查左上方有无冲突
        for(int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--){
            if(chessboard[i][j] == 'Q'){
                return false;
            }
        }

        //检查右上方有无冲突
        for(int i = row - 1, j = col + 1; j < chessboard.length && i >= 0; i--, j++){
            if(chessboard[i][j] == 'Q'){
                return false;
            }
        }

        return true;
    }

    public List<String> ArrayToList(char[][] chessboard){
        List<String> list = new ArrayList<>();
        for(char[] c : chessboard){
            list.add(new String(c));
        }
        return list;
    }
}
  • 9.组合
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> combine(int n, int k) {
        backtrace(n, k, new ArrayList<>(), 1);
        return res;
    }

    public void backtrace(int n, int k, List<Integer> list, int index){
        if(list.size() == k){
            res.add(new ArrayList<>(list));
            return;
        }
        for(int i = index; i <= n - (k - list.size()) + 1; i++){
            list.add(i);
            backtrace(n, k, list, i + 1);
            list.remove(list.size() - 1);
        }
    }
}
  • 10.子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        backtrace(nums, new ArrayList<>(), 0);
        return res;
    }

    public void backtrace(int[] nums, List<Integer> list, int index){
        res.add(new ArrayList<>(list));
        for(int i = index; i < nums.length; i++){
            list.add(nums[i]);
            backtrace(nums, list, i + 1);
            list.remove(list.size() - 1);
        }
    }
}
  • 11.子集II
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        backtrace(nums, new ArrayList<>(), 0);
        return res;
    }

    public void backtrace(int[] nums, List<Integer> list, int index){
        res.add(new ArrayList<>(list));
        for(int i = index; i < nums.length; i++){
            if(i > index && nums[i] == nums[i - 1]) continue;
            list.add(nums[i]);
            backtrace(nums, list, i + 1);
            list.remove(list.size() - 1);
        }
    }
}
  • 12.单词搜索***
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
class Solution {
    boolean find = false;
    public boolean exist(char[][] board, String word) {
        int m = board.length;
        int n = board[0].length;
        boolean[][] visited = new boolean[m][n];
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                backtrace(board, i, j, word, 0, visited);
            }
        }
        return find;
    }

    public void backtrace(char[][] board, int i, int j, String word, int index, boolean[][] visited){
        if(i >= board.length || j >= board[0].length ||
           i < 0 || j < 0 || visited[i][j] || find ||
           board[i][j] != word.charAt(index)){
            return;
        }

        if(index == word.length() - 1){
            find = true;
            return;
        }

        visited[i][j] = true;
        backtrace(board, i + 1, j, word, index + 1, visited);
        backtrace(board, i - 1, j, word, index + 1, visited);
        backtrace(board, i, j + 1, word, index + 1, visited);
        backtrace(board, i, j - 1, word, index + 1, visited);
        visited[i][j] = false;
    }
    
}
  • 13.格雷编码***
格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。

给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。即使有多个不同答案,你也只需要返回其中一种。

格雷编码序列必须以 0 开头。
class Solution {
    List<Integer> res = new ArrayList<>();
    boolean[] visited;
    public List<Integer> grayCode(int n) {
        visited = new boolean[1 << n];
        backtrace(0, n);
        return res;
    }

    public boolean backtrace(int cur, int n){
        if(res.size() == (1 << n)){
            return true;
        }
        res.add(cur);
        visited[cur] = true;
        for(int i = 0; i < n; i++){
            int next = cur ^ (1 << i);
            if(!visited[next] && backtrace(next, n)){
                return true;
            }
        }
        visited[cur] = false;
        return false;
    }
}
  • 14.复原IP地址***
class Solution {
    List<String> res = new ArrayList<>();
    public List<String> restoreIpAddresses(String s) {
        if(s == null || s.length() < 4 || s.length() > 16) return new ArrayList<>();
        backtrace(s, 0, new ArrayList<>());
        return res;
    }

    public void backtrace(String s, int index, List<String> list){
        if(list.size() == 4){
            if(index == s.length()){
                res.add(String.join(".", list));
                return;
            }
        }
        int sum = 0;
        for(int i = index; i < s.length(); i++){
            if(s.charAt(index) == '0' && i > index) break;
            sum = sum * 10 + (s.charAt(i) - '0');
            if(sum <= 255 && sum >= 0){
                list.add(String.valueOf(sum));
                backtrace(s, i + 1, list);
                list.remove(list.size() - 1);
            }
        }
    }
}
  • 15.不同的二叉搜索树II***
给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不同 二叉搜索树 。可以按 任意顺序 返回答案。
class Solution {
    public List<TreeNode> generateTrees(int n) {
        if(n == 0){
            return new ArrayList<>();
        }
        return build(1, n);
    }

    public List<TreeNode> build(int start, int end){
        List<TreeNode> res = new ArrayList<>();
        if(start > end){
            res.add(null);
            return res;
        }
        for(int i = start; i <= end; i++){
            List<TreeNode> leftList = build(start, i - 1);
            List<TreeNode> rightList = build(i + 1, end);
            for(TreeNode left : leftList){
                for(TreeNode right : rightList){
                    TreeNode root = new TreeNode(i);
                    root.left = left;
                    root.right = right;
                    res.add(root);
                }
            }
        }
        return res;
    }
}
  • 16.路径总和II
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        backtrace(root, targetSum, new ArrayList<>());
        return res;
    }

    public void backtrace(TreeNode root, int targetSum, List<Integer> list){
        if(root == null) return;
        list.add(root.val);
        if(root.left == null && root.right == null){
            if(targetSum == root.val){
                res.add(new ArrayList<>(list));  
            }
        }
        if(root.left != null) backtrace(root.left, targetSum - root.val, list);
        if(root.right != null) backtrace(root.right, targetSum - root.val, list);
        list.remove(list.size() - 1);
    }
}
  • 17.分割回文串
class Solution {
    List<List<String>> res = new ArrayList<>();
    public List<List<String>> partition(String s) {
        backtrace(s, 0, new ArrayList<>());
        return res;
    }

    public void backtrace(String s, int index, List<String> list){
        if(index == s.length()){
            res.add(new ArrayList<>(list));
        }
        for(int i = index; i < s.length(); i++){
            String t = s.substring(index, i + 1);
            if(check(t)){
                list.add(t);
                backtrace(s, i + 1, list);
                list.remove(list.size() - 1);
            }
        }
    }

    public boolean check(String s){
        int l = s.length();
        for(int i = 0; i < l / 2; i++){
            if(s.charAt(i) != s.charAt(l - i - 1)){
                return false;
            }
        }
        return true;
    }
}

2. 动态规划-背包问题

  • 1.01背包
有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。
每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

在这里假设N=3即有三件物品,w=4即背包的最大重量为4.
weight = [1 , 3, 4];
value  = [15,20,30];
package LeetCode;

public class package01 {

    public static void main(String[] args) {
        //物品表
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};

        int N = 3;//3件物品
        int W = 4;//背包最大容纳重量

        //1.dp数组的含义:  dp[i][j] 表示在背包重量为j时从 0-i的物品里选取放入背包所获得的最大价值
        int[][] dp = new int[N][W + 1];

        //2.递推式
        //dp[i][j]有两种方式得到 : 即对第i个物品 放或者不放
        //在放入之前首先得判断内否放入   即 j + weight[i] <= W
        //如果不把第i个放入: dp[i][j] = dp[i - 1][j]
        //如果把第i个放入:   dp[i][j] = dp[i - 1][j - weight[i]] + value[i]

        //3.初始条件
        //j = 0 即重量为0时 什么都放不进去
        for(int i = 0; i < N; i++){
            dp[i][0] = 0;
        }
        //i = 0 时 只能放入0号物品
        for(int i = W; i >= 0; i--){
            dp[0][i] = value[0];
        }

        //4.遍历方式
        //根据 i - 1 推导 i 顺序遍历即可
        for(int i = 1; i < N; i++){
            for(int j = 0; j <= W ; j++){
                if(j >= weight[i]){
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
                }
            }
        }

        System.out.println(dp[N - 1][W]);
    }
}

  • 2.完全背包
还是上面的例子,只不过不再是每个物品只有1个,每个物品有无限个
package LeetCode;

public class packageComplete {
    public static void main(String[] args) {
        //物品表
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};

        int N = 3;//3件物品
        int W = 4;//背包最大容纳重量

        //dp数组的含义:  dp[i][j] 表示在背包重量为j时从 0-i的物品里选取放入背包所获得的最大价值
        int[][] dp = new int[N][W + 1];

        //初始化
        for(int i = 0; i < N; i++){
            dp[i][0] = 0;
        }
        for(int i = 1; i <= W; i++){
            if(i >= weight[0]){
                dp[0][i] = dp[0][i - weight[0]] + value[0];
            }
        }

        for(int i = 1; i < N; i++){
            for(int j = 0; j <= W; j++){
                if(j >= weight[i]){
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
    }
}

  • 3.分割等和子集
给你一个 只包含正整数 的 非空 数组 nums 。
请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
class Solution {
    public boolean canPartition(int[] nums) {
        int len = nums.length;
        int sum = 0;
        for(int num : nums){
            sum += num;
        }

        if(sum % 2 != 0){
            return false;
        }

        int target = sum / 2;

        //背包问题
        //现在有 len - 1个 “物品”  各自对应的重量是 nums[i] 价值也是 nums[i]
        //判断能否刚好填满重量是target的背包

        int[] dp = new int[target + 1];

        for(int i = 0; i < len; i++){
            for(int j = target; j >= nums[i] ; j--){
                dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
            }
        }
        
        return dp[target] == target ? true : false;
    }
}
  • 4.最后一块石头的重量
有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。
那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        // 假如正数的和为x
        // 那么负数的和就是  -(sum - x)
        // 那么 x - (sum - x) = target
        // x = (target + sum) / 2
        // 换成了填满容量为x的背包有几种方法

      
        int sum = 0;
        for (int i = 0; i < nums.length; i++) sum += nums[i];
        if ((target + sum) % 2 != 0) return 0;
        int size = (target + sum) / 2;
        int[] dp = new int[size + 1];
        dp[0] = 1;
        for (int i = 0; i < nums.length; i++) {
            for (int j = size; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[size];
    }
}
  • 5.目标和
给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
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];
    }
}
  • 6.一和零
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
class Solution {
    public int findMaxForm(String[] strs, int m, int n) {

        int[][] dp = new int[m + 1][n + 1];

        for(String str : strs){
            int CountZero = 0;
            int CountOne = 0;
            for(char c : str.toCharArray()){
                if(c == '0') CountZero++;
                else CountOne++;
            }

            for(int i = m; i >= CountZero; i--){
                for(int j = n; j >= CountOne; j--){
                    dp[i][j] = Math.max(dp[i][j], dp[i - CountZero][j - CountOne] + 1);
                }
            }
        }

        return dp[m][n];
    }
}
  • 7.单词拆分
给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,
判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:

拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        int len = s.length();
        //完全背包问题
        //1. dp的含义 到s的第i位前能否被拆分为wordDict里词
        boolean[] dp = new boolean[len + 1];

        //2. 初始化
        // s = "" 空串的情况
        dp[0] = true;

        //3. 递推式
        // 如果到前j位能够被拆分,且[i,j]的子串也包含在Dict里面 说明到i都能被拆分

        //4. 遍历方式
        //根据前面的得到后面的  顺序遍历即可
        for(int i = 1; i <= len; i++){
            for(int j = 0; j < i; j++){
                if(dp[j] && wordDict.contains(s.substring(j, i))){
                    dp[i] = true;
                }
            }
        }

        return dp[len];
    }
}
  • 8.零钱兑换II
给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。 

题目数据保证结果符合 32 位带符号整数。
class Solution {
    public int change(int amount, int[] coins) {
        // Completepackage

        //1. dp[i]的含义: 凑成价值为i的总金额所需的硬币组合数
        int[] dp = new int[amount + 1];

        //2. 初始条件
        //凑成总金额为0 
        dp[0] = 1;

        //3. 递推式


        for(int i = 0; i < coins.length; i++){
            for(int j = 1; j <= amount; j++){
                if(j >= coins[i]) dp[j] += dp[j - coins[i]];
            }
        }
        
        return dp[amount];
    }
}
  • 9.零钱兑换
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。
class Solution {
    public int coinChange(int[] coins, int amount) {
        //CompletePackage

        //1. dp[i]的含义:凑成总金额为i时需要的最少硬币个数
        int[] dp = new int[amount + 1];

        //2. 初始化
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;

        //3. 递推式
        for(int i = 0; i < coins.length; i++){
            for(int j = coins[i]; j <= amount; j++){
                if(dp[j - coins[i]] != Integer.MAX_VALUE){
                    dp[j] = Math.min(dp[j - coins[i]] + 1, dp[j]);
                }
            }
        }

        return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
    }
}

3.动态规划-基础DP

  • 1.斐波那契数列
class Solution {
    public int fib(int n) {
        if(n <= 1) return n;
        int[] dp = new int[n + 1];
        dp[0] = 0;
        dp[1] = 1;
        for(int i = 2; i <= n; i++)
            dp[i] = dp[i - 2] + dp[i - 1];
        return dp[n];
    }
}
  • 2.爬楼梯
class Solution {
    public int climbStairs(int n) {
        if(n < 2){
            return n;
        }
        int a = 0;
        int b = 1;
        int res = 0;
        while(n-- > 0){
            res = a + b;
            a = b;
            b = res;
        }
        return res;
    }
}
  • 3.使用最小花费爬楼梯
class Solution {
    public int minCostClimbingStairs(int[] cost) {
        //1. dp[i] 登上第i个台阶用的最小的体力
        int len = cost.length;
        int[] dp = new int[len];

        //2. 初始条件
        dp[0] = cost[0];
        dp[1] = cost[1];

        //3. 遍历方式
        for(int i = 2; i < len; i++){
            //4. 递推式
            dp[i] = cost[i] + Math.min(dp[i - 1] , dp[i - 2]);
        }

        return Math.min(dp[len - 1], dp[len - 2]);
    }
}
  • 4.不同路径I
class Solution {
    public int uniquePaths(int m, int n) {
        //1. dp[i][j] 到(i,j)点的路径数
        int[][] dp = new int[m][n];

        //2. 初始化
        dp[0][0] = 1;
        //横向
        for(int i = 1; i < n; i++){
            dp[0][i] = dp[0][i - 1]; 
        }
        //竖向
        for(int i = 1; i < m; i++){
            dp[i][0] = dp[i - 1][0];
        }

        //3. 遍历方式
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                //4. 递推式
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }

        return dp[m - 1][n - 1];
    }
}
  • 5.不同路径II
class Solution {
    int count = 0;
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        if(obstacleGrid[0][0] == 1)
            return 0;
        //dfs(0,0,obstacleGrid);
        //return count;

        //简单DP
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];
        dp[0][0] = 1;
        for(int i = 1; i < m; i++){
            if(obstacleGrid[i][0] == 1) break;
            dp[i][0] = 1;
        }
        for(int i = 1; i < n; i++){
            if(obstacleGrid[0][i] == 1) break;
            dp[0][i] = 1;
        }
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                if(obstacleGrid[i][j] != 1) 
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return  dp[m - 1][n - 1];

    }

    //dfs 超时
    //递归回溯 100 的深度是极限
    public void dfs(int x, int y, int[][] map){
        
        if(x == map.length - 1 && y == map[0].length - 1){
            count++;
            return;
        }
        
        if(y < map[0].length - 1 && map[x][y + 1] != 1){      
            dfs(x, y + 1, map);
        } 
        if (x < map.length - 1 && map[x + 1][y] != 1) {
            dfs(x + 1, y, map);
        }
            
        
    }
}
  • 6.跳跃游戏II
class Solution {
    public int jump(int[] nums) {
        int len = nums.length;
        if(len == 1) return 0;
        //1. dp[i] 到第i个位置所需要的最小步数
        int[] dp = new int[len];

        //2. 初始化
        Arrays.fill(dp, len);
        dp[0] = 0;

        //3. 递推式
        //dp[i] = dp[j] + 1     0 < j < i - 1
        //        dp[i]
        
        //4. 遍历方式
        for(int i = 1; i < len; i++){
            for(int j = 0; j < i; j++){
                if(j + nums[j] >= i)
                    dp[i] = Math.min(dp[i], dp[j] + 1);
            }
        }
        return dp[len - 1];
    }
}
  • 7.单词拆分
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        int len = s.length();
        //完全背包问题
        //1. dp的含义 到s的第i位前能否被拆分为wordDict里词
        boolean[] dp = new boolean[len + 1];

        //2. 初始化
        // s = "" 空串的情况
        dp[0] = true;

        //3. 递推式
        // 如果到前j位能够被拆分,且[i,j]的子串也包含在Dict里面 说明到i都能被拆分

        //4. 遍历方式
        //根据前面的得到后面的  顺序遍历即可
        for(int i = 1; i <= len; i++){
            for(int j = 0; j < i; j++){
                if(dp[j] && wordDict.contains(s.substring(j, i))){
                    dp[i] = true;
                }
            }
        }

        return dp[len];
    }
}
  • 8.整数拆分
class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n + 1];
        dp[0] = 0;
        dp[1] = 0;
        dp[2] = 1;
        for(int i = 3; i <= n ; i++){
            for(int j = 1; j < i; j++){
                dp[i] = Math.max(dp[i], Math.max(j * dp[i - j], (i - j) * j));
            }
        }
        return dp[n];
    }
}
  • 9.不同的二叉搜索树
class Solution {
    public int numTrees(int n) {
        if(n == 1) return 1;

        //1. dp[i] i个节点组成的二叉搜索树的种数
        int[] dp = new int[n + 1];


        //2. 初始条件
        dp[0] = 1;
        dp[1] = 1;
        //3. 递推式
        //dp[i] = dp[0] * dp[i - 1] + dp[1] * dp[i - 2] ....

        //4. 遍历方式
        for(int i = 2; i <= n; i++){
            for(int j = 1; j <= i; j++){
                dp[i] += dp[j - 1] * dp[i - j];
            }
        }
        return dp[n];
    }
}

4.动态规划-打家劫舍

  • 1.打家劫舍1
class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        if(n == 1)
            return nums[0];
        int[] dp = new int[n + 1];
        dp[1] = nums[0];
        for(int i = 2; i <= n; i++){
            dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1]);
        }
        return dp[n];
    }
}
  • 2.打家劫舍II
class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        if(len == 1)
            return nums[0];
        int[] dp1 = new int[len + 1];
        int[] dp2 = new int[len + 1];
        //偷第一个房间 不偷最后一个房间
        dp1[1] = nums[0];
        for(int i = 2; i <= len; i ++){
            dp1[i] = Math.max((dp1[i - 2] + nums[i - 1]), dp1[i - 1]);
        }
        //偷最后一个 不偷第一个
        for(int i = 2; i <= len; i ++){
            dp2[i] = Math.max((dp2[i - 2] + nums[i - 1]), dp2[i - 1]);
        }
        return Math.max(dp1[len - 1], dp2[len]);
    }
}
  • 3.打家劫舍III
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int rob(TreeNode root) {
        int[] res = help(root);
        return Math.max(res[0], res[1]);
    }

    public int[] help(TreeNode root){
        if(root == null) return new int[2];
        int[] left = help(root.left);
        int[] right = help(root.right);
        int[] res = new int[2];
        //不抢该节点
        res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
        //抢该节点
        res[1] = root.val + left[0] + right[0];
        return res;
    }
}

5.动态规划-子序列

  • 1.最长回文子串
class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        if(s == null || len == 0){
            return "";
        }

        int start = 0;
        int maxLen = 1;

        boolean[][] dp = new boolean[len][len];

        for(int i = 0; i < len; i++)
            dp[i][i] = true;
        
        for(int j = 1; j < len; j++){
            for(int i = 0; i < j; i++){
                if(s.charAt(i) == s.charAt(j)){
                    if(j - i <= 2){
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }
                if(dp[i][j] && j - i + 1 > maxLen){
                    maxLen = j - i + 1;
                    start = i;
                }
            }
        }

        return s.substring(start, start + maxLen);
    }
}
  • 2.最大子序和
class Solution {
    public int maxSubArray(int[] nums) {
        int length = nums.length;
        if(length < 2) return nums[0];
        int[] dp = new int[length];
        dp[0] = nums[0];
        int res = nums[0];
        for(int i = 1; i < length; i++){
            dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
            if(dp[i] > res)
                res = dp[i];
        }
        return res;
    }
}
  • 3.最长递增子序列
class Solution {
    public int lengthOfLIS(int[] nums) {
        int len = nums.length;
        int[] dp = new int[len];
        Arrays.fill(dp, 1);
        int maxLen = 1;
        for(int i = 1; i < len; i++){
            for(int j = 0; j < i; j++){
                if(nums[i] > nums[j]){
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
                maxLen = Math.max(dp[i], maxLen);
            }
        }
        return maxLen;
    }
}
  • 4.最长回文子序列
class Solution {
    public int longestPalindromeSubseq(String s) {
        //1. dp[i][j]的含义 s的i到j段最长回文子序列长度
        int[][] dp = new int[s.length()][s.length()];

        //2. 一些条件的初始化
        for(int i = 0; i < s.length(); i++){
            dp[i][i] = 1;
        }

        //3. 遍历方式
        for(int i = s.length() - 1; i >= 0; i--){
            for(int j = i + 1; j < s.length(); j++){
                //4. 状态转移
                if(s.charAt(i) == s.charAt(j)){
                    dp[i][j] = 2 + dp[i + 1][j - 1];
                } else {
                    dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[0][s.length() - 1];
    }
}
  • 5.回文子串
class Solution {
    public int countSubstrings(String s) {
        //1. dp[i][j] s的i到j是否是回文串
        int len = s.length();
        boolean[][] dp = new boolean[len][len];
        int count = 0;

        //2. 初始条件
        //"a" "b" "c"这种是回文串 就是i = j 必为true
        for(int i = 0; i < len; i++){
            dp[i][i] = true;
        }

        //3. 递推式
        //s[i] == s[j]  (1). j - i <= 2 比如"aa","aba" true
        //              (2). 取决于i到j内的串是否回文 比如"acbca" dp[i][j] = dp[i + 1][j - 1];
        //  (i,j - 1)           (i,j)      (i, j + 1)
        //  (i + 1, j - 1)    (i + 1, j)   (i + 1, j + 1)
        //4. 遍历方式
        //根据其递推式  i应该逆序  先得到i + 1再得到i
        //j应该正序 先得到j - 1再得到j
        for(int i = len - 1; i >= 0; i--){
            for(int j = i; j < len; j++){
                if(s.charAt(i) == s.charAt(j)){
                    if(j - i <= 2){
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }

                    if(dp[i][j]){
                        count++;
                    }
                }
            }
        }
        return count;
    }
}
  • 6.最长连续递增序列
class Solution {
    public int findLengthOfLCIS(int[] nums) {
        int len = nums.length;
        int[] dp = new int[len];
        Arrays.fill(dp, 1);
        int maxLen = 1;
        for(int i = 1; i < len; i++){
            if(nums[i] > nums[i - 1]){
                dp[i] = dp[i - 1] + 1;
            }
            maxLen = Math.max(dp[i], maxLen);
        }
        return maxLen;
    }
}
  • 7.最长重复子数组
class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int len1 = nums1.length;
        int len2 = nums2.length;
        int maxLen = 0;
        // 1.dp[i][j] nums1到i为止  nums2到j为止的最长重复子数组的长度
        int[][] dp = new int[len1 + 1][len2 + 1];

        // 2.递推式
        // nums1[i] = nums2[j]     dp[i - 1][j - 1] + 1 

        // 3.初始条件


        // 4.遍历方式
        for(int i = 1; i <= len1; i++){
            for(int j = 1; j <= len2; j++){
                if(nums2[j - 1] == nums1[i - 1]){
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                maxLen = Math.max(dp[i][j], maxLen);
            }
        }

        return maxLen;
    }
}

6. 动态规划-买卖股票

  • 1.买卖股票的最佳时机
class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        //1. dp[i][j]  第i天在j状态下的最大现金
        //   j = 0:表示持有  
        //   j = 1:表示未持有    
        
        int[][] dp = new int[len][2];

        //2. 初始化
        dp[0][0] = -prices[0];
        dp[0][1] = 0;

        //3. 递推式
        // dp[i][0]: 第i天持有的情况  (1).当天买进了dp[i - 1][1] - price[i] 
        //                            (2).之前就持有 当天没卖 dp[i - 1][0]
        // dp[i][1]: 第i天未持有的情况 (1). 当天卖出了  dp[i - 1][0] + price[i]
        //                             (2). 之前就未持有 当天也没买 dp[i - 1][1]

        //4. 遍历方式
        //根据小的 决定 大的
        //i - 1          i
        //正序遍历
        for(int i = 1; i < len; i++){
            dp[i][0] = Math.max( - prices[i], dp[i - 1][0]);
            dp[i][1] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][1]);
        }

        //肯定是当时未持有的时候现金最多
        return dp[len - 1][1];
    }
}
  • 2.买卖股票的最佳时机II
class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        //1. dp[i][j]  第i天在j状态下的最大现金
        //   j = 0:表示持有  
        //   j = 1:表示未持有    
        
        int[][] dp = new int[len][2];

        //2. 初始化
        dp[0][0] = -prices[0];
        dp[0][1] = 0;

        //3. 递推式
        // dp[i][0]: 第i天持有的情况  (1).当天买进了dp[i - 1][1] - price[i] 
        //                            (2).之前就持有 当天没卖 dp[i - 1][0]
        // dp[i][1]: 第i天未持有的情况 (1). 当天卖出了  dp[i - 1][0] + price[i]
        //                             (2). 之前就未持有 当天也没买 dp[i - 1][1]

        //4. 遍历方式
        //根据小的 决定 大的
        //i - 1          i
        //正序遍历
        for(int i = 1; i < len; i++){
            dp[i][0] = Math.max(dp[i - 1][1] - prices[i], dp[i - 1][0]);
            dp[i][1] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][1]);
        }

        //肯定是当时未持有的时候现金最多
        return dp[len - 1][1];
    }
}
  • 3.买卖股票的最佳时机III
class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        
        //1. dp[i][j] 第i天在j状态下的最大现金
        //   j = 0: 无操作
        //   j = 1: 第一次买入
        //   j = 2: 第一次卖出
        //   j = 3: 第二次买入
        //   j = 4: 第二次卖出
        int[][] dp = new int[len][5];

        //2. 初始化
        dp[0][1] = -prices[0];
        dp[0][3] = -prices[0];

        //3. 递推式
        //   dp[i][0] = dp[i - 1][0]
        //   dp[i][1] = dp[i - 1][1]   dp[i - 1][0] - prices[i]
        //   dp[i][2] = dp[i - 1][2]   dp[i - 2][1] + prices[i] 
        //   dp[i][3] = dp[i - 1][3]   dp[i - 1][2] - prices[i]
        //   dp[i][4] = dp[i - 1][4]   dp[i - 2][3] + prices[i] 


        //4. 遍历方式
        for(int i = 1; i < len; i++){
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
            dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
            dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
            dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
        }

        return dp[len - 1][4];
    }
}
  • 4.买卖股票的最佳时机IV
class Solution {
    public int maxProfit(int k, int[] prices) {
        int len = prices.length;
        if(len == 0) return 0;
        int[][] dp = new int[len][k * 2 + 1];

        // 奇数买入
        for(int i = 1; i < dp[0].length; i += 2){
            dp[0][i] = -prices[0];
        }

        for(int i = 1; i < len; i++){
            for(int j = 0; j < dp[0].length - 2; j += 2){
                dp[i][j + 1] = Math.max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
                dp[i][j + 2] = Math.max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
            }
        }

        return dp[len - 1][k * 2];
    }
}
  • 5.买卖股票的最佳时机含手续费
class Solution {
    public int maxProfit(int[] prices, int fee) {
        int len = prices.length;
        //1. dp[i][j]  第i天在j状态下的最大现金
        //   j = 0:表示持有  
        //   j = 1:表示未持有    
        
        int[][] dp = new int[len][2];

        //2. 初始化
        dp[0][0] = -prices[0];
        dp[0][1] = 0;

        //3. 递推式
        // dp[i][0]: 第i天持有的情况  (1).当天买进了dp[i - 1][1] - price[i] 
        //                            (2).之前就持有 当天没卖 dp[i - 1][0]
        // dp[i][1]: 第i天未持有的情况 (1). 当天卖出了  dp[i - 1][0] + price[i]
        //                             (2). 之前就未持有 当天也没买 dp[i - 1][1]

        //4. 遍历方式
        //根据小的 决定 大的
        //i - 1          i
        //正序遍历
        for(int i = 1; i < len; i++){
            dp[i][0] = Math.max(dp[i - 1][1] - prices[i], dp[i - 1][0]);
            dp[i][1] = Math.max(dp[i - 1][0] + prices[i] - fee, dp[i - 1][1]);
        }

        //肯定是当时未持有的时候现金最多
        return dp[len - 1][1];
    }
}


7.贪心

  • 1.分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;
并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,
这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int cookie = 0;
        int Children = 0;
        while(cookie < s.length && Children < g.length){
            if(s[cookie] >= g[Children]){
                Children++;
            }
            cookie++;
        }
        return Children;
    }
}
  • 2.摆动序列
class Solution {
    public int wiggleMaxLength(int[] nums) {
        if(nums.length < 2)
            return 1;
        //之前一对值的差
        int prediff = 0;
        //当前一对值的差
        int curdiff = 0;

        int res = 1;
        for(int i = 1; i < nums.length; i++){
            curdiff = nums[i] - nums[i - 1];

            if((curdiff > 0 && prediff <= 0) || (curdiff < 0 && prediff >= 0)){
                res++;
                prediff = curdiff;
            }
        }

        return res;
    }
}
  • 3.加油站
class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        //起点
        int start = 0;

        int curSum = 0;
        int totalSum = 0;

        for(int i = 0; i < gas.length; i++){
            curSum += (gas[i] - cost[i]);
            totalSum += (gas[i] - cost[i]);
            //说明不能从start点出发到 i + 1点
            //起点需要更新
            if(curSum < 0){
                start = i + 1;
                curSum = 0;
            }
        }

        return totalSum < 0 ? -1 : start;
    }
}
  • 4.分发糖果
class Solution {
    public int candy(int[] ratings) {
        int[] candys = new int[ratings.length];
        Arrays.fill(candys, 1);
        int res = 0;

        for(int i = 1; i < ratings.length; i++){
            if(ratings[i] > ratings[i - 1]){
                candys[i] = candys[i - 1] + 1;
            }
        }

        for(int i = ratings.length - 2; i >= 0; i--){
            if(ratings[i] > ratings[i + 1]){
                candys[i] = Math.max(candys[i + 1] + 1, candys[i]);
            }
        }
        
        for(int candy : candys){
            res += candy;
        }
        return res;
    }
}
  • 5.柠檬水找零
class Solution {
    public boolean lemonadeChange(int[] bills) {
        int count5 = 0;
        int count10 = 0;
        for(int i = 0; i < bills.length; i++){
            if(bills[i] == 5){
                count5++;
            } else if (bills[i] == 10) {
                count10++;
                count5--;
                if(count5 < 0) return false;
            } else if (bills[i] == 20) {
                if(count10 > 0 && count5 > 0){
                    count5--;
                    count10--;
                } else if (count5 > 2) {
                    count5 -= 3;
                } else {
                    return false;
                }
            }
        }
        return true;
    }
}
  • 6.根据身高重建队列
class Solution {
    public int[][] reconstructQueue(int[][] people) {
        Arrays.sort(people, (x,y) -> x[0] == y[0] ? x[1] - y[1] : y[0] - x[0] );
        List<int[]> list = new LinkedList<>();
        for(int[] i : people)
            list.add(i[1], i);
        return list.toArray(new int[list.size()][2]);
    }
}
  • 7.用最少数量的箭引爆气球
class Solution {
    public int findMinArrowShots(int[][] points) {
        Arrays.sort(points, (o1, o2) -> o1[1] > o2[1] ? 1 : -1);
        int last = points[0][1];
        int count = 1;
        for(int i = 1; i < points.length; i++){
            if(points[i][0] > last){
                count++;
                last = points[i][1];
            }
        }
        return count;
    }
}
  • 8.无重叠区间
class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        int n = intervals.length;
        if(intervals.length == 0 || intervals[0].length == 0)
            return 0;
        Arrays.sort(intervals, (o1, o2) -> {
            if(o1[1] != o2[1])
                return o1[1] - o2[1];
            return o1[0] - o2[0];
        });
        int maxCount = 1;
        int pre = 0;
        for(int i = 1; i < n; i++){
            if(intervals[pre][1] <= intervals[i][0]){
                maxCount++;
                pre = i;
            }
        }
        return n - maxCount;
    }
}
  • 9.划分字母区间
class Solution {
    public List<Integer> partitionLabels(String s) {
        int[] lastIndex = new int[26];
        int len = s.length();
        //记录最后出现的位置
        for(int i = 0; i < len; i++){
            lastIndex[s.charAt(i) - 'a'] = i;
        }
        List<Integer> list = new ArrayList<>();
        int end = 0;
        int start = 0;

        for(int i = 0; i < len; i++){
            end = Math.max(end, lastIndex[s.charAt(i) - 'a']);
            if(end == i){
                list.add(end - start + 1);
                start = i + 1;
            }
        }

        return list;
    }
}
  • 10.合并区间
class Solution {
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);
        int index = -1;
        int[][] res = new int[intervals.length][2];
        for(int[] interval : intervals){
            if(index == -1 || interval[0] > res[index][1]){
                res[++index] = interval;
            } else {
                res[index][1] = Math.max(interval[1], res[index][1]);
            }
        }
        return Arrays.copyOf(res, index + 1);
    }
}
  • 11.单调递增的数字
class Solution {
    public int monotoneIncreasingDigits(int n) {
        String s = String.valueOf(n);
        char[] arr = s.toCharArray();
        int len = arr.length;
        int flag = len;
        for(int i = len - 1; i >= 1; i--){
            if(arr[i - 1] > arr[i]){
                arr[i - 1]--;
                flag = i;
            }
        }
        for(int i = flag; i < len; i++){
            arr[i] = '9';
        }
        return Integer.parseInt(new String(arr));
    }
}

8.单调栈

  • 1.每日温度
请根据每日 气温 列表 temperatures ,
请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        Stack<Integer> stack = new Stack<>();
        int len = temperatures.length;
        int[] res = new int[len];

        for(int i = 0; i < len; i++){
            while(!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]){
                res[stack.peek()] = i - stack.pop();
            }
            stack.push(i);
        }
        return res;
    }
}
  • 2.下一个更大的元素I
给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。

请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。
class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int[] res = new int[nums1.length];
        Arrays.fill(res, -1);
        Stack<Integer> stack = new Stack<>();
        Map<Integer, Integer> map = new HashMap<>();
        for(int i = 0; i < nums1.length; i++)
            map.put(nums1[i], i);
        
        stack.push(0);
        for(int i = 1; i < nums2.length; i++){ 
            while(!stack.isEmpty() && nums2[i] > nums2[stack.peek()]){
                if(map.containsKey(nums2[stack.peek()]))
                    res[map.get(nums2[stack.peek()])] = nums2[i];
                stack.pop();
            }   
            stack.push(i);    
        }
        return res;
    }
}
  • 3.下一个更大的元素II
给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。
数字 x 的下一个更大的元素是按数组遍历顺序,
这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
class Solution {
    public int[] nextGreaterElements(int[] nums) {
        Stack<Integer> stack = new Stack<>();
        int[] res = new int[nums.length];
        Arrays.fill(res, -1);
        for(int i = 0; i < nums.length << 1; i++){
            while(!stack.isEmpty() && nums[i % nums.length] > nums[stack.peek()]){
                res[stack.pop()] = nums[i % nums.length];
            }
            stack.push(i % nums.length);
        }
        return res;
    }
}
  • 4.接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
class Solution {
    public int trap(int[] height) {
        int sum = 0;
        Stack<Integer> stack = new Stack<>();
        for(int i = 0; i < height.length; i++){
            while(!stack.isEmpty() && height[i] > height[stack.peek()]){
                //计算储水的列的柱高
                int h = height[stack.peek()];
                stack.pop();
                if(!stack.isEmpty()){
                    //计算间距
                    int distance = i - stack.peek() - 1;
                    //短板效应
                    int min = Math.min(height[i], height[stack.peek()]);
                    sum += (min - h) * distance;
                }
            }
            stack.push(i);
        }
        return sum;
    }
}
  • 5.柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。
class Solution {
    public int largestRectangleArea(int[] heights) {
        int area = 0;
        int[] tmp = new int[heights.length + 2];
        System.arraycopy(heights, 0, tmp, 1, heights.length); 
        Stack<Integer> stack = new Stack<>();
        for(int i = 0; i < tmp.length; i++){
            while(!stack.isEmpty() && tmp[i] < tmp[stack.peek()]){
                int h = tmp[stack.pop()];
                int w = i - stack.peek() - 1;
                area = Math.max(area, w * h);
            }
            stack.push(i);
        }
        return area;
    }
}

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号