剑指 offer 代码+资料 + 具体安排(完结)

PS:为了阅读体验更好,请大家前往这里阅读:《剑指offer最优解》题解
PS:为了阅读体验更好,请大家前往这里阅读:《剑指offer最优解》题解
PS:为了阅读体验更好,请大家前往这里阅读:《剑指offer最优解》题解

这个 PDF 整理了《剑指offer》所有实现代码 + 对应的视频讲解链接以及原题链接,《剑指offer》基本属于面试 必刷,希望这个视频,能够帮助你更好着去理解剑指offer。

本资料已经整理成PDF,可以在帅地的公众号「帅地玩编程」中回复「剑指offer」获取PDF版本哦。

本系列配套视频讲解可以前往帅地的B站看:完美撒花!剑指offer题解合集(完结)|有字幕+动画+现场手撕代码|Java实现

提醒:由于网站原因,代码中的一些 = = 或者 < > 会被吃掉,有时候你们就脑补一下了。(看到的我都处理了)

开篇:聊一聊算法面试

这个视频讲解了算法面试相关的一些事情:开篇:聊一聊算法面试

剑指offer 3~15题

剑指 Offer 03. 数组中重复的数字

视频讲解直达:本题视频讲解

    public int findRepeatNumber(int[] nums) {
        // 遍历数组
        for(int i = 0; i < nums.length; i++) {
            // 之所以用while,是因为交换之后,该位置的元素任然没有在正确的位置
            while(i != nums[i]){
                if(nums[i] == nums[nums[i]]){
                    return nums[i];
                }
                // nums[i] 正确的位置在 nums[nums[i]]
                int k = nums[nums[i]];
                nums[nums[i]] = nums[i];
                nums[i] = k;
            }
        }
        return -1;
    }

剑指 Offer 04. 二维数组中的查找

视频讲解直达: 本题视频讲解

       if(matrix == null || matrix.length <= 0 || matrix[0].length <= 0){
            return false;
        }
        // 这里 cols 表示多少行的意思(但是有人说 cols 是表示列,那我可能记错了)
        int cols = matrix.length;
        // rows 表示多少列的意思
        int rows = matrix[0].length;
        // 左下角的位置
        int col = cols - 1;
        int row = 0;
        // 向上向右走的过程中不能出界
        while(col >= 0 && row < rows){
            if(target > matrix[col][row]){
                row++;
            } else if(target < matrix[col][row]){
                col--;
            } else {
                return true;
            }
        }
        return false;
    }

剑指 Offer 05. 替换空格

视频讲解直达: 本题视频讲解

    // Java中的常规做法
    // public String replaceSpace(String s) {
    //     // 常规
    //     StringBuilder builder = new StringBuilder();
    //     for(int i = 0; i < s.length(); i++){
    //         if(s.charAt(i) == ' '){
    //             builder.append("%20");
    //         } else {
    //             builder.append(s.charAt(i));
    //         }
    //     }
    //     return builder.toString();
    // }

    //这个做法是模拟原地扩容的,但是Java并不支持扩容,所以我们只是联系一下代码怎么写
    public String replaceSpace(String s) {
        // 统计有多少空格
        int count = 0;
        for(int i = 0; i < s.length(); i++){
            if(s.charAt(i) == ' '){
                count ++;
            }
        }

        char[] res = new char[s.length() + count * 2];
        int k = res.length - 1;
        // 从右往左遍历
        for(int i = s.length() - 1; i >= 0; i--){
            // 从右往左移动字符与替换字符
            if(s.charAt(i) == ' '){
                res[k--] = '0';
                res[k--] = '2';
                res[k--] = '%';
            } else {
                res[k--] = s.charAt(i);
            }
        }
        return new String(res);
    }

剑指 Offer 06. 从尾到头打印链表

视频讲解直达: 本题视频讲解

    // 从右边=往左填充
    public int[] reversePrint(ListNode head) {
        if(head == null)
            return new int[0];
        // 统计链表节点个数,方便创建数组
        int count = 0;
        ListNode temp = head;
        while(temp != null){
            count++;
            temp = temp.next;
        }
        int[] res = new int[count];
        int k = count - 1;
        // 从由往左填充数组
        while(head != null){
            res[k--] = head.val;
            head = head.next;
        }

        return res;
    }

剑指 Offer 07. 重建二叉树

视频讲解直达: 本题视频讲解

    Map< Integer, Integer > map = new HashMap();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder == null || preorder.length <= 0){
            return null;
        }
        // 简历中序遍历数组的映射(就是为了快速求出某个元素的下标)
        for(int i = 0; i < inorder.length; i++) {
            map.put(inorder[i], i);
        }

        TreeNode root = f(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);

        return root;
    }

    TreeNode f(int[] preorder, int l1, int r1, int[] inorder, int l2, int r2) {
        // 前序遍历或者中序遍历为空时,表示这棵树不存在,直接返回 null
        if( l1 > r1 || l2 > r2){
            return null;
        }
        // 根节点
        TreeNode root = new TreeNode(preorder[l1]);
        // 根节点在中序遍历中的下标
        int i = map.get(preorder[l1]);
        // 递归求解
        root.left = f(preorder, l1 + 1, l1 + (i - l2), inorder, l2, i - 1);
        root.right = f(preorder, l1 + (i - l2) + 1, r1, inorder, i + 1, r2);

        return root;
    }

剑指 Offer 09. 用两个栈实现队列

视频讲解直达: 本题视频讲解

class CQueue {
    Stack< Integer > stack1;
    Stack< Integer > stack2;
    public CQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();

    }

    public void appendTail(int value) {
        stack1.push(value);

    }

    public int deleteHead() {
        if(!stack2.isEmpty()){
            return stack2.pop();
        }

        if(!stack1.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }

            return stack2.pop();
        }

        return -1;

    }
}

剑指 Offer 10- I. 斐波那契数列

视频讲解直达: 本题视频讲解

    public int fib(int n) {
        if( n <= 1){
            return n;
        }

        int a = 0;
        int b = 1;
        int c = 0;;

        for(int i = 2; i <= n; i++){
            c = (a + b) % 1000000007;
          // a 原本指向的值不会在用到,所以让 a 保存 b,b 保存 c,之后重新计算 c,如此循环
            a = b;
            b = c;
        }

        return c;
    }

剑指 Offer 10- II. 青蛙跳台阶问题

