整理所有题目

  • 09两个栈实现一个队列
    • 栈 队列
  • 10斐波那契数列
    • 迭代 递归
  • 03数组中重复的数字
    • 数组原地置换
  • 04二维数组中的查找
    • 跳表 从右往左 从上往下
  • 10-II 青蛙跳台阶
    • 同斐波那契数列
  • 旋转数组的最小数字
    • 二分
  • 05 替换空格
    • StringBuffer
  • 06从头到尾打印链表
    • 栈 反转
  • 12 矩阵中到路径
    • BFS +剪枝
  • 07重建二叉树
    • 递归
  • 13机器人的运动范围
    • DFS 考虑连通性
  • 14剪绳子|
    • dp
  • 14剪绳子||
  • 树的子结构
    • 递归
  • 对称的二叉树
    • 递归
  • 27二叉树的镜像
    • 递归
  • 29 顺时针打印矩阵
    • 指针 遍历
  • 21 调整数组顺序 使奇数位于偶数之前
    • 双指针
  • 15 二进制中1的个数
    • 位运算 n&(n-1)消去最右边的1
  • 22链表中的倒数第N个节点
    • 快慢指针
  • 16 数值的整数次方
    • 快速幂
  • 17 打印1到最大到N位数
    • 大数打印
  • 最小的K个数
    • TOP K
    • 快速选择
  • 反转链表
    • 迭代
  • 删除链表的节点
    • 快慢指针
  • 复杂链表的复制
    • 复制节点
  • 正则表达式匹配
    • dp
  • 30包含min函数的栈
    • 最小栈
    • 使用Deque
  • 41数据流中的中位数
    • 优先级队列
    • 大根堆 小根堆
  • 42连续子数组的最大和
    • 动态规划
  • 36二叉搜索树与双向链表
    • 中序遍历
  • 31 栈的压入 弹出序列
    • 模拟法
  • 37 序列化二叉树
    • 层序遍历
  • 43 1-n整数中1出现的个数
    • 枚举每一数位上1的个数
  • 39 数组中出现超过一半的数
    • hash统计
    • 数组排序找中点
    • 摩尔投票法
  • 44 数字序列中某一位的数字
    • 数学归纳
  • 38 字符串的排列
    • 全排列II
    • 回溯+剪枝
  • 32 从上到下打印二叉树
    • 层序遍历
  • 33 二叉搜索树的后序遍历
    • 递归
    • 单调栈
  • 51 数组中的逆序对
    • 归并排序
  • 50 第一个只出现一次的字符
    • hash
  • 34 二叉树中和为某一值的路径
    • 回溯
    • 剪枝
  • 55 二叉树的深度
    • 递归
  • 56数组中数字出现的次数|
    • 位运算
    • 相同数字异或为0
    • 找到其最右边的1 进行分组异或即可
  • 56数组中数字出现的次数||
    • 使用数组记录其位的个数 然后对3 取余
  • 57 和为s的两个数字
    • hash
  • 45 把数组排成最小的数
    • 排序重载
  • 57 和为s的连续正序数列
    • 双指针
    • 滑动窗口
  • 46 把数字翻译成字符串
    • 回溯法(数据量少)
    • dp(属于是爬楼梯变种)
  • 52 两个链表的第一个公共节点
    • 数学归纳
  • 47 礼物的最大价值
    • dp
    • 可以开大一点的空间,减少边界处理的复杂度 比如 0开始的 i-1
    • 回溯(超时)
  • 58I反转单词顺序
    • substring
    • 双指针
  • 58II 左字符串
    • substring
  • 53I在排序数组中查找数字I
    • 二分
  • 53II 0-n-1中缺失的数字
    • 二分
  • 48 最长的不含重复字符的字符串
    • 双指针
  • 54 二叉树的第K大节点
    • 中序遍历
  • 49 丑数
    • 模拟法
  • 65 不用加减乘除做加法
    • 位运算
  • 67 把字符串转换为整数
    • 模拟法
  • 61 扑克牌中的顺子
    • hash
  • 52 II 平衡二叉树
    • 递归
  • 62 圆圈中最后剩下的数字
    • 约瑟夫环问题
  • 63 股票的最大利润
    • dp
  • 59 I 滑动窗口的最大值
    • 单调队列
    • 大 -> 小
  • 59 II 队列的最大值
    • 单调队列
    • 注意 包装类比较值使用equals方法
  • 60 n个骰子的点数
    • dp
  • 66 构建乘积数组
    • dp

