969. Pancake Sorting
Problem Description
You are given an array of integers arr
that needs to be sorted using only a specific operation called a "pancake flip."
A pancake flip works as follows:
- Choose an integer
k
where1 <= k <= arr.length
- Reverse the first
k
elements of the array (the sub-array from index 0 to index k-1)
For example, if arr = [3,2,1,4]
and you perform a pancake flip with k = 3
, you would reverse the first 3 elements [3,2,1]
, resulting in arr = [1,2,3,4]
.
Your task is to return a list of k
values that represent a sequence of pancake flips that will sort the array. The sequence of flips you provide must sort the array within 10 * arr.length
operations.
The solution approach works by repeatedly placing the largest unsorted element in its correct position. For each position from the end of the array moving backwards:
- Find where the current largest element is located
- If it's not already in the correct position:
- First flip to move it to the front of the array (if it's not already there)
- Then flip to move it to its correct final position
The reverse
helper function performs the actual reversal of elements from index 0 to index j
. The main algorithm iterates through positions from n-1
down to 1, finding element i+1
and moving it to position i
using at most two flips per element.
Intuition
The key insight is to think about how we can systematically place each element in its correct position using only the pancake flip operation. Since we can only flip from the beginning of the array, we need a strategy to move any element to any position.
Consider this: if we want to move an element to a specific position, we can achieve this in two steps:
- First, bring the element to the front of the array (position 0)
- Then, flip enough elements to place it where we want it
This observation leads to a natural approach: sort the array from the largest element to the smallest. Why? Because once we place the largest element at the end of the array, we never need to touch it again - all subsequent flips will involve smaller subarrays that don't include it.
Think of it like stacking pancakes by size. We want the biggest pancake at the bottom. To do this:
- Find where the biggest pancake currently is in the stack
- Flip from that position to bring it to the top
- Flip the entire remaining stack to put it at the bottom
- Now forget about that pancake and repeat with the next biggest one
For example, with arr = [3,1,2,4]
:
- We want
4
at position 3 (the end). It's already there, so we move on - We want
3
at position 2. It's at position 0, so we flip the first 3 elements:[2,1,3,4]
- We want
2
at position 1. It's at position 0, so we flip the first 2 elements:[1,2,3,4]
- The array is now sorted
This greedy approach works because each element only needs to be positioned once, and positioning larger elements first ensures they stay in place while we sort the smaller ones.
Learn more about Greedy, Two Pointers and Sorting patterns.
Solution Approach
The implementation follows the intuition of placing each element in its correct position, starting from the largest element and working our way down to the smallest.
Helper Function - reverse(arr, j)
:
This function reverses the first j+1
elements of the array (from index 0 to index j
). It uses a two-pointer approach:
- Initialize pointers
i = 0
andj
- Swap elements at positions
i
andj
- Move
i
forward andj
backward until they meet
Main Algorithm: The algorithm processes elements from largest to smallest:
-
Outer Loop: Iterate
i
fromn-1
down to1
- At each iteration, we're trying to place element with value
i+1
at positioni
- At each iteration, we're trying to place element with value
-
Find Current Position:
j = i while j > 0 and arr[j] != i + 1: j -= 1
This finds the current position
j
of elementi+1
-
Perform Flips if Needed: If
j < i
(element is not in correct position):- First Flip (if
j > 0
):- Add
j + 1
to the answer list - Call
reverse(arr, j)
to bring elementi+1
to the front
- Add
- Second Flip:
- Add
i + 1
to the answer list - Call
reverse(arr, i)
to place elementi+1
at positioni
- Add
- First Flip (if
Example Walkthrough with arr = [3,2,1,4]
:
-
i = 3
: Looking for element4
at position 3- Found at position 3 (already correct), no flips needed
-
i = 2
: Looking for element3
at position 2- Found at position 0 (
j = 0
) - Skip first flip since
j = 0
(already at front) - Flip with
k = 3
:[1,2,3,4]
- Found at position 0 (
-
i = 1
: Looking for element2
at position 1- Found at position 1 (already correct), no flips needed
The algorithm guarantees at most 2 flips per element, giving us at most 2n
flips total, which is well within the 10n
limit.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with arr = [3,2,4,1]
.
Our goal is to place each element from largest (4) to smallest (2) in their correct positions using pancake flips.
Initial state: [3,2,4,1]
Step 1: Place element 4 at position 3
- Looking for element 4 (i=3, need value i+1=4)
- Found at position 2 (j=2)
- Since j < i, we need to move it:
- First flip with k=3 (j+1): Reverse first 3 elements
[3,2,4,1]
β[4,2,3,1]
(brings 4 to front)
- Second flip with k=4 (i+1): Reverse first 4 elements
[4,2,3,1]
β[1,3,2,4]
(places 4 at position 3)
- First flip with k=3 (j+1): Reverse first 3 elements
Step 2: Place element 3 at position 2
- Looking for element 3 (i=2, need value i+1=3)
- Found at position 1 (j=1)
- Since j < i, we need to move it:
- First flip with k=2 (j+1): Reverse first 2 elements
[1,3,2,4]
β[3,1,2,4]
(brings 3 to front)
- Second flip with k=3 (i+1): Reverse first 3 elements
[3,1,2,4]
β[2,1,3,4]
(places 3 at position 2)
- First flip with k=2 (j+1): Reverse first 2 elements
Step 3: Place element 2 at position 1
- Looking for element 2 (i=1, need value i+1=2)
- Found at position 0 (j=0)
- Since j < i, we need to move it:
- Skip first flip (j=0, already at front)
- Second flip with k=2 (i+1): Reverse first 2 elements
[2,1,3,4]
β[1,2,3,4]
(places 2 at position 1)
Final sorted array: [1,2,3,4]
Sequence of k values returned: [3, 4, 2, 3, 2]
This demonstrates how the algorithm systematically places each element in its correct position using at most 2 flips per element, working from the largest element down to ensure previously placed elements remain in their correct positions.
Solution Implementation
1class Solution:
2 def pancakeSort(self, arr: List[int]) -> List[int]:
3 """
4 Sorts the array using pancake flips.
5 A pancake flip at index k reverses the subarray arr[0:k+1].
6 Returns a list of k-values representing the sequence of flips performed.
7 """
8
9 def reverse_subarray(array: List[int], end_index: int) -> None:
10 """
11 Reverses the subarray from index 0 to end_index (inclusive).
12
13 Args:
14 array: The array to modify in-place
15 end_index: The last index of the subarray to reverse
16 """
17 start_index = 0
18 while start_index < end_index:
19 # Swap elements at start and end positions
20 array[start_index], array[end_index] = array[end_index], array[start_index]
21 start_index += 1
22 end_index -= 1
23
24 array_length = len(arr)
25 flip_sequence = []
26
27 # Process each position from the end to the beginning
28 for current_position in range(array_length - 1, 0, -1):
29 # Find where the target value (current_position + 1) is located
30 target_value = current_position + 1
31 target_index = current_position
32
33 # Search for the target value's current position
34 while target_index > 0 and arr[target_index] != target_value:
35 target_index -= 1
36
37 # If the target value is not already in its correct position
38 if target_index < current_position:
39 # First flip: bring target value to the front (if not already there)
40 if target_index > 0:
41 flip_sequence.append(target_index + 1)
42 reverse_subarray(arr, target_index)
43
44 # Second flip: move target value to its final position
45 flip_sequence.append(current_position + 1)
46 reverse_subarray(arr, current_position)
47
48 return flip_sequence
49
1class Solution {
2 /**
3 * Sorts an array using pancake sort algorithm.
4 * Pancake sort works by repeatedly finding the maximum element and flipping it to its correct position.
5 *
6 * @param arr The input array to be sorted
7 * @return List of flip positions (k-values) that will sort the array
8 */
9 public List<Integer> pancakeSort(int[] arr) {
10 int arrayLength = arr.length;
11 List<Integer> flipPositions = new ArrayList<>();
12
13 // Process from the largest element to the smallest
14 // Place each element at its correct position from right to left
15 for (int currentTargetIndex = arrayLength - 1; currentTargetIndex > 0; currentTargetIndex--) {
16 // Find the position of the element that should be at currentTargetIndex
17 // The target value is (currentTargetIndex + 1) since array is 1-indexed in value
18 int targetValue = currentTargetIndex + 1;
19 int currentPosition = currentTargetIndex;
20
21 // Search for the target value in the unsorted portion
22 while (currentPosition > 0 && arr[currentPosition] != targetValue) {
23 currentPosition--;
24 }
25
26 // If the element is not already in its correct position
27 if (currentPosition < currentTargetIndex) {
28 // First flip: bring the target element to the front (if not already there)
29 if (currentPosition > 0) {
30 flipPositions.add(currentPosition + 1); // Add 1 for 1-indexed flip position
31 reverse(arr, currentPosition);
32 }
33
34 // Second flip: move the element from front to its correct position
35 flipPositions.add(currentTargetIndex + 1); // Add 1 for 1-indexed flip position
36 reverse(arr, currentTargetIndex);
37 }
38 }
39
40 return flipPositions;
41 }
42
43 /**
44 * Reverses the first (endIndex + 1) elements of the array.
45 * This simulates a pancake flip operation.
46 *
47 * @param arr The array to perform the flip on
48 * @param endIndex The index up to which elements should be reversed (inclusive)
49 */
50 private void reverse(int[] arr, int endIndex) {
51 int startIndex = 0;
52
53 // Swap elements from both ends moving towards the center
54 while (startIndex < endIndex) {
55 int temp = arr[startIndex];
56 arr[startIndex] = arr[endIndex];
57 arr[endIndex] = temp;
58
59 startIndex++;
60 endIndex--;
61 }
62 }
63}
64
1class Solution {
2public:
3 vector<int> pancakeSort(vector<int>& arr) {
4 int n = arr.size();
5 vector<int> flips; // Store the sequence of flip operations
6
7 // Process each position from right to left (largest to smallest)
8 for (int currentSize = n - 1; currentSize > 0; --currentSize) {
9 // Find the position of the element that should be at index currentSize
10 // The target element value is (currentSize + 1)
11 int targetPos = currentSize;
12 while (targetPos > 0 && arr[targetPos] != currentSize + 1) {
13 --targetPos;
14 }
15
16 // If the element is already in the correct position, skip
17 if (targetPos == currentSize) {
18 continue;
19 }
20
21 // If the target element is not at the beginning,
22 // first flip to bring it to the front
23 if (targetPos > 0) {
24 flips.push_back(targetPos + 1); // Record flip size (1-indexed)
25 reverse(arr.begin(), arr.begin() + targetPos + 1);
26 }
27
28 // Now flip the first (currentSize + 1) elements to place
29 // the target element at its correct position
30 flips.push_back(currentSize + 1); // Record flip size (1-indexed)
31 reverse(arr.begin(), arr.begin() + currentSize + 1);
32 }
33
34 return flips;
35 }
36};
37
1/**
2 * Performs pancake sort on an array and returns the sequence of flips
3 * @param arr - The input array to be sorted
4 * @returns Array of k-values representing flip positions (1-indexed)
5 */
6function pancakeSort(arr: number[]): number[] {
7 const flips: number[] = [];
8
9 // Process from the end of array to the beginning
10 for (let currentSize = arr.length; currentSize > 1; currentSize--) {
11 // Find the index of maximum element in current unsorted portion
12 let maxIndex = 0;
13 for (let i = 1; i < currentSize; i++) {
14 if (arr[i] >= arr[maxIndex]) {
15 maxIndex = i;
16 }
17 }
18
19 // Skip if max element is already at its correct position
20 if (maxIndex === currentSize - 1) {
21 continue;
22 }
23
24 // Flip to bring max element to front, then flip to correct position
25 reverse(arr, maxIndex);
26 reverse(arr, currentSize - 1);
27
28 // Record the flip operations (1-indexed)
29 flips.push(maxIndex + 1);
30 flips.push(currentSize);
31 }
32
33 return flips;
34}
35
36/**
37 * Reverses elements in an array from index 0 to the specified end index
38 * @param nums - The array to perform reversal on
39 * @param end - The end index (inclusive) of the reversal
40 */
41function reverse(nums: number[], end: number): void {
42 let left = 0;
43 let right = end;
44
45 // Swap elements from both ends moving towards center
46 while (left < right) {
47 [nums[left], nums[right]] = [nums[right], nums[left]];
48 left++;
49 right--;
50 }
51}
52
Time and Space Complexity
Time Complexity: O(nΒ²)
The algorithm uses a nested loop structure:
- The outer loop runs from
n-1
to1
, executingn-1
iterations - For each iteration
i
:- The inner while loop searches for element
i+1
, which takesO(i)
time in the worst case - The
reverse
function is called at most twice, each takingO(i)
time
- The inner while loop searches for element
- Total time:
β(i=1 to n-1) O(i) = O(1 + 2 + ... + (n-1)) = O(n(n-1)/2) = O(nΒ²)
Space Complexity: O(1)
excluding the output array, or O(n)
including it
- The algorithm modifies the input array in-place
- Only a constant amount of extra variables are used (
i
,j
,n
) - The
reverse
function usesO(1)
additional space - The answer list
ans
stores the flip operations, which in the worst case can beO(2n) = O(n)
operations (at most 2 flips per element position) - If we exclude the output array (which is required for the answer), the space complexity is
O(1)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Off-by-One Errors in Index vs. k-value Conversion
The most common mistake in this problem is confusing array indices (0-based) with k-values (1-based) for pancake flips. The problem states that k represents the number of elements to flip, not the index.
Incorrect Implementation:
# Wrong: Using index directly as k-value if target_index > 0: flip_sequence.append(target_index) # ERROR: Should be target_index + 1 reverse_subarray(arr, target_index)
Correct Implementation:
# Correct: Converting index to k-value (number of elements) if target_index > 0: flip_sequence.append(target_index + 1) # Flip first (target_index + 1) elements reverse_subarray(arr, target_index)
Pitfall 2: Assuming Array Contains Values 1 to N
A critical error is assuming the array contains consecutive integers from 1 to n. The original solution searches for target_value = current_position + 1
, which only works if the array is a permutation of [1, 2, ..., n].
Problem Example:
arr = [10, 30, 20, 40] # Not [1,2,3,4] # The algorithm will search for values 4, 3, 2, 1 which don't exist!
Robust Solution:
def pancakeSort(self, arr: List[int]) -> List[int]:
def reverse_subarray(array, end_index):
start_index = 0
while start_index < end_index:
array[start_index], array[end_index] = array[end_index], array[start_index]
start_index += 1
end_index -= 1
n = len(arr)
flip_sequence = []
# Create a sorted version to know target values
sorted_arr = sorted(arr)
for current_position in range(n - 1, 0, -1):
# Find the value that should be at this position
target_value = sorted_arr[current_position]
# Find where this value currently is
target_index = arr.index(target_value)
if target_index != current_position:
# First flip: bring to front if not already there
if target_index > 0:
flip_sequence.append(target_index + 1)
reverse_subarray(arr, target_index)
# Second flip: place in correct position
flip_sequence.append(current_position + 1)
reverse_subarray(arr, current_position)
return flip_sequence
Pitfall 3: Inefficient Search for Target Element
The original solution uses a linear search backwards which can be inefficient and error-prone. Using list.index()
is cleaner but has its own considerations.
Potential Issue with Duplicates:
arr = [3, 2, 3, 1] # Contains duplicate 3s # list.index() will always return the first occurrence
Solution for Duplicates:
def pancakeSort(self, arr: List[int]) -> List[int]:
# ... (reverse_subarray function remains the same)
n = len(arr)
flip_sequence = []
# Work with indices paired with values to handle duplicates
indexed_arr = [(val, idx) for idx, val in enumerate(arr)]
indexed_arr.sort(reverse=True) # Sort by value, then by original index
for rank, (value, original_idx) in enumerate(indexed_arr):
target_position = n - 1 - rank
current_position = arr.index(value)
if current_position != target_position:
if current_position > 0:
flip_sequence.append(current_position + 1)
reverse_subarray(arr, current_position)
flip_sequence.append(target_position + 1)
reverse_subarray(arr, target_position)
return flip_sequence
These corrections ensure the algorithm works correctly for all valid input arrays, not just permutations of consecutive integers starting from 1.
Is the following code DFS or BFS?
void search(Node root) { if (!root) return; visit(root); root.visited = true; for (Node node in root.adjacent) { if (!node.visited) { search(node); } } }
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Want a Structured Path to Master System Design Too? Donβt Miss This!