视频讲解直达: 本题视频讲解

    // 如果看不懂代码为啥这样写,可以参考上一题
    public int numWays(int n) {
        //递归公示 f(n) = f(n - 1) + f(n - 2);
        if( n <= 1)
            return 1;
        int a = 1;
        int b = 1;
        int c = 0;
        for(int i = 2; i <= n; i++){
            c = (a + b) % 1000000007;
            a = b;
            b = c;
        }

        return c;
    }

剑指 Offer 11. 旋转数组的最小数字

视频讲解直达: 本题视频讲解

    public int minArray(int[] numbers) {
        // O(n)
        // logN O1
        int l = 0;
        int r = numbers.length - 1;
        while(l < r) {
            if(numbers[l] < numbers[r]){
                return numbers[l];
            }

            int mid = (r + l) / 2;
            if(numbers[mid] > numbers[l]){
                l = mid + 1;
            } else if(numbers[mid] < numbers[l]){
                r = mid;
            } else {
                l++;
            }
        }

        return numbers[l];
    }

剑指 Offer 12. 矩阵中的路径

视频讲解直达: 本题视频讲解

class Solution {
    int n;
    int m;
    int len;
    boolean[][] visited;
    public boolean exist(char[][] board, String word) {
        this.n = board.length;
        this.m = board[0].length;
        this.len = word.length();
        visited = new boolean[n][m];

        for(int i = 0; i < n; i++){
            for(int j = 0; j < m; j++){
                if(dsf(board, i, j, word, 0)){
                    return true;
                }
            }
        }

        return false;
    }
    boolean dsf(char[][] board, int i, int j, String word, int k){
        // 判断是否越界,已经访问过或者不匹配
        if(i < 0 || i >= n || j < 0 || j >= m || board[i][j] != word.charAt(k)){
            return false;
        }

        if(k == len - 1){
            return true;
        }
        //visited[i][j] = true;
        board[i][j] = '\n';
        // 四个方向都搜索一下
        boolean res = dsf(board, i, j + 1, word, k + 1) ||
                      dsf(board, i + 1, j, word, k + 1) ||
                      dsf(board, i, j - 1, word, k + 1) ||
                      dsf(board, i - 1, j, word, k + 1);

        board[i][j] = word.charAt(k);
        return res;
        // 时间复杂度:mn * 3^len
    }
}

剑指 Offer 13. 机器人的运动范围

视频讲解直达: 本题视频讲解

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

        return dfs(0, 0);
    }

    int dfs(int i, int j){
        // 是否在方格之内或者是否访问过或者是否是障碍物
        if(i < 0 || j < 0 || i >= m || j >= n || visited[i][j] || k < sum(i) + sum(j)){
            return 0;
        }

        visited[i][j] = true;
        return 1 + dfs(i + 1, j) + dfs(i, j + 1);
    }

    int sum(int x){
        int res = 0;
        while(x != 0){
            res = res + x % 10;
            x = x / 10;
        }
        return res;
    }
}

剑指 Offer 14- I. 剪绳子

视频讲解直达: 本题视频讲解

    public int cuttingRope(int n) {
        if(n <= 2){
            return 1;
        }
        if(n == 3){
            return 2;
        }

        int res = n / 3;
        int mod = n % 3;

        if(mod == 0){
            return pow(3, res);
        } else if(mod == 1){
            return pow(3, res - 1) * 4;
        } else {
            return pow(3, res) * 2;
        }
    }
    // 这里多余了,其实直接调用Math.pow就可以了
    int pow(int a, int n){
        int sum = 1;
        for(int i = 1; i <= n; i ++){
            sum = sum * a;
        }
        return sum;
    }

剑指 Offer 14- II. 剪绳子 II

视频讲解直达: 本题视频讲解

    public int cuttingRope(int n) {
        if(n <= 2){
            return 1;
        }
        if(n == 3){
            return 2;
        }

        int res = n / 3;
        int mod = n % 3;
        int p = 1000000007;

        if(mod == 0){
            return (int)pow(3, res);
        } else if(mod == 1){
            return (int)(pow(3, res - 1) * 4 % p);
        } else {
            return (int)(pow(3, res) * 2 % 1000000007);
        }
    }

    //求 a^n %p
    long pow(int a, int n){
        long res = 1;
        int p = 1000000007;
        for(int i= 1; i <= n; i++){
            res = (res * a) % p;
        }

        return res;
    }

剑指 Offer 15. 二进制中1的个数

视频讲解直达: 本题视频讲解

    public int hammingWeight(int n) {
        int sum = 0;
        while(n != 0){
          // n & (n - 1)可以消除n最右边的一个 1(二进制表示)
            n = n & (n - 1);
            sum ++;
        }
        return sum;
    }

剑指offer 16~30题

剑指 Offer 16. 数值的整数次方

视频讲解直达: 本题视频讲解

    public double myPow(double x, int n) {
        double res = 1;
        long y = n;// 不能用 int。
        if(n < 0){
            y = -y;
            x = 1 / x;
        }
        while(y > 0){
            if(y % 2 == 1){
                res = res * x;
            }

            x = x * x;
            y = y / 2;
        }
        return res;
    }

剑指 Offer 17. 打印从1到最大的n位

视频讲解直达: 本题视频讲解

    public int[] printNumbers(int n) {
        int sum = (int)Math.pow(10, n);
        int[] res = new int[sum - 1];
        for(int i = 1; i <= sum - 1; i++){
            res[i - 1] = i;
        }

        return res;
    }

剑指 Offer 18. 删除链表的节点

视频讲解直达: 本题视频讲解

   public ListNode deleteNode(ListNode head, int val) {
        if(head == null){
            return null;
        }

        if(head.val == val){
            return head.next;
        }

        ListNode temp = head.next;
        ListNode pre = head;

        while(temp != null){
            if(temp.val == val){
                pre.next = temp.next;
                return head;
            }
            temp = temp.next;
            pre = pre.next;
        }

        return head;
    }

剑指 Offer 19. 正则表达式匹配

视频讲解直达: 本题视频讲解