代码

基础代码

两个栈实现一个队列

  1. class CQueue {
  2. LinkedList<Integer> A, B;
  3. public CQueue() {
  4. A = new LinkedList<Integer>();
  5. B = new LinkedList<Integer>();
  6. }
  7. public void appendTail(int value) {
  8. A.addLast(value);
  9. }
  10. public int deleteHead() {
  11. if(!B.isEmpty()) return B.removeLast();
  12. if(A.isEmpty()) return -1;
  13. while(!A.isEmpty())
  14. B.addLast(A.removeLast());
  15. return B.removeLast();
  16. }
  17. }

斐波那契数列

  1. class Solution {
  2. public int fib(int n) {
  3. int a = 0;
  4. int b = 1;
  5. int c = 0;
  6. for(int i = 1; i < n ;i ++) {
  7. c = a + b;
  8. a = b;
  9. b = c;
  10. }
  11. return c;
  12. }
  13. }

数组中重复的数字

class Solution {
    public int findRepeatNumber(int[] nums) {
        for(int i = 0 ; i < nums.length; i++) {
            if(nums[i] != i){
                swap(i, nums[i],nums);
                if(nums[i] == nums[nums[i]]) {
                    return nums[i];
                }
            }

        }
        return 0;
    }
    public void swap(int i, int j, int[] nums){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

二维数组中的查找

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        if (matrix == null ||matrix.length == 0 ||matrix[0].length==0) return false;
        //从右上角开始
        int x = matrix.length-1;
        int y = 0;
        while(x>=0 && y<= matrix[0].length-1) {
            if(target == matrix[x][y]) {
                return true;
            }else if(target > matrix[x][y]) {
                y++;
            }else{
                x--;
            }
        }
        return false;
    }
}

旋转数组的最小数字

class Solution {
    public int minArray(int[] numbers) {
        int left = 0;
        int right = numbers.length-1;
        int min = Integer.MAX_VALUE;
        // 313  3456111
        //num[mid] > num[right]     min 在 右侧  [mid + 1, right]    left = mid + 1.   
        //num[mid] < num[right]     min 在 左侧  [left, mid]         right = mid
        //num[mid] == num[right] unknown. right--;
        while(left < right) {
            int mid = left + (right - left) / 2;
            if(numbers[mid] < numbers[right]) {
                right = mid;
            }else if(numbers[mid] > numbers[right]){
                left = mid+1;
            }else {
                right = right - 1;
            }
        }
        return numbers[left];
    }
}

矩阵中的路径

class Solution {
    public boolean exist(char[][] board, String word) {
        char[] words = word.toCharArray();
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board[0].length; j++) {
                if(dfs(board, words, i, j, 0)) return true;
            }
        }
        return false;
    }
    boolean dfs(char[][] board, char[] word, int i, int j, int k) {
        if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;
        if(k == word.length - 1) return true;
        board[i][j] = '\0';
        boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) || 
                      dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
        board[i][j] = word[k];
        return res;
    }
}

重建二叉树

class Solution {
    int[] preorder;
    Map<Integer, Integer> map = new HashMap();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        this.preorder = preorder;
        for(int i = 0; i < inorder.length; i++){
            map.put(inorder[i], i);
        }
        return recur(0, 0, preorder.length-1);
    }
    TreeNode recur(int pre_index, int in_left, int in_right){
        if(in_left > in_right) return null;
        int in_index = map.get(preorder[pre_index]);
        TreeNode node = new TreeNode(preorder[pre_index]);
        int left_length = in_index - in_left;
        node.left = recur(pre_index + 1, in_left, in_index-1);
        node.right = recur(pre_index + 1 + left_length, in_index+1, in_right);
        return node;
    }
}

机器人的运动范围

class Solution {
    public int count = 0;
    boolean[][] visited;
    public int movingCount(int m, int n, int k) {
        boolean[][] visited = new boolean[m][n];
        this.visited = visited;
        dfs(m, n, 0, 0, k);
        return count;
    }

    private void dfs(int m, int n, int i, int j, int k) {
        if (i < 0 || j < 0 || i >= m || j >= n || visited[i][j]) {
            return;
        }
        if (sum(i) + sum(j) > k) {
            return;
        }
        visited[i][j] = true;
        count++;
        dfs(m, n, i + 1, j, k);
        dfs(m, n, i - 1, j, k);
        dfs(m, n, i, j + 1, k);
        dfs(m, n, i, j - 1, k);
    }

