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

这篇具有很好参考价值的文章主要介绍了【LeetCode热题100】打卡第39天:数组中第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个最大元素

【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形,# LeetCode热题100,编程练习,leetcode,算法,职场和发展

🔑题解

  • 解法一:使用快速排序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 ( 1 ) O(1) O(1)
    • 空间复杂度: 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 ( 1 ) O(1) O(1)
    • 空间复杂度: 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 ( 1 ) O(1) O(1)
    • 空间复杂度: O ( l o g n ) O(logn) O(logn)

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

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

    由于快速排序是不稳定的,最好的情况 O ( 1 ) O(1) O(1),最坏的情况是 O ( n 2 ) O(n^2) O(n2),为了提高快排的稳定性,我们可以引入一个随机因子,使得时间复杂度的期望值接近于 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;
        }
    }
    

    复杂度分析:

    • 时间复杂度: O ( n ) O(n) O(n)
    • 空间复杂度: O ( l o g n ) O(logn) O(logn)

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

  • 解法三:堆排序

    
    

最大正方形

🔒题目

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

【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形,# LeetCode热题100,编程练习,leetcode,算法,职场和发展

🔑题解

  • 解法一:向下扩展

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

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

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

    【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形,# LeetCode热题100,编程练习,leetcode,算法,职场和发展

    【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形,# LeetCode热题100,编程练习,leetcode,算法,职场和发展

    /**
     * @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数组就是纵向求和

      【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形,# LeetCode热题100,编程练习,leetcode,算法,职场和发展

      【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形,# LeetCode热题100,编程练习,leetcode,算法,职场和发展

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

        比如第一层:

        【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形,# LeetCode热题100,编程练习,leetcode,算法,职场和发展

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

        【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形,# LeetCode热题100,编程练习,leetcode,算法,职场和发展

        【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形,# LeetCode热题100,编程练习,leetcode,算法,职场和发展

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

    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数组的时候更新最大正方形的面积,我相信只要看懂优化前的代码,优化后的代码也是很容易能够看懂的,至于能否写出这么优雅的代码,就需要不但的积累和练习了,优雅不是一蹴而就的,需要不断积累,正如我刷题专栏上写的铭言:不积硅步无以至千里,不积小流无以至千里。天赋这东西不得不承认他是存在的,但是这东西不是我所能决定的,后期的努力我所能决定的,我是坚信能够勤能补拙的,不渴望能够媲美那些天赋型选手,只希望能战胜和我差不多的选手,加油(ง •_•)ง文章来源地址https://www.toymoban.com/news/detail-594721.html

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

到了这里,关于【LeetCode热题100】打卡第39天:数组中第K个最大元素&最大正方形的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【LeetCode热题100】打卡第38天:课程表&实现前缀树

    大家好,我是知识汲取者,欢迎来到我的LeetCode热题100刷题专栏! 精选 100 道力扣(LeetCode)上最热门的题目,适合初识算法与数据结构的新手和想要在短时间内高效提升的人,熟练掌握这 100 道题,你就已经具备了在代码世界通行的基本能力。在此专栏中,我们将会涵盖各种

    2024年02月17日
    浏览(53)
  • 【LeetCode热题100】打卡第33天:环形链表&LRU缓存

    大家好,我是知识汲取者,欢迎来到我的LeetCode热题100刷题专栏! 精选 100 道力扣(LeetCode)上最热门的题目,适合初识算法与数据结构的新手和想要在短时间内高效提升的人,熟练掌握这 100 道题,你就已经具备了在代码世界通行的基本能力。在此专栏中,我们将会涵盖各种

    2024年02月15日
    浏览(44)
  • 【LeetCode热题100】【子串】滑动窗口最大值

    题目 给你一个整数数组  nums ,有一个大小为  k   的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的  k  个数字。滑动窗口每次只向右移动一位。 返回  滑动窗口中的最大值  。 示例 1: 示例 2: 提示: 1 = nums.length = 105 -104 = nums[i] = 104 1 =

    2024年01月19日
    浏览(49)
  • 面试热题(数组中的第K个最大元素)

    给定整数数组  nums  和整数  k ,请返回数组中第  k  个最大的元素。 请注意,你需要找的是数组排序后的第  k  个最大的元素,而不是第  k  个不同的元素。        提到数组中最大元素,我们往往想到就是先给数组进行排序,然后取最大值,现在我们按照这个思路写一

    2024年02月13日
    浏览(54)
  • leetcode 215.数组中第k大的元素

    🌟 leetcode链接:数组中第k大的元素 思路: 使用堆数据结构,大堆的堆顶是堆内最大的元素,也就是把当前堆 pop k - 1 次,第 k 次 top 出来的元素就是第 k 大的数。 代码:

    2024年02月09日
    浏览(54)
  • 【leetcode热题100】接雨水、直方图最大矩形面积、矩阵中最大的矩形

    题目链接 题目描述: 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝

    2024年02月03日
    浏览(55)
  • 【LeetCode热题100】【子串】和为 K 的子数组

    题目 给你一个整数数组  nums  和一个整数  k  ,请你统计并返回  该数组中和为  k   的子数组的个数  。 子数组是数组中元素的连续非空序列。 示例 1: 示例 2: 提示: 1 = nums.length = 2 * 104 -1000 = nums[i] = 1000 -107 = k = 107 暴力  直接两层循环找出所有连续子数组的和,这个

    2024年01月19日
    浏览(44)
  • ( 数组和矩阵) 378. 有序矩阵中第 K 小的元素 ——【Leetcode每日一题】

    难度:中等 给你一个 n x n n x n n x n 矩阵 m a t r i x matrix ma t r i x ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。 请注意,它是 排序后 的第 k 小元素,而不是第 k 个 不同 的元素。 你必须找到一个内存复杂度优于 O ( n 2 ) O(n^2) O ( n 2 ) 的解决方案。 示例 1:

    2024年02月14日
    浏览(58)
  • 《LeetCode 热题 HOT 100》——寻找两个正序数组的中位数

    本期给大家带来的是是《 LeetCode 热题 HOT 100 》第四题—— 寻找两个正序数组的中位数的 题目讲解 !!!() 本文目录 💥题意分析 💥解题思路: 1、直接法  (❌) 2、归并思想 (❌) ①《LeetCode》第88题——合并两个有序数组 3、二分查找(✔️) 整体思想: 题目如下

    2023年04月27日
    浏览(43)
  • LeetCode 热题 100 JavaScript--108. 将有序数组转换为二叉搜索树

    给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 提示: 1 = nums.length = 104 -104 = nums[i] = 104 nums 按 严格递增 顺序排列

    2024年02月14日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包