class Solution {
    public boolean isMatch(String s, String p) {
        if(s == null || p == null){
            return true;
        }
        int n = s.length();
        int m = p.length();
        boolean[][] dp = new boolean[n+1][m+1];
        // 初始化
        dp[0][0] = true;
        for(int j = 2; j <= m; j++){
            if(p.charAt(j - 1) == '*'){
                dp[0][j] = dp[0][j - 2];
            }
        }

        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                // 当j不为 *
                if(p.charAt(j - 1) != '*'){
                    // 如果不匹配
                    if(p.charAt(j-1) == '.' || p.charAt(j-1) == s.charAt(i - 1)){
                        dp[i][j] = dp[i-1][j-1];
                    }
                } else {
                    // 第j-1个字符不匹配
                    if(p.charAt(j - 2) != s.charAt(i - 1) && p.charAt(j-2) != '.'){
                        dp[i][j] = dp[i][j-2];
                    } else {
                        dp[i][j] = dp[i][j-2] || dp[i][j-1] || dp[i-1][j];
                    }
                }
            }
        }

        return dp[n][m];
    }
}

剑指 Offer 20. 表示数值的字符串

视频讲解直达: 本题视频讲解

class Solution {
    public boolean isNumber(String s) {
        //有限状态机
        // 2.小数点 3.E/e 4. 数字字符 5. -+
        if(s == null || s.length() <= 0){
            return false;
        }
        char[] res = s.trim().toCharArray();
        if(res.length <= 0) return false;

        int n = res.length;

        boolean is_dot = false;
        boolean is_e_or_E = false;
        boolean is_num = false;
        for(int i = 0; i < n; i++){
            if(res[i] >= '0' && res[i] <= '9'){
                is_num = true;
            } else if(res[i] == '.'){
                //-+ 8.  8.8  .8
                // 前面:不能有重复的小数点,也不能出现 e/E
                if(is_dot || is_e_or_E){
                    return false;
                }
                is_dot = true;
            } else if(res[i] == 'e' || res[i] == 'E'){
                // 前面必须要有一个数字 || 前面不能出现重复的 e/E
                if(is_e_or_E || !is_num){
                    return false;
                }
                is_e_or_E = true;
                is_num =false;//11E+ 11E
            } else if(res[i] == '-' || res[i] == '+'){
                if(i!=0 && res[i-1] != 'e' && res[i-1] != 'E'){
                    return false;
                }
            } else {
                return false;
            }
        }

        return is_num;
    }
}

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

视频讲解直达: 本题视频讲解

class Solution {
    public int[] exchange(int[] nums) {
        if(nums == null || nums.length == 0){
            return nums;
        }
        int left = 0;
        int right = nums.length - 1;
        while(left < right){
            while(left < right && nums[left] % 2 != 0) left++;
            while(left < right && nums[right] % 2 != 1) right--;

            int temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp;
        }

        return nums;
    }
}

剑指 Offer 22. 链表中倒数第k个节

视频讲解直达: 本题视频讲解

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        if(head == null){
            return null;
        }

        ListNode fast = head, slow = head;
        for(int i = 0; i < k; i++){
            if(fast == null){
                return null;
            }
            fast = fast.next;
        }

        while(fast != null){
            fast = fast.next;
            slow = slow.next;
        }

        return slow;
    }
}

剑指 Offer 24. 反转链表

视频讲解直达: 本题视频讲解

class Solution {
    // 原地反转
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }

        ListNode cur = head, pre = null;

        while(cur != null){
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }

        return pre;

        // 额外空间复杂度=1
    }

  // 递归
      public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        // 反转子链表
        ListNode temp = reverseList(head.next);
        head.next.next = head;
        head.next = null;

        return temp;
    }
}

剑指 Offer 25. 合并两个排序的链表

视频讲解直达: 本题视频讲解

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode merge = new ListNode(0);
        ListNode temp = merge;

        while(l1 != null && l2 != null){
            if(l1.val < l2.val){

                temp.next = l1;
                l1 = l1.next;
            } else {
                temp.next = l2;
                l2 = l2.next;
            }

            temp = temp.next;
        }

        temp.next = l1 == null ? l2 : l1;

        return merge.next;
    }
}

剑指 Offer 26. 树的子结构

视频讲解直达: 本题视频讲解

class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(A == null || B == null){
            return false;
        }

        if(isSubTree(A, B)){
            return true;
        }

        if(isSubStructure(A.left, B) || isSubStructure(A.right, B)){
            return true;
        }

        return false;
    }

    public boolean isSubTree(TreeNode Ta, TreeNode Tb){
        if(Tb == null){
            return true;
        }

        if(Ta == null){
            return false;
        }

        if(Ta.val != Tb.val){
            return false;
        }

        return isSubTree(Ta.left, Tb.left) &&
               isSubTree(Ta.right, Tb.right);
    }
}

剑指 Offer 27. 二叉树的镜像

视频讲解直达: 本题视频讲解

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null || (root.left == null && root.right == null)){
            return root;
        }

        TreeNode left = mirrorTree(root.left);
        TreeNode right = mirrorTree(root.right);

        root.left = right;
        root.right = left;

        return root;
    }
}

剑指 Offer 28. 对称的二叉树

视频讲解直达: 本题视频讲解

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null || (root.left == null && root.right == null)){
            return true;
        }

        return f(root.left, root.right);
    }
    // 判断B是不是以A阶段为根节点的子树
    public boolean f(TreeNode A, TreeNode B){
        if(A == null && B == null){
            return true;
        }

        if(A == null || B == null){
            return false;
        }

        if(A.val != B.val){
            return false;
        }

        return f(A.left, B.right) && f(A.right, B.left);
    }
}

剑指 Offer 29. 顺时针打印矩阵

视频讲解直达: 本题视频讲解

class Solution {
    public int[] spiralOrder(int[][] matrix) {
        if(matrix == null || matrix.length == 0 || matrix[0].length == 0){
            return new int[0];
        }

        int l = 0, r = matrix[0].length - 1, t = 0, b = matrix.length - 1;
        int[] res = new int[(r+1) * (b+1)];
        int k = 0;
        while(true){
            // 从左到右
            for(int i = t,j = l; j <= r; j++){
                res[k++] = matrix[i][j];
            }
            if(++t > b) break;
            // 从下往上
            for(int i = t, j = r; i <= b; i++){
                res[k++] = matrix[i][j];
            }
            if(l > --r) break;
            // 从右往左
            for(int i = b, j = r; j >= l; j--){
                res[k++] = matrix[i][j];
            }
            if(t > --b) break;
            // 从下往上
            for(int i = b, j = l; i >= t; i--){
                res[k++] = matrix[i][j];
            }
            if(++l > r) break;
        }
        return res;
    }
}

剑指 Offer 30. 包含min函数的栈

视频讲解直达: 本题视频讲解

class MinStack {
    Stack stack1;
    Stack stack2;