    private int sum(int m) {
        int n = 0;
        while (m != 0) {
            n = n + m % 10;
            m = m / 10;
        }
        return n;
    }
}

树的子结构

class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        return (A != null && B != null) && (recur(A, B)||isSubStructure(A.left, B)||isSubStructure(A.right, B));
    }

    public boolean recur(TreeNode A, TreeNode B) {
        if(B == null) return true;
        if(A == null || A.val != B.val ) return false;
        return recur(A.right, B.right) && recur(A.left, B.left);
    }

}

对称二叉树

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null) return true;
        return is(root.left, root.right);
    }
    public boolean is(TreeNode n1, TreeNode n2){
        if(n1 == null && n2 == null) return true;
        if(n1 == null || n2 == null) return false;
        if(n1.val != n2.val) return false;
        return is(n1.left, n2.right) && is(n1.right, n2.left);
    }
}

二叉树镜像

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null) return null;
        TreeNode leftRoot = mirrorTree(root.right);
        TreeNode rightRoot = mirrorTree(root.left);

        root.left = leftRoot;
        root.right = rightRoot;
        return root;
    }
}

割绳子

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

顺时针打印矩阵

class Solution {
    public int[] spiralOrder(int[][] matrix) {
        //定义四个方向 lrud 确定范围以及走后对l r u d 的影响 注意停止标记
        if(matrix.length == 0) return new int[0];
        int[] res = new int[matrix.length *(matrix[0].length)];
        int k=0;
        int left = 0;
        int right = matrix[0].length-1;
        int up = 0;
        int down = matrix.length-1;
        while(true) {
            for(int i = left; i <= right; i++) res[k++] = matrix[up][i];
            if(++up > down) break;
            for(int i = up; i <= down; i++) res[k++] = matrix[i][right];
            if(--right < left) break;
            for(int i = right; i >= left; i--) res[k++] = matrix[down][i];
            if(--down < up) break;
            for(int i = down; i >= up; i--) res[k++] = matrix[i][left];
            if(++left > right) break;
        }
        return res;
    }
}

二进制中1的个数

public class Solution {
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0) {
            res = res + (n&1);
            n = n >>>1;
        }
        return res;
    }
}

public class Solution {
    public int hammingWeight(int n) {
        //n&(n-1) 将二进制数字中最右边的1变为0
        int res = 0;
        while(n != 0) {
            res++;
            n = n & (n - 1);
        }
        return res;
    }
}

数值的整数次方

class Solution {
    public double myPow(double x, int n) {
        if(n < 0) {
            x = 1/x;
            n = -n;
        }
        double temp = 1;
        for(int i = 0; i < n ; i++){
            temp = temp * x;
        }
        return temp;
    }
}
class Solution {
    // 11 ->      1011 
    //快速幂2e11 -> X2e3 * x2e1 * x2e1
    public double myPow(double x, int n) {
        if(x==0) return 0;
        long b = n;
        double res = 1.0;
        if(b < 0) {
            x = 1/x;
            b  = -b;
        }
        while(b > 0){
            if((b&1)==1) res = res * x;
            x = x*x;
            b = b >> 1;
        }
        return res;

    }
}

打印1到最大到N位数

