Reveal Cards In Increasing Order
MedPrompt
You are given an integer array deck. There is a deck of cards where every card has a unique integer. The integer on the ith card is deck[i].
You can order the deck in any order you want. Initially, all the cards start face down (unrevealed) in one deck.
You will do the following steps repeatedly until all cards are revealed:
- Take the top card of the deck, reveal it, and take it out of the deck.
- If there are still cards in the deck then put the next top card of the deck at the bottom of the deck.
- If there are still unrevealed cards, go back to step 1. Otherwise, stop.
Return an ordering of the deck that would reveal the cards in increasing order.
Note that the first entry in the answer is considered to be the top of the deck.
Example 1:
Input: deck = [17,13,11,2,3,5,7]
Output: [2,13,3,11,5,17,7]
Explanation:
We get the deck in the order [17,13,11,2,3,5,7] (this order does not matter), and reorder it.
After reordering, the deck starts as [2,13,3,11,5,17,7], where 2 is the top of the deck.
We reveal 2, and move 13 to the bottom. The deck is now [3,11,5,17,7,13].
We reveal 3, and move 11 to the bottom. The deck is now [5,17,7,13,11].
We reveal 5, and move 17 to the bottom. The deck is now [7,13,11,17].
We reveal 7, and move 13 to the bottom. The deck is now [11,17,13].
We reveal 11, and move 17 to the bottom. The deck is now [13,17].
We reveal 13, and move 17 to the bottom. The deck is now [17].
We reveal 17.
Since all the cards revealed are in increasing order, the answer is correct.Example 2:
Input: deck = [1,1000]
Output: [1,1000]
Constraints:
1 <= deck.length <= 10001 <= deck[i] <= 106- All the values of
deckare unique.
Approaches
3 approaches with complexity analysis and trade-offs.
The most straightforward but highly inefficient approach is to try every possible arrangement of the deck. We can generate all permutations of the input cards and, for each permutation, simulate the revealing process to see if it produces cards in increasing order.
Algorithm
- Generate all permutations of the input
deck. - For each permutation
p:- Create a queue
qfromp. - Create an empty list
revealed. - While
qis not empty:- Reveal a card:
revealed.add(q.poll()). - If
qis not empty, move the next card to the bottom:q.add(q.poll()).
- Reveal a card:
- Check if
revealedis sorted in increasing order. - If it is, return
p.
- Create a queue
Walkthrough
The algorithm works as follows:
- Generate all unique permutations of the
deckarray. There areN!such permutations, whereNis the number of cards. - For each permutation, treat it as the initial deck and simulate the card revealing process:
- Use a queue to represent the deck.
- Repeatedly take the top card (reveal it) and if the deck is not empty, move the next top card to the bottom.
- Store the revealed cards in a list.
- After the simulation for a permutation is complete, check if the list of revealed cards is sorted in ascending order.
- If it is, we have found the correct ordering, so we can return it. If we check all permutations and none work (which is impossible given the problem statement), we would have no solution.
This method is guaranteed to find the solution but is computationally infeasible for the given constraints.
Complexity
Time
O(N! * N). There are N! permutations to check. For each permutation, the simulation involves N reveal steps. If a queue is used for the simulation, each step (poll, add) is O(1), making the simulation O(N). Thus, the total time complexity is O(N! * N).
Space
O(N). We need space to store the current permutation being tested and the queue for the simulation, both of which are of size N.
Trade-offs
Pros
Conceptually simple to understand if a permutation generation utility is available.
Cons
Extremely inefficient due to the factorial time complexity.
Only feasible for very small inputs (e.g., N < 10), making it impractical for the problem's constraints (N <= 1000).
A more practical approach is to work backward. We know the cards must be revealed in increasing order. So, if we sort the deck, we have the exact sequence of revealed cards. We can then reverse the revealing process to reconstruct the initial deck configuration. Using a standard ArrayList for this reconstruction is intuitive but results in a quadratic time complexity due to costly insertions at the beginning of the list.
Algorithm
- Sort the
deckarray in ascending order. - Create an empty
ArrayListnamedresult. - Iterate through the sorted
deckfrom the last element (n-1) down to the first (0). - For each card
deck[i]:- If
resultis not empty, remove the last element fromresultand insert it at index0. - Insert
deck[i]at index0ofresult.
- If
- Convert the
resultlist into an array and return it.
Walkthrough
The key insight is to reverse the operations. The forward process is: 1. Reveal top card. 2. Move next top card to bottom. The reverse process is: 1. Take the card that was moved to the bottom and place it on top. 2. Add the previously revealed card to the top.
The algorithm is as follows:
- Sort the input
deckin ascending order. This gives us the cards in the order they are revealed. - Create an empty
ArrayListto build our result deck. - Iterate through the sorted
deckin reverse order (from largest to smallest card). - For each card
c:- If the result list is not empty, undo the 'move to bottom' step. This means taking the last card in our list and moving it to the front. With an
ArrayList, this isresult.add(0, result.remove(result.size() - 1)). - Undo the 'reveal' step. This means adding the card
cto the front of the list:result.add(0, c).
- If the result list is not empty, undo the 'move to bottom' step. This means taking the last card in our list and moving it to the front. With an
- After processing all cards, the list contains the required deck ordering. Convert it to an array and return.
import java.util.Arrays;import java.util.ArrayList;import java.util.List; class Solution { public int[] deckRevealedIncreasing(int[] deck) { int n = deck.length; Arrays.sort(deck); List<Integer> resultList = new ArrayList<>(); for (int i = n - 1; i >= 0; i--) { if (!resultList.isEmpty()) { // Move the last element to the front resultList.add(0, resultList.remove(resultList.size() - 1)); } // Add the new card to the front resultList.add(0, deck[i]); } int[] finalDeck = new int[n]; for (int i = 0; i < n; i++) { finalDeck[i] = resultList.get(i); } return finalDeck; }}Complexity
Time
O(N^2). Sorting the deck takes O(N log N). The main loop runs N times. Inside the loop, inserting an element at the beginning of an `ArrayList` (`add(0, ...)`), takes O(k) time, where k is the current size of the list. This leads to a total simulation time of approximately 1 + 2 + ... + (N-1), which is O(N^2). The overall complexity is dominated by this quadratic factor.
Space
O(N). An `ArrayList` of size N is used to store the result.
Trade-offs
Pros
Much more efficient than brute-force.
The logic of reversing the process is sound and easier to reason about.
Cons
The use of an
ArrayListfor operations at the front of the list is inefficient, leading to a quadratic time complexity which can be slow for larger N.
This approach builds upon the reverse simulation logic but employs a more suitable data structure, a Double-Ended Queue (Deque), to achieve optimal performance. By using a Deque, the operations of adding to the front and moving an element from the back to the front can be done in constant time, reducing the overall time complexity to be dominated by the initial sort.
Algorithm
- Sort the
deckarray in ascending order. - Create an empty
Deque(e.g.,ArrayDeque). - Iterate through the sorted
deckfrom the last element (n-1) down to the first (0). - For each card
deck[i]:- If the deque is not empty, remove the last element and add it to the front of the deque.
- Add
deck[i]to the front of the deque.
- Convert the final deque into an array and return it.
Walkthrough
The algorithm is identical in logic to the previous approach but differs in implementation. We still sort the deck and reconstruct the result by iterating from the largest card to the smallest.
- Sort the input
deckin ascending order. - Create an empty
Deque(anArrayDequeis a good choice in Java). - Iterate through the sorted
deckin reverse order. - For each card
c:- If the deque is not empty, perform the reverse of 'move to bottom'. This involves taking the last element and putting it at the front:
deque.addFirst(deque.removeLast()). This is an O(1) operation for a deque. - Then, perform the reverse of 'reveal'. This involves adding the current card
cto the front:deque.addFirst(c). This is also an O(1) operation.
- If the deque is not empty, perform the reverse of 'move to bottom'. This involves taking the last element and putting it at the front:
- After the loop finishes, the deque holds the cards in the correct initial order. We can then convert it to an array and return.
This use of a Deque turns the O(N^2) simulation into an O(N) one, making the entire algorithm much faster.
import java.util.Arrays;import java.util.Deque;import java.util.ArrayDeque; class Solution { public int[] deckRevealedIncreasing(int[] deck) { int n = deck.length; Arrays.sort(deck); Deque<Integer> deque = new ArrayDeque<>(); for (int i = n - 1; i >= 0; i--) { if (!deque.isEmpty()) { deque.addFirst(deque.removeLast()); } deque.addFirst(deck[i]); } int[] result = new int[n]; int i = 0; for (int card : deque) { result[i++] = card; } return result; }}Complexity
Time
O(N log N). Sorting takes O(N log N). The simulation loop runs N times, and each operation on the `Deque` (`addFirst`, `removeLast`) takes O(1) constant time. Therefore, the simulation part takes O(N) time. The overall time complexity is dominated by the sorting step.
Space
O(N). A `Deque` of size N is used to store the result. If the sorting is done in-place, this is the main space overhead. Otherwise, space for the sorted copy is also O(N).
Trade-offs
Pros
Optimal time complexity.
Elegant and efficient solution.
Directly implements the reverse logic using the right data structure.
Cons
Requires familiarity with the Deque data structure and its constant-time operations at both ends.
Solutions
Solution
class Solution {public int[] deckRevealedIncreasing(int[] deck) { Deque<Integer> q = new ArrayDeque<>(); Arrays.sort(deck); int n = deck.length; for (int i = n - 1; i >= 0; --i) { if (!q.isEmpty()) { q.offerFirst(q.pollLast()); } q.offerFirst(deck[i]); } int[] ans = new int[n]; for (int i = n - 1; i >= 0; --i) { ans[i] = q.pollLast(); } return ans; }}class Solution {public: vector<int> deckRevealedIncreasing(vector<int> &deck) { sort(deck.rbegin(), deck.rend()); deque<int> q; for (int v : deck) { if (!q.empty()) { q.push_front(q.back()); q.pop_back(); } q.push_front(v); } return vector<int>(q.begin(), q.end()); }};class Solution: def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: q = deque() for v in sorted(deck, reverse=True): if q: q . appendleft(q . pop()) q . appendleft(v) return list(q)Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.