题目
标题和出处
标题:用栈操作构建数组
难度
3 级
题目描述
要求
给你一个目标数组 target \texttt{target} target 和一个整数 n \texttt{n} n。每次迭代,需要从 list = {1,2,3, … ,n} \texttt{list = \{1,2,3,\ldots,n\}} list = {1,2,3,…,n} 中依序读取一个数字。
请使用下述操作来构建目标数组 target \texttt{target} target:
- Push \texttt{Push} Push:从 list \texttt{list} list 中读取一个新元素, 并将其推入数组中。
- Pop \texttt{Pop} Pop:删除数组中的最后一个元素。
- 如果目标数组构建完成,就停止读取更多元素。
请返回构建目标数组所用的操作序列。如果有多种可能的答案,返回任意一种答案。
示例
示例 1:
输入:
target
=
[1,3],
n
=
3
\texttt{target = [1,3], n = 3}
target = [1,3], n = 3
输出:
["Push","Push","Pop","Push"]
\texttt{["Push","Push","Pop","Push"]}
["Push","Push","Pop","Push"]
解释:
读取
1
\texttt{1}
1 并自动推入数组
→
[1]
\rightarrow \texttt{[1]}
→[1]
读取
2
\texttt{2}
2 并自动推入数组,然后删除它
→
[1]
\rightarrow \texttt{[1]}
→[1]
读取
3
\texttt{3}
3 并自动推入数组
→
[1,3]
\rightarrow \texttt{[1,3]}
→[1,3]
示例 2:
输入:
target
=
[1,2,3],
n
=
3
\texttt{target = [1,2,3], n = 3}
target = [1,2,3], n = 3
输出:
["Push","Push","Push"]
\texttt{["Push","Push","Push"]}
["Push","Push","Push"]
示例 3:
输入:
target
=
[1,2],
n
=
4
\texttt{target = [1,2], n = 4}
target = [1,2], n = 4
输出:
["Push","Push"]
\texttt{["Push","Push"]}
["Push","Push"]
解释:只需要读取前
2
\texttt{2}
2 个数字就可以停止。
示例 4:
输入:
target
=
[2,3,4],
n
=
4
\texttt{target = [2,3,4], n = 4}
target = [2,3,4], n = 4
输出:
["Push","Pop","Push","Push","Push"]
\texttt{["Push","Pop","Push","Push","Push"]}
["Push","Pop","Push","Push","Push"]
数据范围
- 1 ≤ target.length ≤ 100 \texttt{1} \le \texttt{target.length} \le \texttt{100} 1≤target.length≤100
- 1 ≤ target[i] ≤ n \texttt{1} \le \texttt{target[i]} \le \texttt{n} 1≤target[i]≤n
- 1 ≤ n ≤ 100 \texttt{1} \le \texttt{n} \le \texttt{100} 1≤n≤100
- target \texttt{target} target 是严格递增的
解法
思路和算法
由于目标数组 target \textit{target} target 中的元素都不超过 n n n,因此构建目标数组时需要读取的数字都不会超过 n n n,而是由目标数组中的最大值决定。用 m m m 表示目标数组中的最大值,则遍历从 1 1 1 到 m m m 的每个数字即可构建目标数组。由于 m ≤ n m \le n m≤n,因此构建目标数组时不需要考虑 n n n。
对于从 1 1 1 到 m m m 的每个数字,如果该数字在目标数组中,则需要将其推入数组中,如果该数字不在目标数组中,则需要将其推入数组中然后删除。
为了得到每个数字是否在目标数组中,需要遍历目标数组中的每个元素。由于目标数组中的元素严格递增,因此遍历目标数组中的元素顺序一定是从小到大。
用 num \textit{num} num 表示在 [ 1 , m ] [1, m] [1,m] 范围内遍历到的数字,初始时 num = 1 \textit{num} = 1 num=1。遍历目标数组,假设当前遍历到的目标数组中的数字是 targetNum \textit{targetNum} targetNum,进行如下操作:
-
当 num < targetNum \textit{num} < \textit{targetNum} num<targetNum 时, num \textit{num} num 不在目标数组中,因此需要将 num \textit{num} num 推入数组中然后删除,需要一次 Push \text{Push} Push 操作和一次 Pop \text{Pop} Pop 操作,然后将 num \textit{num} num 的值加 1 1 1 表示遍历下一个数字;
-
将 num \textit{num} num 的值加 1 1 1 之后,如果仍有 num < targetNum \textit{num} < \textit{targetNum} num<targetNum,则重复上一步操作,直到 num = targetNum \textit{num} = \textit{targetNum} num=targetNum;
-
当 num = targetNum \textit{num} = \textit{targetNum} num=targetNum 时, num \textit{num} num 在目标数组中,因此需要将 num \textit{num} num 推入数组,需要一次 Push \text{Push} Push 操作,然后将 num \textit{num} num 的值加 1 1 1 表示遍历下一个数字。
遍历目标数组结束之后,从 1 1 1 到 m m m 的每个数字都遍历过,遍历过程中可以知道每个数字是否在目标数组中,并得到操作序列。
代码
class Solution {
public List<String> buildArray(int[] target, int n) {
List<String> operations = new ArrayList<String>();
int length = target.length;
int num = 1;
for (int i = 0; i < length; i++) {
int targetNum = target[i];
while (num < targetNum) {
operations.add("Push");
operations.add("Pop");
num++;
}
operations.add("Push");
num++;
}
return operations;
}
}
复杂度分析
-
时间复杂度: O ( m ) O(m) O(m),其中 m m m 是数组 target \textit{target} target 的最大元素。构建目标数组时,到达目标数组中的最大值 m m m 即停止构建,因此需要遍历从 1 1 1 到 m m m 的每个数字,对于每个数字,可能有一次 Push \text{Push} Push 操作,或者一次 Push \text{Push} Push 操作和一次 Pop \text{Pop} Pop 操作,因此每个数字的操作时间都是 O ( 1 ) O(1) O(1),总时间复杂度是 O ( m ) O(m) O(m)。题目满足 m ≤ n m \le n m≤n。
-
空间复杂度: O ( 1 ) O(1) O(1)。除了返回值以外,使用的空间复杂度是常数。