//全排列 但是有多余的0
class Solution {
    StringBuilder res;
    int count = 0, n;
    char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    public String printNumbers(int n) {
        this.n = n;
        res = new StringBuilder(); // 数字字符串集
        num = new char[n]; // 定义长度为 n 的字符列表
        dfs(0); // 开启全排列递归
        res.deleteCharAt(res.length() - 1); // 删除最后多余的逗号
        return res.toString(); // 转化为字符串并返回
    }
    void dfs(int x) {
        if(x == n) { // 终止条件:已固定完所有位
            res.append(String.valueOf(num) + ","); // 拼接 num 并添加至 res 尾部,使用逗号隔开
            return;
        }
        for(char i : loop) { // 遍历 ‘0‘ - ’9‘
            num[x] = i; // 固定第 x 位为 i
            dfs(x + 1); // 开启固定第 x + 1 位
        }
    }
}
//String 表示
class Solution {
    StringBuilder res;
    int nine = 0, count = 0, start, n;
    char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    public String printNumbers(int n) {
        this.n = n;
        res = new StringBuilder();
        num = new char[n];
        start = n - 1;
        dfs(0);
        res.deleteCharAt(res.length() - 1);
        return res.toString();
    }
    void dfs(int x) {
        if(x == n) {
            String s = String.valueOf(num).substring(start);
            if(!s.equals("0")) res.append(s + ",");
            if(n - start == nine) start--;
            return;
        }
        for(char i : loop) {
            if(i == '9') nine++;
            num[x] = i;
            dfs(x + 1);
        }
        nine--;
    }
}
//real
class Solution {
    int[] res;
    int nine = 0, count = 0, start, n;
    char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    public int[] printNumbers(int n) {
        this.n = n;
        res = new int[(int)Math.pow(10, n) - 1];
        num = new char[n];
        start = n - 1;
        dfs(0);
        return res;
    }
    void dfs(int x) {
        if(x == n) {
            String s = String.valueOf(num).substring(start);
            if(!s.equals("0")) res[count++] = Integer.parseInt(s);
            if(n - start == nine) start--;
            return;
        }
        for(char i : loop) {
            if(i == '9') nine++;
            num[x] = i;
            dfs(x + 1);
        }
        nine--;
    }
}

二叉树搜索树转链表

//中序遍历转链表
class Solution {
    Node pre, head;
    public Node treeToDoublyList(Node root) {
        if(root == null) return null;
        dfs(root);
        head.left = pre;
        pre.right = head;
        return head;
    }
    void dfs(Node cur) {
        if(cur == null) return;
        dfs(cur.left);
        if(pre != null) {
            pre.right = cur;
        } else {
            head = cur;
        }
        cur.left = pre; 
        pre = cur;
        dfs(cur.right);
    }
}

序列化二叉树

public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root == null) return "[]";
        StringBuilder res = new StringBuilder("[");
        Queue<TreeNode> queue = new LinkedList();
        queue.add(root);
        while(!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if(node!= null){
                res.append(node.val + ",");
                queue.add(node.left);
                queue.add(node.right);
            }else{
                res.append("null,");
            }
        }
        res.deleteCharAt(res.length() -1);
        res.append(']');
        return res.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data.equals("[]")) return null;
        String[] vals = data.substring(1, data.length() - 1).split(",");
        TreeNode root = new TreeNode(Integer.parseInt(vals[0]));
        Queue<TreeNode> queue = new LinkedList();
        queue.add(root);
        int i =1;
        while(!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if(!vals[i].equals("null")) {
                node.left = new TreeNode(Integer.parseInt(vals[i]));
                queue.add(node.left);
            }
            i++;
            if(!vals[i].equals("null")) {
                node.right = new TreeNode(Integer.parseInt(vals[i]));
                queue.add(node.right);
            }
            i++;
        }
        return root;
    }
}

数据流中的中位数

class MedianFinder {

    Queue<Integer> A;
    Queue<Integer> B;
    /** initialize your data structure here. */
    public MedianFinder() {
        A = new PriorityQueue();                     //小顶堆  由大到小
        B = new PriorityQueue<>((o1, o2)-> (o2 - o1));  //大顶堆   由小到大
    }

    public void addNum(int num) {
        if(A.size() != B.size()) {
            A.add(num);
            B.add(A.poll());
        } else {
            B.add(num);
            A.add(B.poll());
        }
    }

    public double findMedian() {
        return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0;
    }
}

数组中出现次数超过一半的数字

//多数的票数为+1 少数为-1
class Solution {
    public int majorityElement(int[] nums) {
        int x = 0, votes = 0;
        for(int num : nums){
            if(votes == 0) x = num;
            votes = vote + num == x ? 1 : -1;
        }
        return x;
    }
}

数组序列中某一位数字

class Solution {
    public int findNthDigit(int n) {
        int digit = 1;
        long start = 1;
        long count = 9;
        while (n > count) { // 1.
            n -= count;
            digit += 1;
            start *= 10;
            count = digit * start * 9;
        }
        long num = start + (n - 1) / digit; // 2.
        return Long.toString(num).charAt((n - 1) % digit) - '0'; // 3.
    }
}
//  数学
//           数字范围   位数   数字数量   数位数量
//            1 - 9      1        9          9
//           10 - 99     2        90         180
//          100 - 999    3        900        2700
//             ...      ...       ...        ...
//        start - end   digit    9*start   9*start*dight

