Facebook Pixel

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 where 1 <= 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:

  1. Find where the current largest element is located
  2. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. First, bring the element to the front of the array (position 0)
  2. 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 and j
  • Swap elements at positions i and j
  • Move i forward and j backward until they meet

Main Algorithm: The algorithm processes elements from largest to smallest:

  1. Outer Loop: Iterate i from n-1 down to 1

    • At each iteration, we're trying to place element with value i+1 at position i
  2. Find Current Position:

    j = i
    while j > 0 and arr[j] != i + 1:
        j -= 1

    This finds the current position j of element i+1

  3. 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 element i+1 to the front
    • Second Flip:
      • Add i + 1 to the answer list
      • Call reverse(arr, i) to place element i+1 at position i

Example Walkthrough with arr = [3,2,1,4]:

  • i = 3: Looking for element 4 at position 3

    • Found at position 3 (already correct), no flips needed
  • i = 2: Looking for element 3 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]
  • i = 1: Looking for element 2 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 Evaluator

Example 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)

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)

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 to 1, executing n-1 iterations
  • For each iteration i:
    • The inner while loop searches for element i+1, which takes O(i) time in the worst case
    • The reverse function is called at most twice, each taking O(i) time
  • 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 uses O(1) additional space
  • The answer list ans stores the flip operations, which in the worst case can be O(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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More