当前位置: 首页 > news >正文

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

本次题目

      • 75 颜色分类
      • 76 最小覆盖子串
      • 78 子集
      • 79 单词搜索
      • 84 柱状图中最大的矩形
      • 85 最大矩形
      • 94 二叉树的中序遍历
      • 96 不同的二叉搜索树
      • 98 验证二叉搜索树
      • 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;
    }
}
  • 双指针,左右指针分别从数组两头出发,遍历一次数组,遇到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;
    }
}

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

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);   //回溯
        }
    }
}

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

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

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

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);//右
    }
}

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

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

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

相关文章:

  • 数据结构之——OJ题环形队列实现详解
  • 基于樽海鞘群算法的线性规划求解matlab程序
  • qt自定义控件之TextEdit
  • 深度估计 双目深度估计+单目深度估计 ONNX运行程序
  • Express 路由
  • 2022蓝帽杯初赛电子取证
  • 数据结构与算法复习:第三十六弹
  • CSS - 响应式布局(一)媒体查询
  • 【JAVA预备】课程目录
  • 2022年0902Maven的依赖学习<第四课>
  • Android 11 上的文件读写权限(MANAGE_EXTERNAL_STORAGE)
  • Vue模板语法(01)
  • 世界第一台通用计算机:ENIAC
  • JAVA学习----HashSet类
  • 文章组合生成-免费文章组合生成软件
  • co.js - 让异步代码同步化
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • Go 语言编译器的 //go: 详解
  • Redis的resp协议
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • RxJS: 简单入门
  • Selenium实战教程系列(二)---元素定位
  • session共享问题解决方案
  • Vue 2.3、2.4 知识点小结
  • webpack入门学习手记(二)
  • 翻译:Hystrix - How To Use
  • 工作中总结前端开发流程--vue项目
  • 官方解决所有 npm 全局安装权限问题
  • 后端_ThinkPHP5
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 扑朔迷离的属性和特性【彻底弄清】
  • 前端攻城师
  • 区块链分支循环
  • 如何编写一个可升级的智能合约
  • 如何利用MongoDB打造TOP榜小程序
  • 手写一个CommonJS打包工具(一)
  • 算法之不定期更新(一)(2018-04-12)
  • 【干货分享】dos命令大全
  • MyCAT水平分库
  • Prometheus VS InfluxDB
  • Spring Batch JSON 支持
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • #DBA杂记1
  • #pragma pack(1)
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • $GOPATH/go.mod exists but should not goland
  • (1)Nginx简介和安装教程
  • (C语言)fgets与fputs函数详解
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (十二)springboot实战——SSE服务推送事件案例实现
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (学习日记)2024.03.25:UCOSIII第二十二节:系统启动流程详解
  • (原創) 物件導向與老子思想 (OO)