class Solution {
public:
    int findNthDigit(int n) {
        int digit = 1;    // 数位
        long start = 1;   // 当前数字范围的左区间
        long count = 9;   // 数位数量

        // 定位目标数字所在数字范围
        while (n > count) {
            n -= count;       // 减去上一个数字范围的总数位数量
            digit += 1;       // "当前数字范围的"数位
            start *= 10;      // "当前数字范围的"左区间
            count = 9 * start * digit;   // "当前数字范围的"总数位数量
        }
        int num = (start - 1) + n / digit;   // 注:"start-1"表示上一个数字范围的右区间
        int y = n % digit;                   
        // 如果"余数y"不为0,说明目标数字为 num+1
        if (y) {    
            num += 1;
            string s = std::to_string(num);  
            return s[y - 1] - '0';
        }
        //否则,第n位数即"数字num的个位"
        return num % 10;
    }
};

1-n 中1的个数

image.png
image.png

class Solution {
    public int countDigitOne(int n) {
        // mulk 表示 10^k
        // 在下面的代码中,可以发现 k 并没有被直接使用到(都是使用 10^k)
        // 但为了让代码看起来更加直观,这里保留了 k
        long mulk = 1;
        int ans = 0;
        for (int k = 0; n >= mulk; ++k) {
            ans += (n / (mulk * 10)) * mulk + Math.min(Math.max(n % (mulk * 10) - mulk + 1, 0), mulk);
            mulk *= 10;
        }
        return ans;
    }
}

二叉树的后序遍历

//递归 n2
class Solution {
    public boolean verifyPostorder(int[] postorder) {
        return recur(postorder, 0, postorder.length - 1);
    }
    boolean recur(int[] postorder, int i, int j) {
        if(i >= j) return true;
        int p = i;
        while(postorder[p] < postorder[j]) p++;
        int m = p;
        while(postorder[p] > postorder[j]) p++;
        return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);
    }
}

//单调栈
//逆序的后序序列等效先序遍历
//单调栈维护二叉搜索树的右子树 因为大,出栈更新当前子树跟节点
//相当于root是一个MAXroot的左子树
class Solution {
    public boolean verifyPostorder(int[] postorder) {
        Deque<Integer> stack = new LinkedList();
        int root = Integer.MAX_VALUE;
        for(int i = postorder.length - 1; i >= 0; i--) {
            if(postorder[i] > root) return false;
            while(!stack.isEmpty() && stack.peek() > postorder[i]){
                root = stack.pop();
            }
            stack.push(postorder[i]);
        }
        return true;
    }
}

数组中的逆序对

class Solution {
    int count = 0;
    int[] aux;
    public int reversePairs(int[] nums) {
        aux = new int[nums.length];
        sort(nums, 0, nums.length - 1);
        return count;
    }

    public void sort(int[] nums, int lo, int hi){
        if(hi <= lo)    return;

        int mid = (lo + hi) / 2;
        sort(nums, lo, mid);
        sort(nums, mid + 1, hi);

        merge(nums, lo, mid, hi);
    }

    private void merge(int[] nums, int lo, int mid, int hi) {
        int i = lo, j = mid + 1;
        for(int k = lo; k <= hi; k++)
            aux[k] = nums[k];

        int index = lo;
        while(i <= mid || j <= hi){
            if(i > mid)                 nums[index++] = aux[j++];
            else if(j > hi)             nums[index++] = aux[i++];
            else if(aux[i] <= aux[j])   nums[index++] = aux[i++];
            else                        {nums[index++] = aux[j++];  count += mid - i + 1;}
        }
    }
}

把数字翻译成字符串

//回溯法
class Solution {
    int count = 0;
    int[] nums;
    public int translateNum(int num) {
        String str = String.valueOf(num);
        int[] nums = new int[str.length()];
        for(int i=0; i < str.length(); i++){
            nums[i]=Integer.parseInt(String.valueOf(str.charAt(i)));
        }
        this.nums = nums;
        back(0);
        return count;
    }
    public void back(int i) {
        if(i >= nums.length-1) {
            count++;
            return;
        }
        if(nums[i] <= 2 && i + 1 < nums.length ){
            if(nums[i] == 1||(nums[i]==2 && nums[i+1] <= 5)){
                back(i+2);
            }
        }
        back(i+1);
    }
}
//dp
class Solution {
    public int translateNum(int num) {
        if(num < 10) return 1;
        char[] chars = String.valueOf(num).toCharArray();
        int[] dp = new int[chars.length];

        dp[0] = 1; // 第一个格子只存在跳一格
        // 判断到第二格能否以跳两格的形式
        dp[1] = chars[0]-'0'==1 || (chars[0]-'0'==2 && chars[1]-'0'<6) ? 2 : 1; 

        for(int i = 2; i < dp.length; i++){
            int temp = chars[i-1] - '0';
            dp[i] = temp == 1 || (temp == 2 && chars[i]-'0'<6) ?  // 满足 >=10 <=25 即可跳两格
                        dp[i-1]+dp[i-2] : dp[i-1];
        }
        return dp[chars.length - 1];
    }
}