    /** initialize your data structure here. */
    public MinStack() {
        this.stack1 = new Stack();
        this.stack2 = new Stack();
    }

    public void push(int x) {
        stack1.push(x);
        if(stack2.isEmpty() || x <= stack2.peek().intValue()){
            stack2.push(x);
        }
    }

    public void pop() {
        if(!stack1.isEmpty()){
            //Integer, 数值 > 127 I
            if(stack1.peek().intValue() == stack2.peek().intValue()){
                stack2.pop();
            }
            stack1.pop();
        }
    }

    public int top() {
        return stack1.peek();

    }

    public int min() {
        return stack2.peek();
    }
}

剑指offer 31~45题

剑指 Offer 31. 栈的压入、弹出序列

视频讲解直达: 本题视频讲解

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        if(pushed == null || pushed.length <= 0){
            return true;
        }
        int k = 0;
        Stack stack = new Stack();
        for(int i = 0; i < pushed.length; i++){
            stack.push(pushed[i]);
            while(!stack.isEmpty() && stack.peek() == popped[k]){
                stack.pop();
                k++;
            }
        }
        return stack.isEmpty();
    }
}

剑指 Offer 32 - I. 从上到下打印二叉树

视频讲解直达: 本题视频讲解

class Solution {
    public int[] levelOrder(TreeNode root) {
        if(root == null){
            return new int[0];
        }

        Queue queue = new LinkedList<>();
        List res = new ArrayList<>();

        queue.add(root);
        while(!queue.isEmpty()){
            TreeNode t = queue.poll();
            res.add(t.val);
            if(t.left != null) queue.add(t.left);
            if(t.right != null) queue.add(t.right);
        }

        int[] arr = new int[res.size()];
        for(int i = 0; i < res.size(); i++){
            arr[i] = res.get(i);
        }

        return arr;

    }
}

剑指 Offer 32 - II. 从上到下打印二叉树

视频讲解直达: 本题视频讲解

class Solution {
    public List> levelOrder(TreeNode root) {
        if(root == null){
            return new ArrayList<>();
        }

        Queue queue = new LinkedList<>();
        List> res = new ArrayList<>();

        queue.add(root);
        while(!queue.isEmpty()){
            int k = queue.size();
            List tmp = new ArrayList<>();
            for(int i = 0; i < k; i++){
                TreeNode t = queue.poll();
                tmp.add(t.val);
                if(t.left != null) queue.add(t.left);
                if(t.right != null) queue.add(t.right);
            }
            res.add(tmp);
        }

        return res;
    }
}

剑指 Offer 32 - III. 从上到下打印二叉树

视频讲解直达: 本题视频讲解

class Solution {
    public List> levelOrder(TreeNode root) {
        if(root == null){
            return new ArrayList<>();
        }

        Queue queue = new LinkedList<>();
        List> res = new ArrayList<>();
        int sum = 1;
        queue.add(root);
        while(!queue.isEmpty()){
            int k = queue.size();
            LinkedList tmp = new LinkedList<>();
            for(int i = 0; i < k; i++){
                TreeNode t = queue.poll();
                if(sum % 2 == 1){
                    tmp.add(t.val);
                } else {
                    tmp.addFirst(t.val);
                }

                if(t.left != null) queue.add(t.left);
                if(t.right != null) queue.add(t.right);
            }
            res.add(tmp);
            sum ++;
        }

        return res;
    }
}

剑指 Offer 33. 二叉搜索树的后序遍历序列

视频讲解直达: 本题视频讲解

class Solution {
    public boolean verifyPostorder(int[] postorder) {
        if(postorder == null){
            return true;
        }

        return f(postorder, 0, postorder.length - 1);
    }

    boolean f(int[] postorder, int i, int j){
        if(i >= j){
            return true;
        }

        int root = postorder[j];
        int p = i;
        // 获取第一个大于或者等于 root 等元素的位置
        while(postorder[p] < root) p++;
        // 判断 p ~ j -1 这个范围是否存在小于root的元素
        for(int k = p; k < j; k++){
            if(postorder[k] < root){
                return false;
            }
        }

        return f(postorder, i, p - 1) && f(postorder, p, j - 1);
    }
}

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

视频讲解直达: 本题视频讲解

class Solution {
    List> res;
    List tmp;
    public List> pathSum(TreeNode root, int target) {
        res = new ArrayList<>();
        tmp = new ArrayList<>();
        dsf(root, target);
        return res;
    }
    void dsf(TreeNode root, int target){
        if(root == null){
            return;
        }

        tmp.add(root.val);
        target = target - root.val;
        if(root.left == null && root.right == null && target == 0){
            res.add(new ArrayList<>(tmp));
        }
        dsf(root.left, target);
        dsf(root.right, target);

        tmp.remove(tmp.size() - 1);

    }
}

剑指 Offer 35. 复杂链表的复制

视频讲解直达: 本题视频讲解

class Solution {
    public Node copyRandomList(Node head) {
        if(head == null){
            return null;
        }
        // 复制链表节点
        Node cur = head;
        while(cur != null){
            Node next = cur.next;
            cur.next = new Node(cur.val);
            cur.next.next = next;
            cur = next;
        }

        // 复制随机节点
        cur = head;
        while(cur != null){
            Node curNew = cur.next;
            curNew.random = cur.random == null ? null : cur.random.next;
            cur = cur.next.next;
        }

        // 拆分,比如把 A->A1->B->B1->C->C1拆分成 A->B->C和A1->B1->C1
        Node headNew = head.next;
        cur = head;
        Node curNew = head.next;
        while(cur != null){
            cur.next = cur.next.next;
            cur = cur.next;
            curNew.next = cur == null ? null : cur.next;
            curNew = curNew.next;
        }

        return headNew;
    }
}

剑指 Offer 36. 二叉搜索树与双向链表

视频讲解直达: 本题视频讲解

class Solution {
    public Node treeToDoublyList(Node root) {
        if(root == null){
            return null;
        }

        Queue queue = new LinkedList<>();
        inOrder(root, queue);
        Node head = queue.poll();
        Node pre = head;

        while(!queue.isEmpty()){
            Node cur = queue.poll();
            pre.right = cur;
            cur.left = pre;
            pre = cur;
        }

        pre.right = head;
        head.left = pre;

        return head;
    }

    void inOrder(Node root, Queue queue){
        if(root == null){
            return;
        }
        inOrder(root.left, queue);
        queue.add(root);
        inOrder(root.right, queue);
    }
}

