【LeetCode热题100】打卡第39天:数组中第K个最大元素最大正方形

news2024/10/7 6:39:25

文章目录

  • 【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形
    • ⛅前言
  • 数组中的第K个最大元素
    • 🔒题目
    • 🔑题解
  • 最大正方形
    • 🔒题目
    • 🔑题解

【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形

⛅前言

大家好,我是知识汲取者,欢迎来到我的LeetCode热题100刷题专栏!

精选 100 道力扣(LeetCode)上最热门的题目,适合初识算法与数据结构的新手和想要在短时间内高效提升的人,熟练掌握这 100 道题,你就已经具备了在代码世界通行的基本能力。在此专栏中,我们将会涵盖各种类型的算法题目,包括但不限于数组、链表、树、字典树、图、排序、搜索、动态规划等等,并会提供详细的解题思路以及Java代码实现。如果你也想刷题,不断提升自己,就请加入我们吧!QQ群号:827302436。我们共同监督打卡,一起学习,一起进步。

博客主页💖:知识汲取者的博客

LeetCode热题100专栏🚀:LeetCode热题100

Gitee地址📁:知识汲取者 (aghp) - Gitee.com

题目来源📢:LeetCode 热题 100 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台

PS:作者水平有限,如有错误或描述不当的地方,恳请及时告诉作者,作者将不胜感激

数组中的第K个最大元素

🔒题目

原题链接:215.数组中的第K个最大元素

image-20230718124356071