class Solution {
    public int translateNum(int num) {
        String s = String.valueOf(num);
        int a = 1, b = 1;
        for(int i = 2; i <= s.length(); i++) {
            String tmp = s.substring(i - 2, i);
            int c = tmp.compareTo("10") >= 0 && tmp.compareTo("25") <= 0 ? a + b : a;
            b = a;
            a = c;
        }
        return a;
    }
}

和为s的连续正序数列

class Solution {
    public int[][] findContinuousSequence(int target) {
        List<int[]> res = new ArrayList();
        int i = 1;
        int j = 2;
        int count = 3;
        while(i < j) {
            if(count == target) {
                int[] t = new int[j-i+1];
                for(int k = i; k <= j; k++){
                    t[k-i] = k;
                }
                res.add(t);
            }
            if(count < target){
                j++;
                count = count + j;
            }else{
                count = count - i;
                i++;
            }
        }
        return res.toArray(new int[0][]);
    }
}

数组中数字出现的次数

//2个
class Solution {
    public int[] singleNumbers(int[] nums) {
        int res = 0;
        int m = 1;
        int x = 0;
        int y = 0;
        for(int i : nums){
            res = res ^ i;
        }
        while((res&m) == 0) {
            m = m << 1;
        }
        for(int i : nums){
            if((i&m) == 0) {
                x = x ^ i;
            }else {
                y = y ^ i;
            }
        }
        return new int[]{x,y};
    }
}
//3个
//统计二进制每位的位数 有+1。则最后可以被3整除
class Solution {
    public int singleNumber(int[] nums) {
        int[] k = new int[32];
        for (int num : nums) {
            for (int i = 0; i < 32; i++) {
                k[i] = k[i] +(num & 1);
                num = num >> 1;
            }
        }
        int res = 0;
        for (int i = 0; i < 32; i++) {
            int temp = (k[i] % 3) << i;
            res = res ^ temp;
        }
        return res;
    }
}

把数组排成最小的数

class Solution {
    public String minNumber(int[] nums) {
        String[] strs = new String[nums.length];
        for(int i = 0; i < nums.length; i++) {
            strs[i] = String.valueOf(nums[i]);
        }
        Arrays.sort(strs, (x, y)-> (x+y).compareTo(y+x));
        StringBuilder res = new StringBuilder();
        for(String s: strs){
            res.append(s);
        }
        return res.toString();
    }
}

丑数

class Solution {
    public int nthUglyNumber(int n) {
        int a = 0, b = 0, c = 0;
        int[] dp = new int[n];
        dp[0] = 1;
        for(int i = 1; i < n; i++) {
            int n2 = dp[a] * 2, n3 = dp[b] * 3, n5 = dp[c] * 5;
            dp[i] = Math.min(Math.min(n2, n3), n5);
            if(dp[i] == n2) a++;
            if(dp[i] == n3) b++;
            if(dp[i] == n5) c++;
        }
        return dp[n - 1];
    }
}

在排序数组中查找数字I

//统计一个数字在排序数组中出现的次数
//输入: nums = [5,7,7,8,8,10], target = 8
//输出: 2
class Solution {
    public int search(int[] nums, int target) {
        return helper(nums, target) - helper(nums, target - 1);
    }
    int helper(int[] nums, int tar) {
        int i = 0, j = nums.length - 1;
        while(i <= j) {
            int m = (i + j) / 2;
            if(nums[m] <= tar) i = m + 1;
            else j = m - 1;
        }
        return i;
    }
}

0-n-1中缺失的数字

class Solution {
    public int missingNumber(int[] nums) {
        int i = 0;
        int j = nums.length-1;
        while(i <= j){
            int mid = (i+j)/2;
            if(nums[mid] != mid) {
                j = mid-1;
            }else{
                i = mid+1;
            }
        }
        return i;
    }
}

