有效括号的嵌套深度 图解题解
这道题到底在问什么
- 输入
- seq = "(()())"
- 输出
- [0,1,1,1,1,0]
- 输入
- 本节演示 seq = "((()))(())"
- 输出
- [0,1,0,0,1,0,0,1,1,0]
最优解:一步一步想明白
- 3记住这句:看括号在第几层,偶数层标 0 进 A、奇数层标 1 进 B。一层隔一层地分,配对括号同层同组。后面每一帧都在套它。
- 4开局:深度计数器 depth = 0,分组数组还全是空位。上面一排是要扫的括号串,下面这根栈每压入一个左括号就长高一层,栈高就是当前嵌套深度,也就是层号的来源。指针从第 0 个字符开始往右走。
- 5指针移到第 0 个字符,是一个左括号。现在栈高是 0,先看清它待在第几层,再决定标 0 还是标 1。
- 6这个左括号进栈前深度是 0,说明它待在第 0 层。第 0 层是偶数层,归 A 组,标 0。标完把它压进栈,深度长到 1。
- 7指针移到第 1 个字符,是一个左括号。现在栈高是 1,先看清它待在第几层,再决定标 0 还是标 1。
- 8这个左括号进栈前深度是 1,说明它待在第 1 层。第 1 层是奇数层,归 B 组,标 1。标完把它压进栈,深度长到 2。
- 9指针移到第 2 个字符,是一个左括号。现在栈高是 2,先看清它待在第几层,再决定标 0 还是标 1。
- 10这个左括号进栈前深度是 2,说明它待在第 2 层。第 2 层是偶数层,归 A 组,标 0。标完把它压进栈,深度长到 3。
- 11指针移到第 3 个字符,是一个右括号。现在栈高是 3,先看清它待在第几层,再决定标 0 还是标 1。
- 12这个右括号先弹掉栈顶,把它配对的那个左括号送走,深度从 3 落到 2。它封的正是第 2 层,和当年那个左括号同一层。第 2 层是偶数层,所以跟着标 0、进 A 组。
- 13指针移到第 4 个字符,是一个右括号。现在栈高是 2,先看清它待在第几层,再决定标 0 还是标 1。
- 14这个右括号先弹掉栈顶,把它配对的那个左括号送走,深度从 2 落到 1。它封的正是第 1 层,和当年那个左括号同一层。第 1 层是奇数层,所以跟着标 1、进 B 组。
- 15指针移到第 5 个字符,是一个右括号。现在栈高是 1,先看清它待在第几层,再决定标 0 还是标 1。
- 16这个右括号先弹掉栈顶,把它配对的那个左括号送走,深度从 1 落到 0。它封的正是第 0 层,和当年那个左括号同一层。第 0 层是偶数层,所以跟着标 0、进 A 组。
- 17指针移到第 6 个字符,是一个左括号。现在栈高是 0,先看清它待在第几层,再决定标 0 还是标 1。
- 18这个左括号进栈前深度是 0,说明它待在第 0 层。第 0 层是偶数层,归 A 组,标 0。标完把它压进栈,深度长到 1。
- 19指针移到第 7 个字符,是一个左括号。现在栈高是 1,先看清它待在第几层,再决定标 0 还是标 1。
- 20这个左括号进栈前深度是 1,说明它待在第 1 层。第 1 层是奇数层,归 B 组,标 1。标完把它压进栈,深度长到 2。
- 21指针移到第 8 个字符,是一个右括号。现在栈高是 2,先看清它待在第几层,再决定标 0 还是标 1。
- 22这个右括号先弹掉栈顶,把它配对的那个左括号送走,深度从 2 落到 1。它封的正是第 1 层,和当年那个左括号同一层。第 1 层是奇数层,所以跟着标 1、进 B 组。
- 23指针移到第 9 个字符,是一个右括号。现在栈高是 1,先看清它待在第几层,再决定标 0 还是标 1。
- 24这个右括号先弹掉栈顶,把它配对的那个左括号送走,深度从 1 落到 0。它封的正是第 0 层,和当年那个左括号同一层。第 0 层是偶数层,所以跟着标 0、进 A 组。
- 25整串扫完,栈又空了、深度回到 0,说明括号配对完整。标 0 的拼起来是 A = "(())()",标 1 的拼起来是 B = "()()",两串都仍然括号配对。原串最深 3 层,被一层隔一层劈开后,A 最深 2 层、B 最深 1 层,更深那组只有 2 层,正好是 3 层对半向上取整的最优结果。最终分组数组 [0,1,0,0,1,0,0,1,1,0]。
⚠️ 容易写错的地方
✗ 错:简单地把所有左括号标 0、所有右括号标 1
✓ 对:按括号所在层号的奇偶分组
全左括号挤进一组根本不是合法括号串,也没把深度对半分,最大深度还是原来那么深
✗ 错:左括号用「加一之后」的深度、右括号用「减一之前」的深度定组
✓ 对:左括号用加一之前的旧深度,右括号用减一之后的新深度
只有这样,一对配对括号才落在同一层号、分到同一组,两组才各自仍是合法括号串
✗ 错:以为非得真开一个栈把左括号存起来
✓ 对:一个整数深度计数器就够
栈里压的全是相同的左括号,真正有用的信息只是它有多高,也就是当前深度
完整代码(Python / C++ / Java)
Python
from __future__ import annotations
from typing import *
from collections import *
from functools import *
from itertools import *
from math import *
from heapq import *
from bisect import *
class Solution:
def maxDepthAfterSplit(self, seq: str) -> List[int]:
ans = [0] * len(seq)
x = 0
for i, c in enumerate(seq):
if c == "(":
ans[i] = x & 1
x += 1
else:
x -= 1
ans[i] = x & 1
return ansC++
#include <algorithm>
#include <array>
#include <cctype>
#include <climits>
#include <cmath>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
vector<int> maxDepthAfterSplit(string seq) {
int n = seq.size();
vector<int> ans(n);
for (int i = 0, x = 0; i < n; ++i) {
if (seq[i] == '(') {
ans[i] = x++ & 1;
} else {
ans[i] = --x & 1;
}
}
return ans;
}
};Java
import java.util.*;
class Solution {
public int[] maxDepthAfterSplit(String seq) {
int n = seq.length();
int[] ans = new int[n];
for (int i = 0, x = 0; i < n; ++i) {
if (seq.charAt(i) == '(') {
ans[i] = x++ & 1;
} else {
ans[i] = --x & 1;
}
}
return ans;
}
}复杂度
时间
O(n)
n 为括号串长度。每个字符只看一遍,算一次层号奇偶就定下它的组,整体是线性扫描
空间
O(1)
只需一个整数深度计数器;真实代码连栈都不用开。返回的分组数组是题目要求的输出,不计入额外空间
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 有效括号的嵌套深度 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么按层号奇偶交替分组,就能让更深那组的深度最小?+
原串某段嵌套有 D 层。把第 0、2、4 层这些偶数层分给 A,第 1、3、5 层这些奇数层分给 B,相当于把 D 层一层隔一层地劈成两摞,每摞最多 ceil(D 除以 2) 层。而无论怎么分,总有一组要扛下至少 ceil(D 除以 2) 层,这是个下界。交替分组正好达到这个下界,所以它就是最优。
这题和判断有效括号(lc20)、删最外层括号(lc1021)是什么关系?+
内核都是「一遍扫描维护嵌套深度」这个母题。lc20 用深度配合栈判括号是否匹配;lc1021 用深度找每个原语的最外层那对括号;这道题用深度的奇偶给括号染色分两组。认出深度计数这个套路,三道题就都通了。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 有效括号的嵌套深度 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。