剑指 Offer 37. 序列化二叉树

视频讲解直达: 本题视频讲解

public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        // 序列化成字符串,1,2,3,null,null,4,5,null...
        if(root == null){
            return "";
        }
        StringBuilder build = new StringBuilder();
        Queue queue = new LinkedList<>();
        queue.add(root);

        while(!queue.isEmpty()){
            TreeNode t = queue.poll();
            if(t != null){
                queue.add(t.left);
                queue.add(t.right);
                build.append(t.val + ",");
            }else{
                build.append("null,");
            }
        }

        return build.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        // 1,2,3,null,null,4,5,null...
        if(data == null || data.length() <= 0){
            return null;
        }
        String[] s = data.split(",");
        Queue queue = new LinkedList<>();
        TreeNode root = new TreeNode(Integer.parseInt(s[0]));
        queue.add(root);
        int i = 1;
        while(!queue.isEmpty()){
            TreeNode t = queue.poll();
            // 构建做左节点
            if(!s[i].equals("null")){
                TreeNode left = new TreeNode(Integer.parseInt(s[i]));
                t.left = left;
                queue.add(left);
            }
            i++;
            // 构建右节点
            if(!s[i].equals("null")){
                TreeNode right = new TreeNode(Integer.parseInt(s[i]));
                t.right = right;
                queue.add(right);
            }
            i++;
        }
        return root;

    }
}

剑指 Offer 38. 字符串的排列

视频讲解直达: 本题视频讲解

class Solution {
    List path;
    List res;
    boolean[] visited;
    public String[] permutation(String s) {
        this.path = new ArrayList<>();
        this.res = new ArrayList<>();
        this.visited = new boolean[s.length()];

        char[] arr = s.toCharArray();
        Arrays.sort(arr);
        dfs(arr, 0);

        String[] ss = new String[res.size()];
        for(int i = 0; i < res.size(); i++){
            ss[i] = res.get(i);
        }

        return ss;

    }
    // 时间复杂度:O(n*n!) 空间:N,N,递归调用最大深度也是 N,3n,O(n)
    void dfs(char[] arr, int k){
        if(arr.length == k){
           res.add(listToString(path));
           return;
        }

        //进行N叉树搜索
        for(int i = 0; i < arr.length; i++){
            // 剪枝 aab
            if(i > 0 && arr[i] == arr[i - 1] && visited[i - 1] == false){
                continue;
            }
            if(visited[i] == false){
                // 递归访问
                visited[i] = true;
                path.add(arr[i]);
                dfs(arr, k + 1);
                path.remove(path.size() - 1);
                visited[i] = false;
            }
        }
    }

    String listToString(List list){
        StringBuilder b = new StringBuilder();
        for(int i = 0; i < list.size(); i++){
            b.append(list.get(i));
        }

        return b.toString();
    }
}

剑指 Offer 39. 数组中出现次数超过一半的数字

视频讲解直达: 本题视频讲解

class Solution {
    public int majorityElement(int[] nums) {
        if(nums.length <= 2){
            return nums[0];
        }

        int x = nums[0];
        int sum = 1;

        for(int i = 1; i < nums.length; i++){
            if(sum == 0){
                x = nums[i];
                sum = 1;
            } else {
                if(x == nums[i]){
                    sum++;
                } else {
                    sum--;
                }
            }
        }

        return x;
    }
}

剑指 Offer 40. 最小的k个数

视频讲解直达: 本题视频讲解

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if(arr == null || arr.length == 0 || k == 0){
            return new int[0];
        }

        return quickFind(arr, 0, arr.length - 1, k);
    }

    int[] quickFind(int[] arr, int left, int right, int k){
        int i = partition(arr, left, right);
        // 之所以需要 i+1,是因为下标从 0 开始,0~i之间一共有 i+1个数
        if(i + 1 == k){
            return Arrays.copyOf(arr, k);
        }

        if(i + 1 > k){
            return quickFind(arr, 0, i - 1, k);
        } else {
            return quickFind(arr, i + 1, right, k);
        }
    }
    // 找出pivot的下标以及使小于等于pivot在左边,大于等于的在右边
    int partition(int[] arr, int left, int right){
        int pivot = arr[left];

        int i = left + 1;
        int j = right;

        while(i < j){
            while(i <= j && arr[i] <= pivot) i++;
            while(i <= j && arr[j] >= pivot) j--;
            if(i >= j){
                break;
            }

            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }

        arr[left] = arr[j];
        arr[j] = pivot;
        return j;
    }
}

剑指 Offer 41. 数据流中的中位数

视频讲解直达: 本题视频讲解

class MedianFinder {
    Queue min, max;
    /** initialize your data structure here. */
    public MedianFinder() {
        min = new PriorityQueue<>(); // 小根,保存较大的
        max = new PriorityQueue<>((x, y) -> (y - x));// 大根
    }

    public void addNum(int num) {
        // 如果是偶数
        if(min.size() == max.size()){
            min.add(num);
            max.add(min.poll());
        } else {
            max.add(num);
            min.add(max.poll());
        }
    }

    public double findMedian() {
        // 如果是偶数
        if(min.size() == max.size()){
            return (min.peek() + max.peek()) / 2.0;
        } else {
            return max.peek() * 1.0;
        }
    }
}

剑指 Offer 42. 连续子数组的最大和

视频讲解直达: 本题视频讲解

class Solution {
    public int maxSubArray(int[] nums) {
        int dp = nums[0];
        int max = nums[0];
        // 刷新dp之前,dp相当于是 dp[i-1],刷新之后,Dp就是dp[i]
        for(int i = 1; i < nums.length; i++){
            dp = Math.max(dp + nums[i], nums[i]);
            max = Math.max(max, dp);
        }

        return max;
    }
}

剑指 Offer 43. 1~n 整数中 1 出现的次数

视频讲解直达: 本题视频讲解

class Solution {
    public int countDigitOne(int n) {
        // 几个变量计算:cur = (n/bit)%10, low = n % bit, high = n / bit / 10
        // 几个公示
        // cur > 1 => (high + 1) * bit
        // cur == 1 =>  (high * bit) + (1 + low)
        // cur = 0 => high * bit

        long bit = 1;
        long sum = 0;
        while(bit <= n){
            long cur = (n/bit)%10;
            long low = n % bit;
            long high = n / bit / 10;

            if(cur > 1){
                sum += (high + 1) * bit;
            }else if(cur == 1){
                sum += (high * bit) + (1 + low);
            }else{
                sum += high * bit;
            }
            bit  = bit * 10;
        }

        return (int)sum;

    }
}