不用加减乘除做加法

class Solution {
    public int add(int a, int b) {
        while(b != 0) { // 当进位为 0 时跳出
            int c = (a & b) << 1;  // c = 进位
            a ^= b; // a = 非进位和
            b = c; // b = 进位
        }
        return a;
    }
}

圆圈中的最后一位数字

class Solution {
    public int lastRemaining(int n, int m) {
        //当只剩一个人时其下标必为0,从此反推即可
        //补上m个位置,然后模上当时的数组大小,得到其在此轮的下标位置
        int x = 0;
        for (int i = 2; i <= n; i++) {
            x = (x + m) % i;
        }
        return x;
    }
}

股票的最大利润

class Solution {
    public int maxProfit(int[] prices) {
        int cost = Integer.MAX_VALUE, profit = 0;
        for(int price : prices) {
            cost = Math.min(cost, price);
            profit = Math.max(profit, price - cost);
        }
        return profit;
    }
}

扑克牌中的顺子

class Solution {
    public boolean isStraight(int[] nums) {
        Set<Integer> repeat = new HashSet<>();
        int max = 0, min = 14;
        for(int num : nums) {
            if(num == 0) continue; // 跳过大小王
            max = Math.max(max, num); // 最大牌
            min = Math.min(min, num); // 最小牌
            if(repeat.contains(num)) return false; // 若有重复,提前返回 false
            repeat.add(num); // 添加此牌至 Set
        }
        return max - min < 5; // 最大牌 - 最小牌 < 5 则可构成顺子
    }
}

滑动窗口的最大值

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        //单调队列
        //下面是要注意的点:
        //队列按从大到小放入
        //如果首位值(即最大值)不在窗口区间,删除首位
        //如果新增的值小于队列尾部值,加到队列尾部
        //如果新增值大于队列尾部值,删除队列中比新增值小的值,如果在把新增值加入到队列中
        //如果新增值大于队列中所有值,删除所有,然后把新增值放到队列首位,保证队列一直是从大到小
        if (nums.length == 0)   return nums;

        Deque<Integer> deque = new LinkedList<>();
        int[] arr = new int[nums.length - k + 1];
        int index = 0;  //arr数组的下标
        //未形成窗口区间
        for (int i = 0; i < k; i++) {
            //队列不为空时,当前值与队列尾部值比较,如果大于,删除队列尾部值
            //一直循环删除到队列中的值都大于当前值,或者删到队列为空
            while (!deque.isEmpty() && nums[i] > deque.peekLast())  deque.removeLast();
            //执行完上面的循环后,队列中要么为空,要么值都比当前值大,然后就把当前值添加到队列中
            deque.addLast(nums[i]);
        }
        //窗口区间刚形成后,把队列首位值添加到队列中
        //因为窗口形成后,就需要把队列首位添加到数组中,而下面的循环是直接跳过这一步的,所以需要我们直接添加
        arr[index++] = deque.peekFirst();
        //窗口区间形成
        for (int i = k; i < nums.length; i++) {
            //i-k是已经在区间外了,如果首位等于nums[i-k],那么说明此时首位值已经不再区间内了,需要删除
            if (deque.peekFirst() == nums[i - k])   deque.removeFirst();
            //删除队列中比当前值大的值
            while (!deque.isEmpty() && nums[i] > deque.peekLast())  deque.removeLast();
            //把当前值添加到队列中
            deque.addLast(nums[i]);
            //把队列的首位值添加到arr数组中
            arr[index++] = deque.peekFirst();
        }
        return arr;
    }
}

队列的最大值

class MaxQueue {
    Deque<Integer> q1;
    Deque<Integer> q2;
    public MaxQueue() {
        q1 = new LinkedList();
        q2 = new LinkedList();
    }
    public int max_value() {
        if(q2.isEmpty()) {
            return -1;
        }
        return q2.peekFirst();
    }
    public void push_back(int value) {
        while(!q2.isEmpty() && value > q2.peekLast()){
            q2.removeLast();
        }
        q1.addLast(value);
        q2.addLast(value);
    }

    public int pop_front() {
        if(q1.isEmpty()) {
            return -1;
        }
        if(q1.peekFirst().equals(q2.peekFirst())) {
            q2.removeFirst();
        }
        return q1.removeFirst();
    }
}

