• 力扣记录:Hot100(4)——75-101


    75 颜色分类

    • 快排,选择最左为哨兵,循环时先移动右指针,递归时跳过哨兵
      • 时间复杂度O(nlogn),空间复杂度O(n)
    class Solution {
        public void sortColors(int[] nums) {
            //快排
            quickSort(nums, 0, nums.length - 1);
        }
        //快排函数
        private void quickSort(int[] nums, int start, int end){
            if(end - start < 1) return;
            int i = start;  //选择哨兵
            int j = end;
            while(i < j){
                while(i < j && nums[j] >= nums[start]) j--;  //先移动j
                while(i < j && nums[i] <= nums[start]) i++;
                swap(nums, i, j);
            }
            swap(nums, i, start);   //交换哨兵到中间
            //递归快排
            quickSort(nums, start, i - 1);
            quickSort(nums, i + 1, end);
        }
        //交换函数
        private void swap(int[] nums, int i, int j){
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 双指针,左右指针分别从数组两头出发,遍历一次数组,遇到0与左指针位置交换,左指针右移;遇到2与右指针位置交换,右指针左移(这里需要判断交换后当前遍历位置是否为2,是则继续交换,0则与左指针交换)。
      • 时间复杂度O(n),空间复杂度O(1)
    class Solution {
        public void sortColors(int[] nums) {
            //双指针,左右指针分别从数组两头出发
            int left = 0;
            int right = nums.length - 1;
            //遍历一次数组
            for(int i = 0; i <= right; i++){
                while(i <= right && nums[i] == 2){  //遇到0与左指针位置交换,左指针右移
                    //这里需要判断交换后当前遍历位置是否为2,是则继续交换,直到不为2
                    swap(nums, i, right--);
                }
                //先换2再进行0的交换,有可能2换完为0
                if(nums[i] == 0){   //遇到0与左指针位置交换,左指针右移
                    swap(nums, i, left++);
                }
            }
        }
        //交换函数
        private void swap(int[] nums, int i, int j){
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    76 最小覆盖子串

    • 滑动窗口,之前做过,使用StringBuilder处理字符串,使用哈希表存储字符串t的字符及出现次数,左右指针都从字符串s开头出发,右指针向右遍历,如果哈希表中有该字符,则对应次数减1,若次数减为0则一个字符满足,记录为flag。当哈希表中所有字符都满足时判断左指针是否向右移动缩小窗口,若左指针指向字符不是哈希表中字符或次数小于0则向右移动,否则不移动,计算长度并更新最短长度和子串。
    • 注意:求StringBuilder的长度为length(),同字符串。
      • 时间复杂度O(|字符集大小| * s + t),空间复杂度O(|字符集大小|)
    class Solution {
        public String minWindow(String s, String t) {
            //滑动窗口
            //使用StringBuilder处理字符串
            StringBuilder s1 = new StringBuilder(s);
            StringBuilder t1 = new StringBuilder(t);
            int leng = Integer.MAX_VALUE;
            String result = "";
            //使用哈希表存储字符串t的字符及出现次数
            Map<Character, Integer> map = new HashMap<>();
            for(int i = 0; i < t1.length(); i++){
                if(!map.containsKey(t1.charAt(i))){
                    map.put(t1.charAt(i), 0);
                }
                map.put(t1.charAt(i), map.get(t1.charAt(i)) + 1);
            }
            //左右指针都从字符串s开头出发
            int left = 0;
            int right = 0;
            int flag = 0;
            //右指针向右遍历,如果哈希表中有该字符,则对应次数减1
            for(;right < s1.length(); right++){
                if(map.containsKey(s1.charAt(right))){
                    int num = map.get(s1.charAt(right)) - 1;
                    map.put(s1.charAt(right), num);
                    //若次数减为0则一个字符满足,记录为flag
                    if(num == 0) flag += 1;
                }
                //当哈希表中所有字符都满足时判断左指针是否向右移动缩小窗口
                if(flag == map.size()){
                    //若左指针指向字符不是哈希表中字符或次数小于0则向右移动,否则不移动
                    while(!map.containsKey(s1.charAt(left)) || (map.containsKey(s1.charAt(left)) && map.get(s1.charAt(left)) < 0)){
                        if(map.containsKey(s1.charAt(left))){
                            map.put(s1.charAt(left), map.get(s1.charAt(left)) + 1);
                        }
                        left++;
                    }
                    //计算长度并更新最短长度和子串
                    if(leng > right - left + 1){
                        leng = right - left + 1;
                        result = s1.substring(left, right + 1);
                    }
                }
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    78 子集

    • 回溯,之前做过,定义start表示当前从数组第几位开始。
      • 时间复杂度O(n*2^n),空间复杂度O(n)
    class Solution {
        public List<List<Integer>> subsets(int[] nums) {
            //回溯
            List<List<Integer>> result = new ArrayList<>();
            List<Integer> path = new ArrayList<>();
            backtracking(nums, 0, result, path);
            return result;
        }
        //回溯函数,输入数组、从第几位开始、结果集合、路径集合
        private void backtracking(int[] nums, int start, List<List<Integer>> result, List<Integer> path){
            //终止条件
            result.add(new ArrayList(path));
            if(start >= nums.length) return;
            //循环递归
            for(int i = start; i < nums.length; i++){
                path.add(nums[i]);
                backtracking(nums, i + 1, result, path);    //这里应该时i+1而不是start+1
                path.remove(path.size() - 1);   //回溯
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    79 单词搜索

    • 回溯,参考剑指 Offer 12 矩阵中的路径,定义当前位置和已匹配的字符个数,当不匹配时回溯,否则继续递归直到遍历结束,定义已使用的字符,递归时跳过。
    • 注意:在回溯函数外遍历起点,终止条件顺序不能乱。
      • 时间复杂度O(mn*3^L),空间复杂度O(mn),二维数组m行n列,字符串长度L
    class Solution {
        public boolean exist(char[][] board, String word) {
            //回溯
            boolean[][] used = new boolean[board.length][board[0].length];
            //循环每个位置作为起点,不要在回溯里循环
            for(int i = 0; i < board.length; i++){
                for(int j = 0; j < board[i].length; j++){
                    boolean flag = backtracking(board, used, word, i, j, 0);
                    if(flag) return true;
                }
            }
            return false;
        }
        //回溯函数,输入二维数组、已使用字符、目标字符串、当前坐标、已匹配的字符个数
        private boolean backtracking(char[][] board, boolean[][] used, String word, int x, int y, int z){
            //终止条件
            //需要先判断该位是否使用过
            if(used[x][y]) return false;
            if(board[x][y] != word.charAt(z)) return false;
            if(word.length() == z + 1) return true;
            //递归
            //当前位置匹配时
            used[x][y] = true;
            if(x + 1 < board.length){   //向下递归
                boolean down = backtracking(board, used, word, x + 1, y, z + 1);
                if(down) return true;   //找到匹配的字符串直接返回
            }
            if(x - 1 >= 0){ //向上递归
                boolean up = backtracking(board, used, word, x - 1, y, z + 1);
                if(up) return true;
            }
            if(y - 1 >= 0){ //向左递归
                boolean left = backtracking(board, used, word, x, y - 1, z + 1);
                if(left) return true;
            }
            if(y + 1 < board[x].length){    //向右递归
                boolean right = backtracking(board, used, word, x, y + 1, z + 1);
                if(right) return true;
            }
            used[x][y] = false; //回溯
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    84 柱状图中最大的矩形

    • 单调栈,之前做过
      • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        public int largestRectangleArea(int[] heights) {
            //单调栈
            //定义单调栈存储柱子对应下标,栈顶到栈底递减
            Deque<Integer> stack = new LinkedList<>();
            //初始化
            //这里将数组扩容,两头加0,两边最小能到首尾
            int[] heights2 = new int[heights.length + 2];
            heights2[0] = 0;
            for(int i = 0; i < heights.length; i++){
                heights2[i + 1] = heights[i];
            }
            heights2[heights2.length - 1] = 0;
            stack.push(0);
            int sum = 0;
            //遍历
            for(int i = 1; i < heights2.length; i++){
                int top = stack.peek();
                if(heights2[i] > heights2[top]){//大于栈顶元素直接入栈
                    stack.push(i);
                }else if(heights2[i] == heights2[top]){//等于栈顶元素则替换栈顶元素
                    stack.pop();
                    stack.push(i);
                }else{//小于栈头元素高度,则计算面积
                    //每弹出一个元素就计算弹出元素的最大面积
                    //相当于找到了弹出元素左右两边第一个比其小的柱子
                    while(!stack.isEmpty() && heights2[i] < heights2[stack.peek()]){
                        int mid = stack.pop();
                        if(!stack.isEmpty()){
                            //面积为宽乘高
                            int h = heights2[mid];
                            int w = i - stack.peek() - 1;
                            sum = Math.max(sum, h * w);
                        }
                    }
                    //计算结束后弹出栈头元素直到当前元素(高度递增)入栈
                    stack.push(i);
                }
            }
            //返回结果
            return sum;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    85 最大矩形

    • 对比上题84 柱状图中最大的矩形,本题需要先将矩阵转为柱状图,定义矩阵每个元素左边连续1的数量为left[i] [j],针对每一列使用单调栈进行计算。
      • 时间复杂度O(mn),空间复杂度O(mn)
    class Solution {
        public int maximalRectangle(char[][] matrix) {
            //定义矩阵每个元素左边连续1的数量为left[i] [j]
            int m = matrix.length;
            if(m == 0) return 0;
            int n = matrix[0].length;
            int[][] left = new int[m + 2][n];   //第一行和最后一行为全0,当一列递增时最后不用额外考虑
            for(int i = 1; i < m + 1; i++){
                for(int j = 0; j < n; j++){
                    if(matrix[i - 1][j] == '1'){
                        left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1;
                    }
                }
            }
            //针对每一列使用单调栈进行计算
            Deque<Integer> stack = new LinkedList<>();
            int max = 0;
            for(int j = 0; j < n; j++){
                stack.push(0);  //初始化为0,方便计算第一行矩阵,如输入[["1"]]
                for(int i = 0; i < m + 2; i++){
                    if(stack.isEmpty()){
                        stack.push(i);
                    }else{
                        int top = stack.peek();
                        if(left[top][j] < left[i][j]){//大于栈顶元素直接入栈
                            stack.push(i);
                        }else if(left[top][j] == left[i][j]){//等于栈顶元素则替换栈顶元素
                            stack.pop();
                            stack.push(i);
                        }else{//小于栈头元素高度,则计算面积
                            while(!stack.isEmpty() && left[stack.peek()][j] > left[i][j]){
                                //每弹出一个元素就计算弹出元素的最大面积
                                //相当于找到了弹出元素左右两边第一个比其小的柱子
                                int mid = stack.pop();
                                if(!stack.isEmpty()){
                                    int h = left[mid][j];
                                    int w = i - stack.peek() - 1;
                                    max = Math.max(max, h * w);
                                }
                            }
                            //计算结束后弹出栈头元素直到当前元素(高度递增)入栈
                            stack.push(i);
                        }
                    }
                    
                }
                //每一列单独计算
                //stack.clear();
            }
            return max;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    94 二叉树的中序遍历

    • 递归,之前做过
      • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        public List<Integer> inorderTraversal(TreeNode root) {
            //递归
            List<Integer> result = new ArrayList<>();
            inorder(root, result);
            return result;
        }
        //递归函数,输入根节点,结果数组
        private void inorder(TreeNode root, List<Integer> result){
            //终止条件
            if(root == null) return;
            inorder(root.left, result);//左
            result.add(root.val);//中
            inorder(root.right, result);//右
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    96 不同的二叉搜索树

    • 动态规划,之前做过
      • 时间复杂度O(n^2),空间复杂度O(n)
    class Solution {
        public int numTrees(int n) {
            //动态规划
            int[] dp = new int[n + 1];
            dp[0] = 1;
            for(int i = 1; i <= n; i++){
                for(int j = 1; j <= i; j++){
                    //卡特兰数公式
                    dp[i] += dp[j - 1] * dp[i - j];
                }
            }
            return dp[n];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    98 验证二叉搜索树

    • 递归,之前做过,二叉搜索树中序遍历为升序,记录上一个节点值并和当前值进行对比。
      • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        TreeNode pre = null;//记录上一个节点
        public boolean isValidBST(TreeNode root) {
            //递归
            if(root == null) return true;
            //中序遍历
            boolean left = isValidBST(root.left);//左
            if(pre != null && pre.val >= root.val) return false;//中
            pre = root;
            boolean right = isValidBST(root.right);//右
            return left && right;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    101 对称二叉树

    • 递归,之前做过,注意比较的节点为镜像对称
      • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        public boolean isSymmetric(TreeNode root) {
            //递归
            if(root == null) return false;
            return compareNode(root.left, root.right);
        }
        //递归函数,输入要对比的两个节点
        private boolean compareNode(TreeNode left, TreeNode right){
            if(left == null && right == null) return true;
            if(left == null || right == null) return false;
            if(left.val != right.val) return false;
            //递归
            boolean l = compareNode(left.left, right.right);
            if(!l) return false;
            boolean r = compareNode(left.right, right.left);
            return l && r;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    linux上 防火墙查看,添加,关闭,开放端口等命令
    JUC_锁是什么,Synchronized与Lock的区别
    vue监听浏览器网页关闭和网页刷新
    初识上位机(上):搭建PLC模拟仿真环境
    一个项目设置两个Git地址,实现同时推送到两个Git仓库
    Day29_10 JavaWeb之Servlet及Servlet细节
    软件数字签名是什么?软件数字签名有什么作用?
    qmake 参数
    单链表的定义(数据结构与算法)
    java计算机毕业设计springboot+vue小区防疫健康信息管理及出入登记平台
  • 原文地址:https://blog.csdn.net/Kiwi_fruit/article/details/125752076