欢迎光临
我们一直在努力

栈的经典应用:从「有效括号」到「寻找两个正序数组的中位数」深度解析

目录

一、有效括号(LeetCode 20・简单)

题目描述

解题思路

Java 代码实现(标准栈版)

复杂度分析

核心知识点总结

二、寻找两个正序数组的中位数(LeetCode 4・困难)

题目描述

解题思路

核心思想

Java 代码实现(标准二分版)

复杂度分析

核心知识点总结

三、算法思想对比与学习路径

学习建议

四、总结


大家好,今天我们来拆解两道经典算法题:一道是栈的入门必刷题有效括号,另一道是二分查找的天花板难题寻找两个正序数组的中位数。前者帮你彻底吃透栈的核心思想,后者带你解锁二分查找的高阶应用,非常适合作为算法学习的进阶内容~


一、有效括号(LeetCode 20・简单)

题目描述

给定一个只包括 '(',')','{','}','[',']' 的字符串 s,判断字符串是否有效。

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。
  • 每个右括号都有一个对应的相同类型的左括号。
  • 示例:

    plaintext

    输入:s = "()[]{}"
    输出:true

    输入:s = "(]"
    输出:false


    解题思路

    这道题是 ** 栈(Stack)** 的经典入门应用,核心逻辑非常简单:

  • 栈的特性:后进先出(LIFO),完美匹配括号「先开后闭」的顺序。
  • 遍历字符串:
    • 遇到左括号 (、{、[,就把对应的右括号压入栈中(方便后续匹配)
    • 遇到右括号,判断栈是否为空(空则说明没有对应左括号,直接返回 false),并弹出栈顶元素,判断是否与当前右括号匹配
  • 最终校验:遍历结束后,栈必须为空(说明所有左括号都被正确闭合),否则返回 false

  • Java 代码实现(标准栈版)

    java

    运行

    import java.util.Stack;

    public class ValidParentheses {
    public boolean isValid(String s) {
    // 奇数长度一定无效
    if (s.length() % 2 != 0) {
    return false;
    }

    Stack<Character> stack = new Stack<>();
    for (char c : s.toCharArray()) {
    // 遇到左括号,压入对应的右括号
    if (c == '(') {
    stack.push(')');
    } else if (c == '{') {
    stack.push('}');
    } else if (c == '[') {
    stack.push(']');
    } else {
    // 遇到右括号,判断栈是否为空,或栈顶不匹配
    if (stack.isEmpty() || stack.pop() != c) {
    return false;
    }
    }
    }

    // 栈为空说明所有括号都正确闭合
    return stack.isEmpty();
    }
    }


    复杂度分析

    • 时间复杂度:O(n),只遍历一次字符串,每个字符入栈 / 出栈最多一次。
    • 空间复杂度:O(n),最坏情况(全是左括号)需要存储整个字符串长度的栈空间。

    核心知识点总结

  • 栈的核心应用场景:匹配问题、逆序问题、深度优先搜索(DFS)、表达式求值等。
  • 括号匹配的关键:左括号入栈、右括号匹配出栈,最终栈空才有效。
  • 优化技巧:先判断字符串长度是否为奇数,直接排除无效情况,提升效率。

  • 二、寻找两个正序数组的中位数(LeetCode 4・困难)

    题目描述

    给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。

    算法的时间复杂度应该为 O(log(m+n))。

    示例:

    plaintext

    输入:nums1 = [1,3], nums2 = [2]
    输出:2.00000
    解释:合并数组 = [1,2,3] ,中位数 2

    输入:nums1 = [1,2], nums2 = [3,4]
    输出:2.50000
    解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5


    解题思路

    这道题的核心难点是时间复杂度要求 O(log(m+n)),因此不能用简单的合并数组法(时间复杂度 O(m+n)),必须用二分查找的思路。

    核心思想

    中位数的本质是:将两个数组的所有元素分成左右两部分,左部分元素个数 = 右部分元素个数(或差 1),且左部分最大值 ≤ 右部分最小值。

    我们的目标就是找到这个「分割线」:

  • 为了简化计算,我们始终让 nums1 是长度更短的数组(m ≤ n),保证二分查找的效率。
  • 对 nums1 进行二分,找到分割点 i,则 nums2 的分割点 j = (m + n + 1) / 2 – i(保证左部分总元素个数 = 右部分总元素个数)。
  • 定义四个边界值:
    • leftMax1:nums1 左部分的最大值(i == 0 时为 -∞)
    • rightMin1:nums1 右部分的最小值(i == m 时为 +∞)
    • leftMax2:nums2 左部分的最大值(j == 0 时为 -∞)
    • rightMin2:nums2 右部分的最小值(j == n 时为 +∞)
  • 当满足 leftMax1 ≤ rightMin2 且 leftMax2 ≤ rightMin1 时,分割线正确,计算中位数:
    • 总长度为奇数:中位数 = max(leftMax1, leftMax2)
    • 总长度为偶数:中位数 = (max(leftMax1, leftMax2) + min(rightMin1, rightMin2)) / 2
  • 若不满足条件,调整二分边界:
    • 若 leftMax1 > rightMin2:说明 nums1 分割点太靠右,需要左移(right = i – 1)
    • 若 leftMax2 > rightMin1:说明 nums1 分割点太靠左,需要右移(left = i + 1)

  • Java 代码实现(标准二分版)

    java

    运行

    public class FindMedianSortedArrays {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    // 保证nums1是长度更短的数组,简化二分
    if (nums1.length > nums2.length) {
    int[] temp = nums1;
    nums1 = nums2;
    nums2 = temp;
    }

    int m = nums1.length;
    int n = nums2.length;
    int left = 0;
    int right = m;
    // 左部分总元素个数 = (m + n + 1) / 2,保证奇数时左部分多1个
    int totalLeft = (m + n + 1) / 2;

    while (left <= right) {
    // nums1的分割点
    int i = left + (right – left) / 2;
    // nums2的分割点
    int j = totalLeft – i;

    // 边界处理:分割点在数组两端时,用无穷大/无穷小代替
    int leftMax1 = i == 0 ? Integer.MIN_VALUE : nums1[i – 1];
    int rightMin1 = i == m ? Integer.MAX_VALUE : nums1[i];
    int leftMax2 = j == 0 ? Integer.MIN_VALUE : nums2[j – 1];
    int rightMin2 = j == n ? Integer.MAX_VALUE : nums2[j];

    // 找到正确的分割线
    if (leftMax1 <= rightMin2 && leftMax2 <= rightMin1) {
    // 总长度为奇数
    if ((m + n) % 2 == 1) {
    return Math.max(leftMax1, leftMax2);
    } else {
    // 总长度为偶数
    return (Math.max(leftMax1, leftMax2) + Math.min(rightMin1, rightMin2)) / 2.0;
    }
    } else if (leftMax1 > rightMin2) {
    // nums1分割点太靠右,左移
    right = i – 1;
    } else {
    // nums1分割点太靠左,右移
    left = i + 1;
    }
    }

    // 理论上不会走到这里
    return 0.0;
    }
    }


    复杂度分析

    • 时间复杂度:O(logmin(m,n)),只对长度更短的数组进行二分,满足题目要求的 O(log(m+n))。
    • 空间复杂度:O(1),仅使用常数级额外空间。

    核心知识点总结

  • 二分查找的高阶应用:不直接查找元素,而是查找「分割线」,通过分割线的位置计算中位数。
  • 边界处理:必须处理分割点在数组两端的情况(用无穷大 / 无穷小代替),避免数组越界。
  • 分割点计算:j = (m + n + 1) / 2 – i 是核心公式,保证左右两部分元素个数相等(或差 1)。
  • 奇偶长度统一处理:通过 totalLeft = (m + n + 1) / 2,让奇数长度时左部分多 1 个,统一中位数计算逻辑。

  • 三、算法思想对比与学习路径

    表格

    算法核心思想适用场景时间复杂度
    栈(Stack) 后进先出(LIFO) 括号匹配、逆序、DFS、表达式求值 O(n)
    二分查找 分治,每次缩小一半范围 有序数组查找、中位数计算 O(logn)

    学习建议

  • 栈的学习:先掌握「有效括号」这道基础题,再进阶到「最长有效括号」「柱状图中最大的矩形」等难题,彻底吃透栈的应用。
  • 二分查找的学习:先掌握基础有序数组的二分查找,再进阶到旋转数组、中位数等难题,重点理解「分割线」的思想。

  • 四、总结

    • 有效括号:栈的经典入门题,核心是利用栈的后进先出特性匹配括号,是学习栈的必刷题。
    • 寻找两个正序数组的中位数:二分查找的天花板难题,核心是通过二分查找找到分割线,将两个数组分成左右两部分,从而计算中位数,是算法面试的高频难题。
    赞(0)
    未经允许不得转载:171主机测评 » 栈的经典应用:从「有效括号」到「寻找两个正序数组的中位数」深度解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址