🔑题解

  • 解法一:使用快速排序API

    import java.util.Arrays;
    import java.util.Collections;
    
    /**
     * @author ghp
     * @title
     * @description
     */
    class Solution {
        public int findKthLargest(int[] nums, int k) {
            Integer[] arr = Arrays.stream(nums).boxed().toArray(Integer[]::new);
            Arrays.sort(arr, Collections.reverseOrder());
            return arr[k - 1];
        }
    }
    

    复杂度分析:

    • 时间复杂度:最坏 O ( n 2 ) O(n^2) O(n2),最好 O ( n l o g n ) O(nlogn) O(nlogn)
    • 空间复杂度: O ( n ) O(n) O(n),arr占用空间n,快排递归占用空间 logn,所以总的空间复杂度为 n

    其中 n n n 为数组中元素的个数

    代码优化

    class Solution {
        public int findKthLargest(int[] nums, int k) {
            Arrays.sort(nums);
            return nums[nums.length - k];
        }
    }
    

    复杂度分析:

    • 时间复杂度:最坏 O ( n 2 ) O(n^2) O(n2),最好 O ( n l o g n ) O(nlogn) O(nlogn)
    • 空间复杂度: O ( l o g n ) O(logn) O(logn)

    其中 n n n 为数组中元素的个数

  • 解法二:手动实现快速排序

    /**
     * @author ghp
     * @title
     * @description
     */
    class Solution {
        public int findKthLargest(int[] nums, int k) {
            return findKthLargestByQuickSort(nums, 0, nums.length - 1, k);
        }
    
        private int findKthLargestByQuickSort(int[] nums, int left, int right, int k) {
            if (left > right) {
                // 区间中没有元素,无法继续划分区间(根据题意,这里永不可达)
                return -1;
            }
            // 随机化选取主元
            int pivot = partition(nums, left, right);
            int result;
            if (k == pivot + 1) {
                // 第K大的元素是当前主元
                result = nums[pivot];
            } else if (k < pivot  + 1) {
                // 第K大的元素在主元左侧
                result = findKthLargestByQuickSort(nums, left, pivot - 1, k);
            } else {
                // 第K大的元素在主元右侧
                result = findKthLargestByQuickSort(nums, pivot + 1, right, k);
            }
            // 划分右侧子区间
            return result;
        }
    
        private int partition(int[] nums, int left, int right) {
            int pivot = nums[right];
            int i = left - 1;
            // 根据主元将数组划分为左右子数组(左侧子数组>主元,右侧子数组<=主元)
            for (int j = left; j < right; j++) {
                if (nums[j] > pivot) {
                    // 将比主元大的元素放到 i+1 的左侧
                    swap(nums, ++i, j);
                }
            }
            // 将主元放到分界点,然后返回主元索引
            swap(nums, ++i, right);
            return i;
        }
    
        private void swap(int[] nums, int i, int j) {
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    }
    

    复杂度分析:

    • 时间复杂度:最坏 O ( n 2 ) O(n^2) O(n2),最好 O ( n l o g n ) O(nlogn) O(nlogn)
    • 空间复杂度: O ( l o g n ) O(logn) O(logn)

    其中 n n n 为数组中元素的个数

    代码优化:时间复杂度优化

    由于快速排序是不稳定的,最好的情况 O ( n 2 ) O(n^2) O(n2),最坏的情况是 n l o g n nlogn nlogn,为了提高快排的稳定性,我们可以引入一个随机因子,使得时间复杂度的期望值接近于 O ( n ) O(n) O(n),这个快排是最基本的算法,这里不做过多介绍了,感兴趣的可以参考我的另一篇文章:详解十大排序算法,这篇文章详细介绍了 十大排序算法的思路、实现,还有十分详细的图解

    import java.util.Random;
    
    /**
     * @author ghp
     * @title
     * @description
     */
    class Solution {
        public int findKthLargest(int[] nums, int k) {
            return findKthLargestByQuickSort(nums, 0, nums.length - 1, k);
        }
    
        private int findKthLargestByQuickSort(int[] nums, int left, int right, int k) {
            if (left > right) {
                // 区间中没有元素,无法继续划分区间(根据题意,这里永不可达)
                return -1;
            }
            // 随机化选取主元
            int pivot = randomPartition(nums, left, right);
            int result;
            if (k == pivot + 1) {
                // 第K大的元素是当前主元
                result = nums[pivot];
            } else if (k < pivot + 1) {
                // 第K大的元素在主元左侧
                result = findKthLargestByQuickSort(nums, left, pivot - 1, k);
            } else {
                // 第K大的元素在主元右侧
                result = findKthLargestByQuickSort(nums, pivot + 1, right, k);
            }
            // 划分右侧子区间
            return result;
        }
    
        private int randomPartition(int[] nums, int left, int right) {
            // 从子区间中随机选取一个元素作为主元
            Random random = new Random();
            int nonce = random.nextInt(right - left + 1) + left;
            swap(nums, nonce, right);
            int pivot = nums[right];
            int i = left - 1;
            // 根据主元将数组划分为左右子数组(左侧子数组>主元,右侧子数组<=主元)
            for (int j = left; j < right; j++) {
                if (nums[j] > pivot) {
                    // 将比主元大的元素放到 i+1 的左侧
                    swap(nums, ++i, j);
                }
            }
            // 将主元放到分界点,然后返回主元索引
            swap(nums, ++i, right);
            return i;
        }
    
        private void swap(int[] nums, int i, int j) {
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    }
    
  • 解法三:堆排序

    
    

最大正方形

🔒题目

原题链接:221.最大正方形

image-20230718140916124

🔑题解

  • 解法一:向下扩展

    本题思路可以参考这题:【LeetCode热题100】打卡第26天:最大矩形

    两者是类似的题目,这题相较于 最大矩形 哪一题多了一个限制,那就是长要等于宽。这里就不多做讲解了 (●ˇ∀ˇ●)

    PS:我感觉很离谱,前面那一题比这一题限制要少,但却是困难提,这一题限制多反而是中等题,不知道LeetCode是怎么划分题目难度的,我表示很疑惑🙄

    image-20230718161627512

    image-20230718161657209

    /**
     * @author ghp
     * @title
     * @description
     */
    class Solution {
        public int maximalSquare(char[][] matrix) {
            int row = matrix.length;
            int col = matrix[0].length;
            // 构建width数组
            int[][] sum = new int[row][col];
            for (int i = 0; i < row; i++) {
                for (int j = 0; j < col; j++) {
                    if (matrix[i][j] == '1') {
                        if (j == 0) {
                            sum[i][j] = 1;
                        } else {
                            sum[i][j] += sum[i][j - 1] + 1;
                        }
                    } else {
                        sum[i][j] = 0;
                    }
                }
            }
            int ans = 0;
            // 枚举每一个节点
            for (int i = 0; i < row; i++) {
                for (int j = 0; j < col; j++) {
                    int width = sum[i][j];
                    // 枚举当前节点下的所有行
                    for (int k = i; k < row; k++) {
                        int height = k - i + 1;
                        if (sum[k][j] < height || height > width) {
                            // 出现断层 || 高已经超过当前宽度
                            break;
                        }
                        // 更新正方形的宽(正方形的面积受限于当前已遍历层中的最小宽)
                        width = Math.min(width, sum[k][j]);
                        // 更新最大矩形的面积(正方形的面积受限于长和宽)
                        int t = Math.min(width, height);
                        ans = Math.max(ans, t * t);
                    }
                }
            }
            return ans;
        }
    }
    

    复杂度分析:

    • 时间复杂度: O ( n 2 ∗ m ) O(n^2*m) O(n2m)
    • 空间复杂度: O ( n ∗ m ) O(n*m) O(nm)

    其中 n n n 为数组的行, m m m为数组的列

    可以看到,时间复杂度虽然比较高,但是却能过,这是因为我们通过if (sum[k][j] < height || height > width)提前剔除了大量不必要的计算(起到了类似于剪枝的效果),从而使得加速计算出结果

  • 解法二:往上扩展

    解法一,自上而下枚举,节点要枚举两遍,第一遍枚举构建sum数组,第二遍枚举计算节点能构成矩形的最大面积。我们可以换一种思路,自下而上枚举,在枚举节点构建sum数组的同时,还计算当前节点能构成矩形的最大面积,这样就能够省掉一遍枚举,虽然时间复杂度没有减小,但是节约了时间😄

    PS:同样的,也是参考之前最大矩形这题写的,不是很理解的可以回看,前面最大矩形那一题进行了详细的详解,有图有分析,可以说是手把手教学

    /**
     * @author ghp
     * @title
     * @description
     */
    class Solution {
        public int maximalSquare(char[][] matrix) {
            int row = matrix.length;
            int col = matrix[0].length;
            int[][] sum = new int[row][col];
            int ans = 0;
            for (int i = 0; i < row; i++) {
                // 构建sum数组
                for (int j = 0; j < col; j++) {
                    if (matrix[i][j] == '1') {
                        if (j == 0) {
                            sum[i][j] = 1;
                        } else {
                            sum[i][j] += sum[i][j - 1] + 1;
                        }
                    } else {
                        sum[i][j] = 0;
                    }
                    int width = sum[i][j];
                    // 以当前节点为矩形的右下角,往上枚举
                    for (int k = i; k >= 0; k--) {
                        int height = i - k + 1;
                        if (sum[k][j] < height || height > width) {
                            // 出现断层 || 高已经超过当前宽度
                            break;
                        }
                        // 更新正方形的宽(正方形的面积受限于当前已遍历层中的最小宽)
                        width = Math.min(width, sum[k][j]);
                        // 更新最大矩形的面积(正方形的面积受限于长和宽)
                        int t = Math.min(width, height);
                        ans = Math.max(ans, t * t);
                    }
                }
            }
            return ans;
        }
    }
    

    复杂度分析:

    • 时间复杂度: O ( n 2 ∗ m ) O(n^2*m) O(n2m)
    • 空间复杂度: O ( n ∗ m ) O(n*m) O(nm)

    其中 n n n 为数组的行, m m m为数组的列

  • 解法三:动态规划

    
    
  • 解法四:单调栈

    其实本题也是参考我前面写的那一题,这里再重新讲解一下本题的思路(现在思路是有,但是代码还有有一点写不出┭┮﹏┭┮)

    1. 构建sum数组,sum数组就是纵向求和

      image-20230718171832610

      image-20230718171839180

      1. 一层一层的遍历数组,把当前层看着一个地平线

        比如第一层:

        image-20230718171955378

        我们在遍历第一层的同时将柱子不断加入栈中,保证栈是严格单调递增的,因为只有保持栈中柱子是单调递增,我们才能够确定左边界(左边矮,右边高,自然左侧柱子就是左边界)

        image-20230718173040176

        image-20230718173047784

        不断迭代,最终我们就可以得到最大正方形的面积

    import java.util.ArrayDeque;
    import java.util.Deque;
    
    /**
     * @author ghp
     * @title
     * @description
     */
    class Solution {
        public int maximalSquare(char[][] matrix) {
            int row = matrix.length;
            int col = matrix[0].length;
            // 构建sum数组
            int[][] sum = new int[row + 1][col + 1];
            for (int i = 1; i <= row; i++) {
                for (int j = 1; j <= col; j++) {
                    sum[i][j] = matrix[i - 1][j - 1] == '0' ? 0 : sum[i - 1][j] + 1;
                }
            }
            // 遍历每一层的柱子,更新max
            int ans = Integer.MIN_VALUE;
            for (int i = 1; i <= row; i++) {
                // 更新正方形的最大面积
                ans = Math.max(ans, largestSquareArea(sum[i]));
            }
            return ans;
        }
    
        private int largestSquareArea(int[] heights) {
            // 创建一个临时数组,前0用于计算第一个柱子的面积,后一个0用于强制出栈(这一步超级重要)
            int[] tempArr = new int[heights.length + 2];
            for (int i = 1; i < heights.length + 1; i++) {
                tempArr[i] = heights[i - 1];
            }
            // 递增栈(栈中存储柱子的索引号,栈中的柱子的高度严格递增)
            Deque<Integer> stack = new ArrayDeque<>();
            int ans = 0;
            for (int i = 0; i < tempArr.length; i++) {
                while (!stack.isEmpty() && tempArr[i] < tempArr[stack.peek()]) {
                    // 当前柱子的右边界已确定,可以计算已弹出柱子的面积
                    int height = tempArr[stack.poll()];
                    int width =  i - stack.peek() - 1;
                    int side = Math.min(height, width);
                    ans = Math.max(ans, side * side);
                }
                stack.push(i);
            }
            return ans;
        }
    }
    

    代码优化

    这段代码是我叫ChartGPT对上面代码进行的一个优化,我也是试一试,没想到他还真给我优化了,这人工智能是真的牛,可能我没有想到这样子写,但是这个代码还是能够看的懂的😄,优化前我们通过自上而下遍历数组构建sum数组,但是发现矩形的最大面积的更新只跟当前层有关,与下一层无关,所以我们可以在构建sum数组的同时利用单调栈去更新当前最大正方形的面积。这里优化思路和前面的 往下扩展==>往上扩展 是一样的,都是在构建sum数组的时候更新最大正方形的面积,我相信只要看懂优化前的代码,优化后的代码也是很容易能够看懂的,至于能否写出这么优雅的代码,就需要不但的积累和练习了,优雅不是一蹴而就的,需要不断积累,正如我刷题专栏上写的铭言:不积硅步无以至千里,不积小流无以至千里。天赋这东西不得不承认他是存在的,但是这东西不是我所能决定的,后期的努力我所能决定的,我是坚信能够勤能补拙的,不渴望能够媲美那些天赋型选手,只希望能战胜和我差不多的选手,加油(ง •_•)ง

    public int maximalSquare(char[][] matrix) {
        int rows = matrix.length;
        int cols = matrix[0].length;
        int[] heights = new int[cols + 1]; // 高度数组,最后一个元素为0
        
        int maxArea = 0;
        Deque<Integer> stack = new ArrayDeque<>(); // 单调栈,存放柱子的下标
        
        for (int i = 0; i < rows; i++) {
            // 构建柱状图,更新每一列的高度
            for (int j = 0; j < cols; j++) {
                if (matrix[i][j] == '1') {
                    heights[j]++;
                } else {
                    heights[j] = 0;
                }
            }
            
            stack.clear();
            
            // 计算每个柱子的面积
            for (int j = 0; j <= cols; j++) {
                while (!stack.isEmpty() && heights[j] < heights[stack.peek()]) {
                    // 当前柱子的右边界已确定,可以计算已弹出柱子的面积
                    int height = heights[stack.pop()];
                    int width = stack.isEmpty() ? j : (j - stack.peek() - 1);
                    int side = Math.min(height, width);
                    maxArea = Math.max(maxArea, side * side);
                }
                stack.push(j);
            }
        }
        
        return maxArea;
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/767735.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

若依(Ruoyi)前后端分离版项目部署到服务器(Linux环境)后,刷新页面报错:404 Not Found

原文章:若依(ruoyi)前后端分离版使用教程之若依后端部署阿里云服务器步骤(超详细)_蓝多多的小仓库的博客-CSDN博客 问题: 在若依项目部署服务器后,可以正常运行,但如果执行刷新页面操作,便会出现404 Not Found。 原因: Nginx未正确配置。由于后台路由采用History模式…

软件测试银行项目面试过程

今天参加了一场比较正式的面试&#xff0c;汇丰银行的视频面试。在这里把面试的流程记录一下&#xff0c;结果还不确定&#xff0c;但是面试也是自我学习和成长的过程&#xff0c;所以记录下来大家也可以互相探讨一下。 请你做一下自我介绍&#xff1f;&#xff08;汇丰要求英…

【JAVA】为char所提供包装类——Character类

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️初识JAVA】 文章目录 前言Character 类装箱(boxing)拆箱(unboxing)转义序列Java的转义序列实例Character 方法isDigit()isLetter()isWhitespace()toUpperCase()toLowerCase()compareTo()isAlphabetic…

《线程池的执行流程》

目录 什么是线程池 线程池的优点 线程池的执行流程 线程池的状态 什么是线程池 线程池是一种多线程处理形式&#xff0c;内部维护了若干个线程。 没有线程任务的时候&#xff0c;线程都处于空闲状态。如果有新的线程任务&#xff0c;就分配给空闲线程执行。如果所有线程都…

【算法与数据结构】144、145、94LeetCode二叉树的前中后遍历(递归法、迭代法)

文章目录 一、题目二、递归算法三、迭代算法四、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、递归算法 思路分析&#xff1a;这道题比较简单&#xff0c;不多说了&#xff0c;大家直接看代码就行。注意前中后…

家具商城小程序:连接优质家居产品的桥梁

随着人们对家居生活品质的追求&#xff0c;家具商城小程序成为提供便捷购物和个性化服务的不可或缺的工具&#xff0c;通过家具商城小程序&#xff0c;用户可以浏览并购买各类家具产品&#xff0c;如沙发、床、桌子等。同时&#xff0c;家具商城小程序还提供个性化的推荐、客户…

【实战篇】docker-compose部署go项目

一、场景&#xff1a; 二、需求 三、实操 Stage 1&#xff1a;GoLand 中 build 生成二进制文件 Stage 2&#xff1a;编写 Dockerfile Stage 3&#xff1a;编写 docker-compose.yaml Stage 4&#xff1a;文件上传到 ubuntu 服务器上 Stage 5&#xff1a;运行 docker-comp…

uni-app做h5IOS底部tabbar高度在不同的tabbar页面会忽高忽低

原因不祥&#xff0c;解决办法的话在App.vue中 <style langscss> //每个页面公共css page { height:100vh; } </style>

word 2019无法打开邮箱下载的docx文档

我通过如下设置&#xff0c;打开了文档&#xff1a; 开始——选型——信任中心——信任中心设置&#xff0c;如下图 2.把宏设置改一下&#xff0c;如下图 3.把受保护的视图设置一下&#xff0c;如下图 3.文件阻止设置为默认&#xff0c;我没有改&#xff0c;如下图 结束了…

Android平台GB28181设备接入端语音广播技术探究和填坑指南

技术背景 GB/T28181-2016官方规范和交互流程&#xff0c;我们不再赘述。 SIP服务器发起广播流程示意图如下&#xff1a; 需要注意的是&#xff1a;语音广播通知、语音广播应答命令 消息头 Content-type字段为 Content-type:Application/MANSCDPxml。 语音广播通知、语音广播…

vue写车牌号 自定义键盘

vue写车牌号自定义键盘 <template><div><div class"content-wrapper"><div class"content-top-wrapper"><van-radio-group v-model"radioCarType"><van-radio name"1">蓝牌<imgslot"icon…

COC 深圳城市开发者社区【职言职语】第1季活动来袭。。。

文章目录 活动介绍活动议程活动奖品参与方式活动时间活动费用活动地点 活动介绍 &#x1f389;&#x1f465; 欢迎加入职言职语活动&#xff01;与我们一起探索职场的智慧和灵感&#xff01; &#x1f454;&#x1f4bc; 在这个快节奏的职业世界中&#xff0c;职场话题 是我们…

【实战总结】SpringMVC架构升级SpringCloudAlibaba

升级目标 SpringMVCDubboZookeeper分布式架构改为Spring Cloud Alibaba微服务 技术框架:Spring Boot 2.7.2、Spring Cloud 2021.0.3 & Alibaba 2021.0.1.0 容器:Tomcat 9.0.65 JDK:1.8 配置中心:Nacos 2.0.4 消息队列:RocetMQ 4.9.3 配置中心:Apollo 11.0 缓存: Redis 4.0…

大华智慧园区综合管理平台存在任意文件上传漏洞

大华智慧园区综合管理平台存在任意文件上传漏洞 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作…

Ceph

Ceph简介 Ceph使用C语言开发&#xff0c;是一个开放、自我修复和自我管理的开源分布式存储系统。具有高扩展性、高性能、高可靠性的优点。Ceph目前已得到众多云计算厂商的支持并被广泛应用。RedHat及OpenStack&#xff0c;Kubernetes都可与Ceph整合以支持虚拟机镜像的后端存储…

CMake:设置语言标准(一)

CMake:设置语言标准&#xff08;一&#xff09; 导言C标准历史C11版本特性介绍类型推导之auto和decltypeC返回值类型后置对模板实例化中连续尖括号>>的改进使用using定义别名&#xff08;替代typedef&#xff09;支持函数模板的默认参数在函数模板和类模板中使用可变参数…

接下来讲一讲Vue的数据代理

首先讲一下原生js的数据代理 原生的 Object.defineProperty() let aa wewewlet person {name: "王李斌",age: 12} Object.defineProperty(person, "address", {// value: 14&#xff0c; 给字段设置值//enumerable:true, 设置动态设置的字段为可以遍历/…

Docker 镜像解密:分层存储与构建原理多角度解析

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

超声波水表的优势

超声波水表是一种新型的水表&#xff0c;它采用超声波测流技术&#xff0c;能够实现对水流的非接触式测量&#xff0c;具有许多优势。下面将从多个方面来介绍超声波水表的优势。 一、高精度 由于超声波测流技术采用的是非接触式测量&#xff0c;因此不会受到水流的摩擦、涡流等…

【C++】C++11 (3): lambda表达式和包装器

一、lambda表达式 C98中的一个例子 在C98中&#xff0c;如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用std::sort方法。 #include <algorithm> #include <functional> int main() {int a[] { 4,1,8,5,3,7,0,9,2,6 };// 默认按照小于比较&#xff…