标题:按递增顺序显示卡牌
6 级
给定一个整数数组 deck \texttt{deck} deck,表示一套牌组,牌组中的每张卡牌都对应有一个唯一的整数。第 i \texttt{i} i 张卡牌上的数字是 deck[i] \texttt{deck[i]} deck[i]。
你可以按你想要的顺序对这套卡片进行排序。最初,这些卡牌在牌组里是正面朝下的(即,未显示状态)。
现在,重复执行以下步骤,直到显示所有卡牌为止:
返回能以递增顺序显示卡牌的牌组顺序。
答案中的第一张牌被认为处于牌组顶部。
示例 1:
输入:
deck
=
[17,13,11,2,3,5,7]
\texttt{deck = [17,13,11,2,3,5,7]}
deck = [17,13,11,2,3,5,7]
输出:
[2,13,3,11,5,17,7]
\texttt{[2,13,3,11,5,17,7]}
[2,13,3,11,5,17,7]
解释:
我们得到的牌组顺序为
[17,13,11,2,3,5,7]
\texttt{[17,13,11,2,3,5,7]}
[17,13,11,2,3,5,7](这个顺序不重要),然后将其重新排序。
重新排序后,牌组以
[2,13,3,11,5,17,7]
\texttt{[2,13,3,11,5,17,7]}
[2,13,3,11,5,17,7] 开始,其中
2
\texttt{2}
2 位于牌组的顶部。
我们显示
2
\texttt{2}
2,然后将
13
\texttt{13}
13 移到底部。牌组现在是
[3,11,5,17,7,13]
\texttt{[3,11,5,17,7,13]}
[3,11,5,17,7,13]。
我们显示
3
\texttt{3}
3,然后将
11
\texttt{11}
11 移到底部。牌组现在是
[5,17,7,13,11]
\texttt{[5,17,7,13,11]}
[5,17,7,13,11]。
我们显示
5
\texttt{5}
5,然后将
17
\texttt{17}
17 移到底部。牌组现在是
[7,13,11,17]
\texttt{[7,13,11,17]}
[7,13,11,17]。
我们显示
7
\texttt{7}
7,然后将
13
\texttt{13}
13 移到底部。牌组现在是
[11,17,13]
\texttt{[11,17,13]}
[11,17,13]。
我们显示
11
\texttt{11}
11,然后将
17
\texttt{17}
17 移到底部。牌组现在是
[13,17]
\texttt{[13,17]}
[13,17]。
我们显示
13
\texttt{13}
13,然后将
17
\texttt{17}
17 移到底部。牌组现在是
[17]
\texttt{[17]}
[17]。
我们显示
17
\texttt{17}
17。
由于所有卡片都是按递增顺序排列显示的,所以答案是正确的。
示例 2:
输入:
deck
=
[1,1000]
\texttt{deck = [1,1000]}
deck = [1,1000]
输出:
[1,1000]
\texttt{[1,1000]}
[1,1000]
这道题要求返回以递增顺序显示卡牌的牌组顺序。如果直接考虑从卡牌的递增顺序还原得到原始牌组会比较困难,可以考虑另一个问题:对于原始牌组,根据题目描述中的操作,会以什么顺序显示卡牌。考虑卡牌的显示顺序时,不需要考虑卡牌上的数字,只需要考虑在显示顺序中的每张卡牌在原始牌组中的下标。
假设牌组中有 n n n 张卡牌,即数组 deck \textit{deck} deck 的长度是 n n n,则下标范围是 [ 0 , n − 1 ] [0, n - 1] [0,n−1]。用 indices \textit{indices} indices 记录显示顺序中的每张卡牌在原始牌组中的下标,用 original \textit{original} original 表示原始牌组的每张卡牌上的数字。
为了得到显示顺序中的每张卡牌在原始牌组中的下标,可以模拟显示卡牌的过程。由于显示卡牌的过程包括从牌组顶部抽牌和将牌放在牌组底部的操作,符合先进先出的特点,因此可以使用队列模拟。
将从 0 0 0 到 n − 1 n - 1 n−1 的每个整数依次入队,然后对于范围 [ 0 , n − 1 ] [0, n - 1] [0,n−1] 中的每个下标 i i i,执行以下操作:
将队首元素出队,记为 index \textit{index} index;
将 index \textit{index} index 赋给 indices [ i ] \textit{indices}[i] indices[i];
如果队列不为空,将队首元素出队,并将出队的元素在队尾入队。
上述操作之后, indices \textit{indices} indices 中的第 i i i 个元素表示在显示顺序中的第 i i i 个元素在原始牌组中的下标。
例如,当 n = 7 n = 7 n=7 时, indices = [ 0 , 2 , 4 , 6 , 3 , 1 , 5 ] \textit{indices} = [0, 2, 4, 6, 3, 1, 5] indices=[0,2,4,6,3,1,5],表示显示顺序中的每张卡牌在原始牌组中的下标依次是 0 0 0、 2 2 2、 4 4 4、 6 6 6、 3 3 3、 1 1 1、 5 5 5,显示顺序中的每张卡牌上的数字依次是 original [ 0 ] \textit{original}[0] original[0]、 original [ 2 ] \textit{original}[2] original[2]、 original [ 4 ] \textit{original}[4] original[4]、 original [ 6 ] \textit{original}[6] original[6]、 original [ 3 ] \textit{original}[3] original[3]、 original [ 1 ] \textit{original}[1] original[1]、 original [ 5 ] \textit{original}[5] original[5]。
为了得到原始牌组,需要执行以下两步操作:
将 deck \textit{deck} deck 排序;
根据 indices \textit{indices} indices 和排序后的 deck \textit{deck} deck 得到 original \textit{original} original。
对于第 1 步操作,Java 有内置的排序方法, Arrays \texttt{Arrays} Arrays 类的 sort \texttt{sort} sort 静态方法可以对数组排序。
对于第 2 步操作,可以将 deck \textit{deck} deck 中的元素按照从小到大的顺序依次填入 original \textit{original} original。对于 0 ≤ i < n 0 \le i < n 0≤i<n, deck [ i ] \textit{deck}[i] deck[i] 是第 i i i 张显示的卡牌。令 index = indices [ i ] \textit{index} = \textit{indices}[i] index=indices[i],根据 indices \textit{indices} indices 的定义可知, deck [ i ] = original [ index ] \textit{deck}[i] = \textit{original}[\textit{index}] deck[i]=original[index],因此将 deck [ i ] \textit{deck}[i] deck[i] 的值赋给 original [ index ] \textit{original}[\textit{index}] original[index]。
上述操作结束之后, original \textit{original} original 即为以递增顺序显示卡牌的原始牌组。
具体实现时,可以显性创建并计算 indices \textit{indices} indices,然后计算 original \textit{original} original,也可以不显性创建 indices \textit{indices} indices 直接计算 original \textit{original} original。第二种实现虽然可以节省一个数组的空间,但是由于有队列空间,因此两种实现的空间复杂度都是 O ( n ) O(n) O(n)。
下面的代码提供了这两种实现。
下面的代码为显性创建并计算 indices \textit{indices} indices 的做法。
class Solution {
public int[] deckRevealedIncreasing(int[] deck) {
int n = deck.length;
Queue<Integer> queue = new ArrayDeque<Integer>();
for (int i = 0; i < n; i++) {
queue.offer(i);
}
int[] indices = new int[n];
for (int i = 0; i < n; i++) {
int index = queue.poll();
indices[i] = index;
if (!queue.isEmpty()) {
queue.offer(queue.poll());
}
}
Arrays.sort(deck);
int[] original = new int[n];
for (int i = 0; i < n; i++) {
int index = indices[i];
original[index] = deck[i];
}
return original;
}
}
下面的代码为不显性创建 indices \textit{indices} indices 的做法。
class Solution {
public int[] deckRevealedIncreasing(int[] deck) {
int n = deck.length;
Queue<Integer> queue = new ArrayDeque<Integer>();
for (int i = 0; i < n; i++) {
queue.offer(i);
}
int[] original = new int[n];
Arrays.sort(deck);
for (int i = 0; i < n; i++) {
int index = queue.poll();
original[index] = deck[i];
if (!queue.isEmpty()) {
queue.offer(queue.poll());
}
}
return original;
}
}
时间复杂度: O ( n log n ) O(n \log n) O(nlogn),其中 n n n 是数组 deck \textit{deck} deck 的长度。对数组 deck \textit{deck} deck 排序的时间复杂度是 O ( n log n ) O(n \log n) O(nlogn),计算 indices \textit{indices} indices 和 original \textit{original} original 的时间复杂度是 O ( n ) O(n) O(n),因此总时间复杂度是 O ( n log n ) O(n \log n) O(nlogn)。
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 deck \textit{deck} deck 的长度。空间复杂度主要取决于队列空间,队列内的元素个数不会超过 n n n。
这道题的解法涉及到排序。由于题目要求以递增顺序显示卡牌,但是输入的数组 deck \textit{deck} deck 不一定是有序的,因此对数组 deck \textit{deck} deck 排序是必不可少的操作。
Java 的 Arrays \texttt{Arrays} Arrays 类和 Collections \texttt{Collections} Collections 类有内置的 sort \texttt{sort} sort 静态方法,分别可以对数组和列表( List \texttt{List} List 接口的对象)排序。只有当数组或列表中的元素可以比较大小时,才能通过调用 sort \texttt{sort} sort 方法排序,默认为升序排序。对于引用类型,可以在调用 sort \texttt{sort} sort 方法时自定义比较方法。
内置的 sort \texttt{sort} sort 方法的时间复杂度是 O ( n log n ) O(n \log n) O(nlogn),空间复杂度是 O ( log n ) O(\log n) O(logn),其中 n n n 是待排序的元素个数。
关于排序的种类、每种排序方法的实现和复杂度分析、排序的应用等内容,将在排序算法部分具体讲解。