剑指 Offer 44. 数字序列中某一位的数字

视频讲解直达: 本题视频讲解

class Solution {
    public int findNthDigit(int n) {
        if(n == 0){
            return 0;
        }

        long bit = 1;
        int i = 1;
        long count = 9;

        while(count < n){
            n = (int)(n - count);
            bit = bit * 10;
            i++;
            count = bit * i * 9;
        }
        // 确定是在这个区间的哪个数
        long num = bit + (n - 1) / i;
        // 确定在 Num 的那个字符
        int index = (n - 1) % i + 1;
        int res = (int)(num / Math.pow(10, i - index)) % 10;

        return res;
    }
}

剑指 Offer 45. 把数组排成最小的数

视频讲解直达: 本题视频讲解

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]);
        }

        quickSort(strs, 0, strs.length - 1);

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < strs.length; i++) {
            sb.append(strs[i]);
        }
        return sb.toString();
    }
///时间nlogn,空间 On
    // 快速排序模版(和普通快速排序模版一样的,除了比较那里不一样)
    private void quickSort(String[] arr, int left, int right) {
        if (left > right) {
            return;
        }
        int i = partition(arr, left, right);
        quickSort(arr, left, i - 1);
        quickSort(arr, i + 1, right);

    }

    int partition(String[] arr, int left, int right){
       String pivot = arr[left]; // 中间值
        int i = left;
        int j = right;

        while (i < j) {
            while (i <= j && (arr[i] + pivot).compareTo(pivot + arr[i]) <= 0) {
                i++;
            }

            while (i <= j && (arr[j] + pivot).compareTo(pivot + arr[j]) >= 0) {
                j--;
            }
            if(i >= j){
                break;
            }
            String temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        arr[left] = arr[j];
        arr[j] = pivot;

        return j;

    }
}

剑指offer 46~68题

剑指 Offer 46. 把数字翻译成字符串

视频讲解直达: 本题视频讲解

class Solution {
    public int translateNum(int num) {
        if(num <= 9){
            return 1;
        }
        char[] arr = String.valueOf(num).toCharArray();
        int n = arr.length;
        //int[] dp = new int[n + 1];
        // f(n) = f(n-1) + f(n-2)
        int a = 1;
        int b = 1;
        int c = 1;
        // 优化 a,b,c
        for(int i = 2; i <= n; i++){
            // 计算第i和第i-1个字符的组合
            int tmp = 10 * (arr[i-2] - '0') + (arr[i-1] - '0');
            if(tmp >= 10 && tmp <= 25){
                c = a + b;
            }else{
                c = b;
            }
            //迭代
            a = b;
            b = c;
        }

        return c;
    }
}

剑指 Offer 47. 礼物的最大价值

视频讲解直达: 本题视频讲解

class Solution {
    public int maxValue(int[][] grid) {
        //优化版本
        int n = grid.length;
        int m = grid[0].length;

        int[]dp = new int[m];
        dp[0] = grid[0][0];

        for(int j = 1; j < m; j++){
            dp[j] = dp[j-1] + grid[0][j];
        }

        for(int i = 1; i < n; i++){
            //
            dp[0] =  dp[0] + grid[i][0];
            for(int j = 1; j < m; j++){
                dp[j] = Math.max(dp[j], dp[j-1]) + grid[i][j];
            }
        }
        return dp[m-1];
    }
  // 二维版本
      public int maxValue(int[][] grid) {
        int n = grid.length;
        int m = grid[0].length;

        int[][] dp = new int[n][m];
        dp[0][0] = grid[0][0];
        for(int i = 1; i < n; i++){
            dp[i][0] = dp[i-1][0] + grid[i][0];
        }
        for(int j = 1; j < m; j++){
            dp[0][j] = dp[0][j -1] + grid[0][j];
        }

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

        return dp[n-1][m-1];
    }
}

剑指 Offer 48. 最长不含重复字符的子字符串

视频讲解直达: 本题视频讲解

class Solution {
    // public int lengthOfLongestSubstring(String s) {
    //     if(s == null || s.length() <= 0){
    //         return 0;
    //     }
    //     // 优化 => 单个变量
    //     Map map = new HashMap<>();
    //     int[] dp = new int[s.length()];
    //     dp[0] = 1;
    //     map.put(s.charAt(0), 0);
    //     int res = 1;
    //     for(int i = 1; i < s.length(); i++){
    //         if(!map.containsKey(s.charAt(i))){
    //             dp[i] = dp[i-1] + 1;
    //         }else{
    //             int k = map.get(s.charAt(i));
    //             dp[i] = i - k <= dp[i-1] ? i - k : dp[i-1] + 1;
    //         }
    //         res = Math.max(res, dp[i]);
    //         map.put(s.charAt(i), i);
    //     }

    //     return res;
    // }

    // 优化版本
    public int lengthOfLongestSubstring(String s) {
        if(s == null || s.length() <= 0){
            return 0;
        }
        // 优化 => 单个变量
        Map map = new HashMap<>();
        //int[] dp = new int[s.length()];
        int a = 1;
        map.put(s.charAt(0), 0);
        int res = 1;
        for(int i = 1; i < s.length(); i++){
            if(!map.containsKey(s.charAt(i))){
                a = a + 1;// 就是没有刷新 a 之前,a表示dp[i-1]
            }else{
                int k = map.get(s.charAt(i));
                a = i - k <= a ? i - k : a + 1;
            }
            res = Math.max(res, a);
            map.put(s.charAt(i), i);
        }

        return res;
        // 时间On,空间On
    }
}

剑指 Offer 49. 丑数

视频讲解直达: 本题视频讲解

class Solution {
    public int nthUglyNumber(int n) {
        int a = 1, b = 1, c = 1;
        int[] dp = new int[n+1];
        dp[1] = 1;

        for(int i = 2; i <= n; i++){
            dp[i] = Math.min(Math.min(dp[a] * 2, dp[b] * 3), dp[c] * 5);
            if(dp[i] == dp[a] * 2) a++;
            if(dp[i] == dp[b] * 3) b++;
            if(dp[i] == dp[c] * 5) c++;
        }

        return dp[n];
    }
}

剑指 Offer 50. 第一个只出现一次的字符

视频讲解直达: 本题视频讲解

