力扣热门算法题 85. 最大矩形,86. 分隔链表,88. 合并两个有序数组

58 篇文章 1 订阅
55 篇文章 0 订阅

85. 最大矩形,86. 分隔链表,88. 合并两个有序数组,每题做详细思路梳理,配套Python&Java双语代码, 2024.03.24 可通过leetcode所有测试用例

目录

85. 最大矩形

解题思路

完整代码

Python

Java 

86. 分隔链表

解题思路

完整代码

Python

Java 

88. 合并两个有序数组

解题思路

完整代码

Python

Java 


85. 最大矩形

给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例 1:

输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:6
解释:最大矩形如上图所示。

示例 2:

输入:matrix = [["0"]]
输出:0

示例 3:

输入:matrix = [["1"]]
输出:1

提示:

  • rows == matrix.length
  • cols == matrix[0].length
  • 1 <= row, cols <= 200
  • matrix[i][j] 为 '0' 或 '1'

解题思路

        关键是利用“柱状图中最大的矩形”问题的解法。我们可以将二维矩阵的每一行视为一个以行为底的柱状图,其中柱子的高度是从当前行向上连续的1的数量。然后,我们可以使用单调栈算法来找到每个柱状图中最大的矩形。通过这种方式,我们逐行处理整个矩阵,最终能够找到只包含1的最大矩形的面积。

具体步骤如下:

  1. 初始化一个数组heights,用于存储每一行转换成的柱状图的高度。初始时,heights的长度等于列数cols,所有元素初始化为0。
  2. 逐行遍历矩阵。对于每一行,更新heights数组,其中heights[j]表示第j列到当前行为止连续1的个数。
  3. 对于每一行的heights数组,使用单调栈算法找出柱状图中最大矩形的面积,并更新最大面积。
  4. 返回最大面积。

完整代码

Python
class Solution:
    def maximalRectangle(self, matrix: List[List[str]]) -> int:
        if not matrix or not matrix[0]:
            return 0

        max_area = 0
        n = len(matrix[0])
        heights = [0] * (n + 1)  # 加一是为了在最后一列后面添加一个0,方便处理最后一列

        for row in matrix:
            for i in range(n):
                heights[i] = heights[i] + 1 if row[i] == '1' else 0

            stack = [-1]
            for i in range(n + 1):
                while heights[i] < heights[stack[-1]]:
                    h = heights[stack.pop()]
                    w = i - 1 - stack[-1]
                    max_area = max(max_area, h * w)
                stack.append(i)

        return max_area
Java 
public class Solution {
    public int maximalRectangle(char[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) return 0;

        int maxArea = 0;
        int cols = matrix[0].length;
        int[] heights = new int[cols + 1]; // 加一是为了在最后一列后面添加一个0,方便处理最后一列

        for (char[] row : matrix) {
            for (int i = 0; i < cols; i++) {
                heights[i] = row[i] == '1' ? heights[i] + 1 : 0;
            }

            Stack<Integer> stack = new Stack<>();
            stack.push(-1);
            for (int i = 0; i <= cols; i++) {
                while (stack.peek() != -1 && heights[i] < heights[stack.peek()]) {
                    int h = heights[stack.pop()];
                    int w = i - 1 - stack.peek();
                    maxArea = Math.max(maxArea, h * w);
                }
                stack.push(i);
            }
        }

        return maxArea;
    }
}

86. 分隔链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

示例 1:

输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]

示例 2:

输入:head = [2,1], x = 2
输出:[1,2]

提示:

  • 链表中节点的数目在范围 [0, 200] 内
  • -100 <= Node.val <= 100
  • -200 <= x <= 200

解题思路

我们可以创建两个新的链表,一个用于存储所有小于 x 的节点,另一个用于存储所有大于或等于 x 的节点。遍历原始链表,根据节点的值将其分配到适当的新链表中。最后,将两个链表合并,使得所有小于 x 的节点都位于前面。

具体步骤如下:

  1. 创建两个哑节点beforeHeadafterHead作为两个新链表的头节点。这些哑节点有助于处理边缘情况,使得代码更加简洁。
  2. 使用两个指针beforeafter分别跟踪beforeHeadafterHead链表的当前位置。
  3. 遍历原始链表。对于每个节点:
    • 如果节点的值小于 x,则将其添加到before链表的末尾,并移动before指针。
    • 否则,将节点添加到after链表的末尾,并移动after指针。
  4. 在遍历完成后,将after链表连接到before链表的末尾。
  5. 返回beforeHead的下一个节点作为新链表的头节点,因为beforeHead是一个哑节点。

完整代码

Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:
        beforeHead = ListNode(0)
        afterHead = ListNode(0)
        before = beforeHead
        after = afterHead

        while head:
            if head.val < x:
                before.next = head
                before = before.next
            else:
                after.next = head
                after = after.next
            head = head.next

        after.next = None  # 防止成环
        before.next = afterHead.next  # 连接两个链表

        return beforeHead.next
Java 
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
public class Solution {
    public ListNode partition(ListNode head, int x) {
        ListNode beforeHead = new ListNode(0);
        ListNode afterHead = new ListNode(0);
        ListNode before = beforeHead, after = afterHead;

        while (head != null) {
            if (head.val < x) {
                before.next = head;
                before = before.next;
            } else {
                after.next = head;
                after = after.next;
            }
            head = head.next;
        }

        after.next = null;  // 防止成环
        before.next = afterHead.next;  // 连接两个链表

        return beforeHead.next;
    }
}

88. 合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。

示例 3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

解题思路

要合并两个已经排序的数组 nums1nums2,并保持合并后的数组也是排序的,可以使用双指针的方法从后向前遍历数组,以避免在合并时覆盖 nums1 中未处理的元素。具体步骤如下:

  1. 初始化两个指针 p1p2,分别指向 nums1nums2 的最后一个元素,即 p1 = m - 1p2 = n - 1。同时,初始化另一个指针 p 指向合并后的 nums1 的最后一个位置,即 p = m + n - 1
  2. 从后向前遍历 nums1nums2,比较 p1p2 指向的元素。将较大的元素复制到 p 指向的位置,并将对应的指针以及 p 向前移动一位。
  3. 如果 p1p2 中有一个先到达数组的开始位置,就将另一个数组剩余的元素直接复制到 nums1 的前面。
  4. 继续步骤2和3,直到 p1p2 都遍历完毕。

完整代码

Python
class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        p1, p2, p = m - 1, n - 1, m + n - 1

        while p1 >= 0 and p2 >= 0:
            if nums1[p1] > nums2[p2]:
                nums1[p] = nums1[p1]
                p1 -= 1
            else:
                nums1[p] = nums2[p2]
                p2 -= 1
            p -= 1

        # 如果 nums2 还有剩余元素,直接复制到 nums1 的前面
        nums1[:p2 + 1] = nums2[:p2 + 1]
Java 
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p1 = m - 1, p2 = n - 1, p = m + n - 1;

        while (p1 >= 0 && p2 >= 0) {
            if (nums1[p1] > nums2[p2]) {
                nums1[p] = nums1[p1];
                p1--;
            } else {
                nums1[p] = nums2[p2];
                p2--;
            }
            p--;
        }

        // 如果 nums2 还有剩余元素,直接复制到 nums1 的前面
        System.arraycopy(nums2, 0, nums1, 0, p2 + 1);
    }
}

  • 22
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昊昊该干饭了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值