• 力扣剑指OFFER


    剑指OFFER第二版

    找出数组中重复的数字。

    在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
    示例 1:
    输入:
    [2, 3, 1, 0, 2, 5, 3]
    输出:2 或 3

    限制:
    2 <= n <= 100000

    来源:力扣

    class Solution {
        public int findRepeatNumber(int[] nums) {
            return exchange(nums);
        }
    
        // set 简单直接
        // 数据多的时候可能会慢
        private int useSet(int[] nums){
            Set<Integer> rec = new HashSet<>();
            for(int n : nums){
                if(!rec.add(n)){
                    return n;
                }
            }
            return -1;
        }
    
        // 桶排序 数据稀疏时浪费空间 数据密集速度快
        private int useBucketSort(int[] nums){
            int space = nums.length;
            int[] record = new int[space];
            for(int n: nums){
                record[n] += 1;
                if(record[n] > 1){
                    return n;
                }
            }
            return -1;
        }
    
        //空间O(1),原地排序
        private int sort(int[] nums){
            Arrays.sort(nums);
            int idx = 0;
            while(idx < nums.length-1){
                if(nums[idx] == nums[idx+1]){
                    return nums[idx];
                }
                idx++;
            }
            return -1;
        }
        
        //空间O(1) 交换排序
        private int exchange(int[] nums){
            //因为 长度为n的数组里的数字大小在0~n-1之间,所以不会有越界
            //遍历每个位置,把该位置上的数字放到下标值也等于这个数的位置上去
            int i = 0;
            //这里是while 循环
            while(i < nums.length){
                int tmp = nums[nums[i]];
                if(tmp == nums[i] && i != nums[i]){
                    return tmp;
                }
                nums[nums[i]] = nums[i];
                nums[i] = tmp;
                if( i == nums[i]) i++;
            }
            return -1;
        }
    }
    

    二维数组中的查找

    在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
    示例:
    现有矩阵 matrix 如下:
    [
    [1, 4, 7, 11, 15],
    [2, 5, 8, 12, 19],
    [3, 6, 9, 16, 22],
    [10, 13, 14, 17, 24],
    [18, 21, 23, 26, 30]
    ]
    给定 target = 5,返回 true。
    给定 target = 20,返回 false。

    限制:
    0 <= n <= 1000
    0 <= m <= 1000

    LeetCode

    class Solution {
        public boolean findNumberIn2DArray(int[][] matrix, int target) {
            return findFromLeftDownCorner(matrix, target);
        }
    
        //右上角开始找,左边比当前值小,下边比当前值大
        public boolean findFromRightUpCorner(int[][] matrix, int target){
            int row = matrix.length;
            if(row == 0){
                return false;
            }
            int col = matrix[0].length;
            if(col == 0){
                return false;
            }
    
            int i = 0, j = col - 1;
            while(i < row && j >= 0){
                if(matrix[i][j] == target){
                    return true;
                }else if(matrix[i][j] < target){
                    i++;
                }else if(matrix[i][j] > target){
                    j--;
                }
            }
            return false;
        }
    
        //也可以左下角开始找,右边的比当前值大,上面的比当前值小
        public boolean findFromLeftDownCorner(int[][] matrix, int target){
            int row = matrix.length;
            if(row == 0){
                return false;
            }
            int col = matrix[0].length;
            if(col == 0){
                return false;
            }
    
            int i = row-1, j = 0;
            while(i >= 0 && j < col){
                if(matrix[i][j] == target){
                    return true;
                }else if(matrix[i][j] < target){
                    j++;
                }else if(matrix[i][j] > target){
                    i--;
                }
            }
            return false;
        }
    }
    

    替换空格

    请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
    示例 1:
    输入:s = "We are happy."
    输出:"We%20are%20happy."

    限制:
    0 <= s 的长度 <= 10000

    LeetCode

    class Solution {
        // 没什么太大意义: 由于每次替换从 1 个字符变成 3 个字符,使用字符数组可方便地进行替换。建立字符数组地长度为 s 的长度的 3 倍,这样可保证字符数组可以容纳所有替换后的字符。
        public String replaceSpace(String s) {
            String res = "";
            for(int i = 0; i<s.length(); i++)
                res += s.charAt(i) == ' '? "%20": s.charAt(i);
            return res;
        }
    }
    

    重建二叉树

    输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
    假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

    示例 1:
    Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
    Output: [3,9,20,null,null,15,7]

    示例 2:
    Input: preorder = [-1], inorder = [-1]
    Output: [-1]

    限制:
    0 <= 节点个数 <= 5000

    来源:力扣

    对于任意一颗树而言,前序遍历的形式总是
    [ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]
    即根节点总是前序遍历中的第一个节点。而中序遍历的形式总是
    [ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
        int[] pre;
        int[] in;
        int len;
        Map<Integer, Integer> map = new HashMap<>();
    
        public TreeNode buildTree(int[] preorder, int[] inorder) {
            pre = preorder;
            in = inorder;
            len = pre.length;
            if(len == 0) return null;
            for(int i  = 0; i < len; i++) map.put(inorder[i], i);
    
            return build(0, len-1, 0 ,len-1);
        }
    
        private TreeNode build(int preL, int preR, int inL, int inR){
          if(preL > preR){
              return null;
          }  
          int rootVal = pre[preL];
          TreeNode root = new TreeNode(rootVal);
          int mid = map.get(rootVal);
          int size = mid - inL;
          root.left = build(preL + 1, preL + size, inL, mid -1);
          root.right = build(preL + size + 1, preR, mid + 1, inR);
          return root;
        }
    }
    

    矩阵中的路径

    给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
    单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
    例如,在下面的 3×4 的矩阵中包含单词 "ABCCED"(单词中的字母已标出)。

    示例 1:
    输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
    输出:true

    示例 2:
    输入:board = [["a","b"],["c","d"]], word = "abcd"
    输出:false

    提示:
    1 <= board.length <= 200
    1 <= board[i].length <= 200
    board 和 word 仅由大小写英文字母组成

    LeetCode

    class Solution {
        char[][] b;
        String w;
        int row;
        int col;
        int[][] directs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        public boolean exist(char[][] board, String word) {
            b=board;
            w=word;
            row = board.length;
            col = board[0].length;
            boolean[][] marks = new boolean[row][col];
            for(int i = 0; i < row; i++){
                for(int j = 0; j < col; j++){
                    if(searchInBoard(marks, 0, i, j)) return true;
                }
            }
            return false;
        }
    
        private boolean searchInBoard(boolean[][] marks, int idx, int x, int y){
            if(x<0 || x > row - 1 || y < 0 || y > col - 1 || marks[x][y]){
                return false;
            }
            if(b[x][y] != w.charAt(idx)){
                return false;
            } 
            if(idx == w.length()-1){
                return true;
            }
    
            marks[x][y] = true;
    
            boolean res = false;
            for(int[] d: directs){
                if(searchInBoard(marks, idx+1, x + d[0], y+d[1])){
                    res = true;
                    break;
                }
            }
            marks[x][y] = false;
            return res;
        }
    }
    

    机器人的运动范围

    地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
    示例 1:
    输入:m = 2, n = 3, k = 1
    输出:3

    示例 2:
    输入:m = 3, n = 1, k = 0
    输出:1

    提示:
    1 <= n,m <= 100
    0 <= k <= 20

    LeetCode

    class Solution {
        boolean[][] marks;
        public int movingCount(int m, int n, int k) {
            marks = new boolean[m][n];
            // return broadSearch(m, n, k);
            return deepSearch(m, n, 0, 0, k);
        }
    
        // 宽度优先搜索
        // 可以只往下和往右搜
        private int broadSearch(int m, int n, int k){
            Queue<int[]> seeds = new LinkedList<>();
            int[][] directs = new int[][]{{1, 0}, {0, 1}};
            int cnt = 0;
    
            cnt++;
            marks[0][0] = true;
            seeds.add(new int[]{0,0});
    
            while(!seeds.isEmpty()){
                int[] seed = seeds.remove();
                for(int[] direct: directs){
                    int x = seed[0] + direct[0];
                    int y = seed[1] + direct[1];
    
                    if(x > m -1 || y > n - 1 || marks[x][y] || calSum(x, y) > k){
                        continue;
                    }
                    cnt++;
                    marks[x][y] = true;
                    seeds.add(new int[]{x, y});
                }
            }
            return cnt;
        }
    
        /** 
        递归参数: 
        终止条件: 当 ① 行列索引越界 或 ② 数位和超出目标值 k 或 ③ 当前元素已访问过 时,返回 0 ,代表不计入可达解。
        递推工作:
            标记当前单元格 :将索引 (i, j) 存入 Set visited 中,代表此单元格已被访问过。
            搜索下一单元格: 计算当前元素的 下、右 两个方向元素的数位和,并开启下层递归 。
        回溯返回值: 返回 1 + 右方搜索的可达解总数 + 下方搜索的可达解总数,代表从本单元格递归搜索的可达解总数。
        */
        private int deepSearch(int m, int n, int x, int y, int k){
            if(x > m -1 || y > n -1 || marks[x][y] || calSum(x, y) > k){
                return 0;
            }
            marks[x][y] = true;
            int cnt = 1;
            cnt += deepSearch(m, n, x + 1, y, k);
            cnt += deepSearch(m, n, x, y + 1, k);
            // 不能回溯,因为路径上的每个点都是一次记录,用过就不能再用了 
            // marks[x][y] = false;
            return cnt;
        }
    
        private int calSum(int x, int y){
            int sum = 0;
            while(x > 0){
                sum += x % 10;
                x/=10;
            }
            while(y > 0){
                sum += y % 10;
                y/=10;
            }
    
            return sum;
        }
    }
    

    I. 剪绳子

    给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]k[1]...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
    示例 1:
    输入: 2
    输出: 1
    解释: 2 = 1 + 1, 1 × 1 = 1

    示例 2:
    输入: 10
    输出: 36
    解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

    提示:
    2 <= n <= 58

    来源:LeetCode

    class Solution {
        public int cuttingRope(int n) {
            return dp(n);
        }
    
        /** 
        dp[i] 表示将长度为 i 的绳子剪成至少两段绳子之后,这些绳子长度的最大乘积
        确定状态转移方程
            当 i ≥ 2 时,假设对长度为 i 绳子剪出的第一段绳子长度是 j(1≤j<i),则有以下两种方案:
                将 i 剪成 j 和 i-j 长度的绳子,且 i−j 不再继续剪,此时的乘积是 j×(i−j) ;
                将 i 剪成 j 和 i−j 长度的绳子,且 i−j 继续剪成多段长度的绳子,此时的乘积是 j×dp[i−j] 。
        因此,当 j 固定时,有 dp[i]=max(j×(i−j),j×dp[i−j])。由于 j 的取值范围是 1 到 i ,需要遍历所有的 j 得到dp[i]的
        */ 
        public int dp(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 - 1; j ++){
                    dp[i] = Math.max(dp[i], Math.max(j * (i-j),dp[i-j] * j));
                }
            }
            return dp[n];
        }
    
        //贪心
        //尽量多的3,剩余的用2补全
        public int greed(int n){
            if(n == 2) return 1;
            if(n == 3) return 2;
            int threeRemains = n % 3;
            int twos = 0;
            int threes = 0;
            if(threeRemains == 1){
                twos = 2;
                n -= 3;
            }else if(threeRemains == 2){
                twos = 1;
            }
            threes = n / 3;
            return (int)Math.pow(3, threes) * (int)Math.pow(2, twos);
        }
    }
    

    剪绳子 II

    给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]k[1]...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
    答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
    示例 1:
    输入: 2
    输出: 1
    解释: 2 = 1 + 1, 1 × 1 = 1

    示例 2:
    输入: 10
    输出: 36
    解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

    提示:
    2 <= n <= 1000

    LeetCode

    大数求余有以下两个主要公式
    公式1:(x+y)%p=(x%p+y%p)%p
    证明:
    x=a*p+b,y=c*p+d
    (x+y)%p=[(a+c)*p+b+d]%p
    因为(a+c)*pp的倍数,故略去,而b=x%p,d=y%p
    所以,(x+y)%p=(b+d)%p=(x%p+y%p)%p

    公式2:(x*y)%p=[(x%p)(y%p)]%p
    证明
    x=a*p+b,y=c*p+d
    (x*y)%p=(a*c*p*p+a*d*p+b*c*p+b*d)%p
    因为a*c*p*p+a*d*p+b*c*pp的倍数,故略去,而b=x%p,d=y%p
    所以,(x*y)%p=(b*d)%p=[(x%p)*(y%p)]%p

    根据上述可推导出:pow(n,m)%p=[(n%p)*pow(n,m-1)%p]%p

    class Solution {
        int mod = 1000000007;
        public int cuttingRope(int n) {
            return math(n);
        }
    
        // 算法主体与 https://leetcode.cn/problems/jian-sheng-zi-lcof/ 类似,
        // 区别在于需要使用快速幂算法求幂次 外加 大数取余
        private int math(int b){
            if(b < 3) return 1;
            if(b == 3) return 2;
            if(b % 3 == 0) return (int)(quickPowAndMod(3, b/3) % mod);
            if(b % 3 == 1) return (int)((quickPowAndMod(3, b/3-1) * 4) % mod);
            if(b % 3 == 2) return (int)((quickPowAndMod(3, b/3) * 2) % mod);
            return 0;
        }
    
        //快速幂取余 
        //与快速幂算法类似,每一步多一个取余的步骤,快速幂算法参考
        //https://leetcode.cn/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/
        private long quickPowAndMod(long b, int n){
            long res = 1;
            while(n > 0){
                if((n & 1) == 1){
                    res = (res * b) % mod;
                }
                b = (b * b) % mod;
                n = n >> 1;
            }
            return (int)(res % mod);
        }
    }
    

    数值的整数次方

    实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
    示例 1:
    输入:x = 2.00000, n = 10
    输出:1024.00000

    示例 2:
    输入:x = 2.10000, n = 3
    输出:9.26100

    示例 3:
    输入:x = 2.00000, n = -2
    输出:0.25000
    解释:2-2 = 1/22 = 1/4 = 0.25

    提示:
    -100.0 < x < 100.0
    -231 <= n <= 231-1
    -104 <= xn <= 104

    来源:LeetCode

    class Solution {
        public double myPow(double x, int n) {
            return n > 0 ? quickMulRecur(x, n) : 1 / quickMulRecur(x,  -(long)n);
            // return n > 0 ? quickMulIter(x, n) : 1 / quickMulIter(x, -(long)n);
        }
    
        //递归版本
        private double quickMulRecur(double x, long n){
            if(n == 0){
                return 1;
            }else{
                double y = quickMulRecur(x, n / 2);
                return n % 2 == 0 ? y * y : y * y * x;
            }
        }
    
        //迭代版本
        //x^9 ==> x^1 * x^8, 9 = (1001b)
        private double quickMulIter(double x, long n){
            double res = 1;
            while (n > 0){
                //先用res乘再往前计算x
                if((n & 1) == 1){
                    res *= x;
                }
                x *= x;
                n = n >> 1;
            }
            return res;
        }
    }
    

    树的子结构

    输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
    B是A的子结构, 即 A中有出现和B相同的结构和节点值。
    例如:
    给定的树 A:
         3
        / \
       4   5
      / \
     1   2
    给定的树 B:
       4 
      /
     1
    返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
    示例 1:
    输入:A = [1,2,3], B = [3,1]
    输出:false

    示例 2:
    输入:A = [3,4,5,1,2], B = [4,1]
    输出:true

    限制:
    0 <= 节点个数 <= 10000

    来源:LeetCode

    树的很多递归题,想清楚如何分解为子问题就好做了。

    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
        public boolean isSubStructure(TreeNode A, TreeNode B) {
            if(B == null) return false;
            return inOrderSearch(A, B);
        }
    
        private boolean inOrderSearch(TreeNode A, TreeNode B){
            if(A == null || B == null) return false;
            if(isTwoSub(A, B)) return true;
            return inOrderSearch(A.left, B) || inOrderSearch(A.right, B);
        }
    
        private boolean isTwoSub(TreeNode a, TreeNode b){
            if(b == null) return true;
            if(a == null) return false;
            if(a.val != b.val) return false;
            return isTwoSub(a.left, b.left) && isTwoSub(a.right, b.right);
        }
    }
    

    表示数值的字符串

    请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
    数值(按顺序)可以分成以下几个部分:
    若干空格
    一个 小数 或者 整数
    (可选)一个 'e' 或 'E' ,后面跟着一个 整数
    若干空格
    小数(按顺序)可以分成以下几个部分:
    (可选)一个符号字符('+' 或 '-')

    下述格式之一:
    至少一位数字,后面跟着一个点 '.'
    至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字
    一个点 '.' ,后面跟着至少一位数字

    整数(按顺序)可以分成以下几个部分:
    (可选)一个符号字符('+' 或 '-')
    至少一位数字
    部分数值列举如下:
    ["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]
    部分非数值列举如下:
    ["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]
    示例 1:
    输入:s = "0"
    输出:true

    示例 2:
    输入:s = "e"
    输出:false

    示例 3:
    输入:s = "."
    输出:false

    示例 4:
    输入:s = "    .1  "
    输出:true

    提示:
    1 <= s.length <= 20
    s 仅含英文字母(大写和小写),数字(0-9),加号 '+' ,减号 '-' ,空格 ' ' 或者点 '.' 。

    来源:LeetCode

    状态机

    class Solution {
        // ' '-空格;'d'-数字; '.'-小数点; 'e'- e; 's'-正负号
     private static final Map<Character, Integer>[] statusMap =
                new Map[] {
                        new HashMap<Character, Integer>() {{
                            put(' ', 0);
                            put('s', 1);
                            put('d', 2);
                            put('.', 4);
                        }}, //0
                        new HashMap<Character, Integer>() {{
                            put('.', 4);
                            put('d', 2);
                        }}, //1
                        new HashMap<Character, Integer>() {{
                            put('d', 2);
                            put('e', 6);
                            put(' ', 9);
                            put('.', 3);
                        }}, //2
                        new HashMap<Character, Integer>() {{
                            put('d', 5);
                            put('e', 6);
                            put(' ', 9);
                        }}, //3
                        new HashMap<Character, Integer>() {{
                            put('d', 5);
                        }},                                        //4
                        new HashMap<Character, Integer>() {{
                            put('d', 5);
                            put('e', 6);
                            put(' ', 9);
                        }},  //5
                        new HashMap<Character, Integer>() {{
                            put('s', 7);
                            put('d', 8);
                        }},  //6
                        new HashMap<Character, Integer>() {{
                            put('d', 8);
                        }},  //7
                        new HashMap<Character, Integer>() {{
                            put('d', 8);
                            put(' ', 9);
                        }}, //8
                        new HashMap<Character, Integer>() {{
                            put(' ', 9);
                        }}, //9
                };
    
        private static final Set<Integer> validStatus = new HashSet<Integer>() {{
            add(2);
            add(3);
            add(5);
            add(8);
            add(9);
        }};
    
    
        public boolean isNumber(String s) {
            int status = 0;
            for (char c : s.toCharArray()) {
                c = getSign(c);
                Map<Character, Integer> currentStatusMap = statusMap[status];
                if (!currentStatusMap.containsKey(c)) {
                    return false;
                }
                status = currentStatusMap.get(c);
            }
            //2,3,5,8,9是有效终态
            return validStatus.contains(status);
        }
    
        private char getSign(char c) {
            if (c >= '0' && c <= '9') {
                return 'd';
            }
            if (c == '-' || c == '+') {
                return 's';
            }
            if (c == 'e' || c == 'E') {
                return 'e';
            }
            if (c == '.' || c == ' ') {
                return c;
            }
            return '?';
        }
    }
    

    二叉搜索树与双向链表

    输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
    为了让您更好地理解问题,以下面的二叉搜索树为例:

    我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
    下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。

    特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。

    LeetCode

    /*
    // Definition for a Node.
    class Node {
        public int val;
        public Node left;
        public Node right;
    
        public Node() {}
    
        public Node(int _val) {
            val = _val;
        }
    
        public Node(int _val,Node _left,Node _right) {
            val = _val;
            left = _left;
            right = _right;
        }
    };
    */
    class Solution {
        //用全局变量保存head pre
        private Node pre, head;
        public Node treeToDoublyList(Node root) {
            if(root == null) return null;
            inOrderSearch(root);
            //最后要把收尾连起来
            head.left = pre;
            pre.right = head;
            return head;
        }
    
        //中序遍历能保证按从小到达顺序遍历每个节点。
        //我们把之前中序遍历打印节点的操作替换为连接前后节点的操作即可简单完成双向链表的连接
    
        private void inOrderSearch(Node cur){
            if(cur == null) return;
            inOrderSearch(cur.left);
            if(pre==null) head = cur;
            else pre.right = cur;
            cur.left = pre;
            pre = cur;
            inOrderSearch(cur.right);
        }
    }
    

    栈的压入、弹出序列

    输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

    示例 1:
    输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
    输出:true
    解释:我们可以按以下顺序执行:
    push(1), push(2), push(3), push(4), pop() -> 4,
    push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1

    示例 2:
    输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
    输出:false
    解释:1 不能在 2 之前弹出。

    提示:
    0 <= pushed.length == popped.length <= 1000
    0 <= pushed[i], popped[i] < 1000
    pushed 是 popped 的排列。

    LeetCode

    class Solution {
        public boolean validateStackSequences(int[] pushed, int[] popped) {
            return leftRightSwagger(pushed, popped);
        }
    
        //2022/06/20 这个解法过不去第 97/151个用例,但我觉得没问题
        private boolean leftRightSwagger(int[] pushed, int[] popped){
            int len = pushed.length;
            if(len == 0) return true;
    
            int right = findIdx(popped[0], pushed);
            if(right == -1) return false;
            int left = right;
    
            for(int i  = 1; i < len; i++){
                if(right+1 < len && pushed[right+1] == popped[i]) right+=1;
                else if(left-1 >= 0 && pushed[left-1] == popped[i]) left-=1;
                else return false;
            }
            return true;
        }
    
        private int findIdx(int val, int[] arr){
            for(int i = 0; i<arr.length; i++){
                if(arr[i] == val) return i;
            }
            return -1;
        }
    }
    

    damn it

    class Solution {
        public boolean validateStackSequences(int[] pushed, int[] popped) {
            return stackMock(pushed, popped);
        }
    
        /** 
        考虑借用一个辅助栈 stackstack ,模拟 压入 / 弹出操作的排列。根据是否模拟成功,即可得到结果。
        入栈操作: 按照压栈序列的顺序执行。
        出栈操作: 每次入栈后,循环判断 “栈顶元素 == 弹出序列的当前元素” 是否成立,将符合弹出序列顺序的栈顶元素全部弹出。
        */
        private boolean stackMock(int[] pushed, int[] popped){
            if(pushed.length <= 0 ) return true;
            Stack<Integer> stack = new Stack<>();
            int p = 0;
            for(int i: pushed){
                stack.push(i);
                while(!stack.isEmpty() && stack.peek() == popped[p]){
                    stack.pop();
                    p++;
                }
            }
            return stack.isEmpty();
        }
    
    
        //2022/06/20 这个解法过不去第 97/151个用例,但我觉得没问题
        private boolean leftRightSwagger(int[] pushed, int[] popped){
            int len = pushed.length;
            if(len == 0) return true;
    
            int right = findIdx(popped[0], pushed);
            if(right == -1) return false;
            int left = right;
    
            for(int i  = 1; i < len; i++){
                if(right+1 < len && pushed[right+1] == popped[i]) right+=1;
                else if(left-1 >= 0 && pushed[left-1] == popped[i]) left-=1;
                else return false;
            }
            return true;
        }
    
        private int findIdx(int val, int[] arr){
            for(int i = 0; i<arr.length; i++){
                if(arr[i] == val) return i;
            }
            return -1;
        }
    }
    

    字符串的排列

    输入一个字符串,打印出该字符串中字符的所有排列。
    你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

    示例:
    输入:s = "abc"
    输出:["abc","acb","bac","bca","cab","cba"]

    限制:
    1 <= s 的长度 <= 8

    LeetCode

    
    
  • 相关阅读:
    4.2说说计算机中的异常
    1.1组合电路、时序电路在计算机课程中的地位
    EFM32JG系列MCU内部温度传感器使用方法
    +7虚拟内存的作用,通过什么方式提高虚拟内存的性能
    +6存储结构是怎样提高性能的,它和局部性的关系是什么。
    +5性能分析定律
    +4 高速缓存
    +3软件优化至关重要,软件优化一般有哪些方法?
    +2流水线是怎样提高性能的,会遇到什么问题,解决方法是什么
    +1阿姆达尔定律
  • 原文地址:https://www.cnblogs.com/greatLong/p/16362988.html
Copyright © 2020-2023  润新知