class Solution {
    public char firstUniqChar(String s) {
        if(s == null || s.length() <= 0){
            return ' ';
        }
//一个字母出现的次数大于 1 次就不符合要求了,这个时候使用 Fasle 标记状态相对于 Integer 的不断递增更合理,也更省空间。布尔值可以用来判断,可以简化代码逻辑。
        Map map = new LinkedHashMap<>();
        for(int i = 0; i < s.length(); i++){
            map.put(s.charAt(i), !map.containsKey(s.charAt(i)));
        }

        for(Map.Entry m : map.entrySet()){
            if(m.getValue()){
                return m.getKey();
            }
        }

        return ' ';
    }
}

剑指 Offer 51. 数组中的逆序对

视频讲解直达: 本题视频讲解

class Solution {
    public int reversePairs(int[] nums) {
        if(nums == null || nums.length <= 1){
            return 0;
        }
        return mergeSort(nums, 0, nums.length - 1);
    }

    int mergeSort(int[] nums, int left, int right){
        if(left >= right){
            return 0;
        }
        int mid = (right - left) / 2 + left;
        int x1 = mergeSort(nums, left, mid);
        int x2 = mergeSort(nums, mid + 1, right);
        int x3 = merge(nums, left, mid, mid+1, right);

        return x1 + x2 + x3;
    }

    int merge(int[] nums, int l1, int r1, int l2, int r2){
        int[] temp = new int[r2 - l1 + 1];
        int count = 0;
        int i = l1, j = l2, k = 0;
        while(i <= r1 && j <= r2){
            if(nums[i] > nums[j]){
                count = count + (l2 - i);
                temp[k++] = nums[j++];
            }else{
                temp[k++] = nums[i++];
            }
        }
        while(i <= r1) temp[k++] = nums[i++];
        while(j <= r2) temp[k++] = nums[j++];

        // 把临时数组复制回原数组
        k = 0;
        for(i = l1; i <= r2; i++){
            nums[i] = temp[k++];
        }

        return count;
    }
}

剑指 Offer 52. 两个链表的第一个

视频讲解直达: 本题视频讲解

class Solution {
    ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) return null;
        ListNode A = headA;
        ListNode B = headB;

        while(A != B){
            A = A == null ? headB : A.next;
            B = B == null ? headA : B.next;
        }

        return A;
    }
}

剑指 Offer 53 - I. 在排序数组中查找

视频讲解直达: 本题视频讲解

class Solution {
    public int search(int[] nums, int target) {
        //常见题型:
        // 1.寻找第一个大于等于 target 的数   2.寻找第一个等于 target 的数
        // 3.寻找最后一个大于等于 target 的数 4.寻找最后一个等于 target 的数
        //PS:其实这里是不需要写两个查找函数的,可以把代码优化放在同一个方法里滴
        int left = search2(nums, target);
        int right = search4(nums, target);

        if(left < 0 || right < 0) return 0;
        return right - left + 1;



    }
    //2.寻找第一个等于 target 的数的下标
    int search2(int[] nums, int target){
        if(nums == null || nums.length <= 0){
            return -1;
        }
        int left = 0, right = nums.length - 1;
        while(left < right){
            // 向下取整
            int mid = (right - left) / 2 + left;
            if(nums[mid] >= target){
                right = mid;
            }else{
                left = mid + 1;
            }
        }
        // 判断该数是否存在
        if(nums[left] != target) return -1;
        return left;
    }

    //4.寻找最后一个等于 target 的数下标
    int search4(int[] nums, int target){
        if(nums == null || nums.length <= 0){
            return -1;
        }
        int left = 0, right = nums.length - 1;
        while(left < right){
            //向上取整
            int mid = (right - left + 1) / 2 + left;
            if(nums[mid] <= target){
                left = mid;
            }else{
                right = mid - 1;
            }
        }
        // 判断该数是否存在
        if(nums[left] != target) return -1;
        return left;
    }

}

剑指 Offer 53 - II. 0~n-1中缺失的数字

视频讲解直达: 本题视频讲解

class Solution {
    public int missingNumber(int[] nums) {
        int l = 0, r = nums.length - 1;
        while(l < r){
            int mid = (r - l) / 2 + l;
            if(nums[mid] == mid) l = mid + 1;
            else r = mid;
        }

        return nums[l] == l ? l + 1 : l;
    }
}

剑指 Offer 54. 二叉搜索树的第k大节点

视频讲解直达: 本题视频讲解

class Solution {
    int k = 0;
    int target = 0;
    public int kthLargest(TreeNode root, int k) {
        // 中序遍历:有序的序列 左 根 右 从小到大排序的
        //                   右 根 左 从大到小的排序
        this.k = k;
        right_root_left(root);
        return target;
    }

    void right_root_left(TreeNode root){
        if(root == null || k <= 0) return;

        right_root_left(root.right);
        // 打印当前根节点
        k--;
        if(k == 0){
            target = root.val;
        }
        right_root_left(root.left);

    }
}

剑指 Offer 55 - I. 二叉树的深度

视频讲解直达: 本题视频讲解

class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        int left = maxDepth(root.left);
        int right = maxDepth(root.right);

        return Math.max(left, right) + 1;
    }
}

剑指 Offer 55 - II. 平衡二叉树

视频讲解直达: 本题视频讲解

class Solution {
    // nlogn 空间 On   方法2:时间 On,空间O
    public boolean isBalanced(TreeNode root) {
        if(root == null) return true;
        if(maxDepth(root) == -1) return false;
        return true;

    }

    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        int left = maxDepth(root.left);
        if(left == -1) return -1;
        int right = maxDepth(root.right);
        if(right == -1) return -1;
        // 返回-1表示不符合条件了
        if(Math.abs(left - right) > 1) return -1;

        return Math.max(left, right) + 1;
    }
}

剑指 Offer 56 - I. 数组中数字出现的次数

视频讲解直达: 本题视频讲解

class Solution {
    public int[] singleNumbers(int[] nums) {
        int z = 0;
        for(int i = 0;i < nums.length; i++){
            z = z ^ nums[i];
        }

        int m = 1;
        while((m & z) == 0){
            m = m << 1;
        }

        int x = 0, y = 0;

        for(int i = 0;i < nums.length; i++){
            if((nums[i] & m) == 0){
                //结果为 0 的子数组,一边统计用异或统计x
                x = x ^ nums[i];
            } else {
                //结果为 1 的子数组,一边统计用异或统计y
                y = y ^ nums[i];
            }
        }

        return new int[]{x, y};
    }
}