n个骰子的点数

class Solution {
    public double[] dicesProbability(int n) {
        double[] dp = new double[6];
        Arrays.fill(dp, 1.0 / 6.0);
        for (int i = 2; i <= n; i++) {
            double[] tmp = new double[5 * i + 1];
            for (int j = 0; j < dp.length; j++) {
                for (int k = 0; k < 6; k++) {
                    tmp[j + k] += dp[j] / 6.0;
                }
            }
            dp = tmp;
        }
        return dp;
    }
}

//2
public double[] dicesProbability(int n) {
        double res[] = new double[n*5+1];
        double dp[][] = new double[n+1][n*6+1];
        for(int i = 1;i <= 6;i++){
            dp[1][i] = 1.0/6;
        }
        for(int i = 2;i <= n;i++){
            for(int j = i;j <= i*6;j++){
                for(int k = 1;k <= 6;k++){
                    if(j-k > 0)
                        dp[i][j] += dp[i-1][j-k]/6;
                    else
                        break;
                }
            }
        }
        for(int i = 0;i <= 5*n;i++){
            res[i] = dp[n][n+i];
        }
        return res;
}
//1-1
public double[] dicesProbability(int n) {
        //因为最后的结果只与前一个动态转移数组有关,所以这里只需要设置一个一维的动态转移数组
        //原本dp[i][j]表示的是前i个骰子的点数之和为j的概率,现在只需要最后的状态的数组,所以就只用一个一维数组dp[j]表示n个骰子下每个结果的概率。
        //初始是1个骰子情况下的点数之和情况,就只有6个结果,所以用dp的初始化的size是6个
        double[] dp = new double[6];
        //只有一个数组
        Arrays.fill(dp,1.0/6.0);
        //从第2个骰子开始,这里n表示n个骰子,先从第二个的情况算起,然后再逐步求3个、4个···n个的情况
        //i表示当总共i个骰子时的结果
        for(int i=2;i<=n;i++){
        //每次的点数之和范围会有点变化,点数之和的值最大是i*6,最小是i*1,i之前的结果值是不会出现的;
        //比如i=3个骰子时,最小就是3了,不可能是2和1,所以点数之和的值的个数是6*i-(i-1),化简:5*i+1
            //当有i个骰子时的点数之和的值数组先假定是temp
            double[] temp = new double[5*i+1];
            //从i-1个骰子的点数之和的值数组入手,计算i个骰子的点数之和数组的值
            //先拿i-1个骰子的点数之和数组的第j个值,它所影响的是i个骰子时的temp[j+k]的值
            for(int j=0;j<dp.length;j++){
            //比如只有1个骰子时,dp[1]是代表当骰子点数之和为2时的概率,它会对当有2个骰子时的点数之和为3、4、5、6、7、8产生影响,因为当有一个骰子的值为2时,另一个骰子的值可以为1~6,产生的点数之和相应的就是3~8;比如dp[2]代表点数之和为3,它会对有2个骰子时的点数之和为4、5、6、7、8、9产生影响;所以k在这里就是对应着第i个骰子出现时可能出现六种情况,这里可能画一个K神那样的动态规划逆推的图就好理解很多
                for(int k=0;k<6;k++){
                    //这里记得是加上dp数组值与1/6的乘积,1/6是第i个骰子投出某个值的概率
                    temp[j+k]+=dp[j]*(1.0/6.0);
                }
            }
            //i个骰子的点数之和全都算出来后,要将temp数组移交给dp数组,dp数组就会代表i个骰子时的可能出现的点数之和的概率;用于计算i+1个骰子时的点数之和的概率
            dp = temp;
        }
        return dp;
    }

构建乘积数组

输入: [1,2,3,4,5]
输出: [120,60,40,30,24]
class Solution {
    public int[] constructArr(int[] a) {
        //记录左右前缀和 相乘
        if (a == null || a.length == 0) return a;
        int len = a.length;
        int[] left = new int[len];
        int[] right = new int[len];
        left[0] = right[len - 1] = 1;

        for (int i = 1; i < len; i++) {
            left[i] = left[i - 1] * a[i - 1];
        }
        for (int i = len - 2; i >= 0; i--) {
            right[i] = right[i + 1] * a[i + 1];
        }

        int[] ans = new int[len];
        for (int i = 0; i < len; i++) {
            ans[i] = left[i] * right[i];
        }
        return ans;
    }
}