剑指 Offer 56 - II. 数组中数字出现的次数 II

视频讲解直达: 本题视频讲解

class Solution {
    public int singleNumber(int[] nums) {
        int[] res = new int[32];
        int m = 1;
        int sum = 0;
        for(int i = 0; i < 32; i++){
            for(int j = 0; j < nums.length; j++){
                if((nums[j] & m) != 0){
                    res[i]++;
                }
            }

            res[i] = res[i] % 3;
            sum = sum + res[i] * m;
            m = m << 1;
        }

        return sum;
    }
}

剑指 Offer 57. 和为s的两个数字

视频讲解直达: 本题视频讲解

class Solution {
    public int[] twoSum(int[] nums, int target) {
        if(nums == null || nums.length < 2){
            return new int[0];
        }

        int i = 0, j = nums.length - 1;

        while(i < j){
            if(nums[i] + nums[j] > target){
                j--;
            }else if(nums[i] + nums[j] < target){
                i++;
            }else{
                return new int[]{nums[i], nums[j]};
            }
        }

        return new int[0];
    }
}

剑指 Offer 57 - II. 和为s的连续正数序列

视频讲解直达: 本题视频讲解

class Solution {
    public int[][] findContinuousSequence(int target) {
        List res = new ArrayList<>();
        int i = 1, j = 1;
        int sum = 1;
        while(i <= target/2){
            if(sum < target){
                j++;
                sum = sum + j;
            } else if(sum > target){
                sum = sum - i;
                i++;
            } else {
                int[] temp = new int[j - i + 1];
                int index = 0;
                for(int k = i; k <= j; k++){
                    temp[index++] = k;
                }
                sum = sum - i;
                i++;
                j++;
                sum = sum + j;
                res.add(temp);
            }
        }

        return res.toArray(new int[res.size()][]);
    }
}

剑指 Offer 58 - I. 翻转单词顺序

视频讲解直达: 本题视频讲解

class Solution {
    public String reverseWords(String s) {
        // split
        if(s == null || s.length() <= 0){
            return s;
        }
        s = s.trim();
        StringBuilder build = new StringBuilder();
        int i = s.length() - 1, j = i;

        while(i >= 0){
            while(i >= 0 && s.charAt(i) != ' ') i--;
            //[i+1, j];
            build.append(s.substring(i+1, j+1) + " ");
            while(i >= 0 && s.charAt(i) == ' ') i--;
            j = i;
        }

        return build.toString().trim();
    }
}

剑指 Offer 58 - II. 左旋转字符串

视频讲解直达: 本题视频讲解

class Solution {
    public String reverseLeftWords(String s, int n) {
        StringBuilder build = new StringBuilder();
        int len = s.length();
        for(int i = n; i < len + n; i++){
            build.append(s.charAt(i%len));
        }

        // for(int i = 0; i < n; i++){
        //     build.append(s.charAt(i));
        // }

        return build.toString();
    }
}

剑指 Offer 59 - I. 滑动窗口的最大值

视频讲解直达: 本题视频讲解

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length <= 1){
            return nums;
        }

        LinkedList queue = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        int index = 0;

        //K

        for(int i = 0; i < nums.length; i++){
            while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
                queue.pollLast();
            }

            queue.add(i);

            if(queue.peekLast() - k == queue.peek()){
                queue.poll();
            }

            if(i + 1 >= k){
                res[index++] = nums[queue.peek()];
            }
        }

        return res;
    }
}

剑指 Offer 60. n个骰子的点数

视频讲解直达: 本题视频讲解

class Solution {
    public double[] dicesProbability(int n) {
        int[][] dp = new int[n+1][6*n+1];

        for(int i = 1; i <= 6; i++){
            dp[1][i] = 1;
        }

        for(int i = 2; i <= n; i++){
            for(int j = i; j <= 6 * i; j++){
                for(int k = 1; k <= 6; k++){
                    if(j < k) break;
                    dp[i][j] += dp[i-1][j-k];
                }
            }
        }

        double[] res = new double[5*n + 1];
        int index = 0;
        double sum = Math.pow(6, n);

        for(int i = n; i <= 6 * n; i++){
            res[index++] = dp[n][i] / sum;
        }

        return res;
    }
}

剑指 Offer 61. 扑克牌中的顺子

视频讲解直达: 本题视频讲解

class Solution {
    public boolean isStraight(int[] nums) {
        // 排序法 nlogn

        // 集合 n,n
        // 如果要成为一个顺子,则需要 满足两个条件
        // 1、不存在有重复的数,大小王除外
        // 2、最大值 - 最小值 < 5,大小王除外
        Set set = new HashSet<>();
        int max = -1, min = 20;
        for(int i = 0; i < nums.length; i++){
            if(nums[i] == 0) continue;
            if(set.contains(nums[i]))return false;
            set.add(nums[i]);
            max = Math.max(nums[i], max);
            min = Math.min(nums[i], min);
        }

        return max - min < 5;
    }
}

剑指 Offer 62. 圆圈中最后剩下的数字

视频讲解直达: 本题视频讲解

class Solution {
    // 时间n 空间 1
    public int lastRemaining(int n, int m) {
        if( n == 0) return n;
        //return (lastRemaining(n - 1, m) + m) % n;
        // 优化
        int res = 0;

        for(int i = 1; i <= n; i++){
            res = (res + m) % i;
        }
        return res;
    }
}

剑指 Offer 63. 股票的最大利润

视频讲解直达: 本题视频讲解

class Solution {
    public int maxProfit(int[] prices) {
        int min = Integer.MAX_VALUE;
        int max = 0;

        for(int i = 0; i < prices.length; i++){
            if(prices[i] < min){
                min = prices[i];
            }else{
                max = Math.max(max, prices[i] - min);
            }
        }
        return max;
    }
}

剑指 Offer 64. 求1+2+…+n

视频讲解直达: 本题视频讲解

class Solution {
    int sum = 0;
    public int sumNums(int n) {

        // 与或门
        boolean flag = n >= 1 && sumNums(n - 1) < 1;
        sum = sum + n;
        return sum;
    }
}

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

视频讲解直达: 本题视频讲解

class Solution {
    // On,O1
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        while(root != null){
            if(root.val > p.val && root.val > q.val){
                root = root.left;
            } else if(root.val < p.val && root.val < q.val){
                root = root.right;
            } else {
                return root;
            }
        }

        return null;
    }
}


[/rihide]

发表回复

后才能评论

评论(2)