Facebook Pixel

1387. Sort Integers by The Power Value

Problem Description

This problem involves calculating the "power" of integers and sorting them based on this power value.

The power of an integer x is defined as the number of steps needed to transform it into 1 using these rules:

  • If x is even: divide it by 2 (x = x / 2)
  • If x is odd: multiply by 3 and add 1 (x = 3 * x + 1)

For example, to find the power of 3:

  • 310 (3 × 3 + 1 = 10)
  • 105 (10 ÷ 2 = 5)
  • 516 (3 × 5 + 1 = 16)
  • 168 (16 ÷ 2 = 8)
  • 84 (8 ÷ 2 = 4)
  • 42 (4 ÷ 2 = 2)
  • 21 (2 ÷ 2 = 1)

This takes 7 steps, so the power of 3 is 7.

Given three integers lo, hi, and k, you need to:

  1. Calculate the power value for all integers in the range [lo, hi] (inclusive)
  2. Sort these integers by their power values in ascending order
  3. If two integers have the same power value, sort them by their actual values in ascending order
  4. Return the k-th integer (1-indexed) from this sorted list

The problem guarantees that all integers in the given range will eventually transform to 1, and their power values will fit in a 32-bit signed integer.

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

Intuition

The problem asks us to find the k-th smallest integer based on a custom ordering - first by power value, then by the integer itself. This naturally leads us to think about sorting.

The key insight is that we need to compute the power value for each integer in the range [lo, hi] and then sort them. Since the power calculation follows a deterministic pattern (divide by 2 if even, multiply by 3 and add 1 if odd), we can implement this as a simple iterative function.

The power calculation process is known as the Collatz sequence. For any given number, we repeatedly apply the transformation rules until we reach 1, counting the steps along the way. Since we might need to calculate the power for the same number multiple times (especially for larger ranges), using memoization with @cache decorator helps avoid redundant calculations.

Once we have a way to calculate the power value, the solution becomes straightforward:

  1. Generate all integers in the range [lo, hi]
  2. Sort them using the power function as the key
  3. Return the element at index k-1 (since k is 1-indexed)

Python's built-in sorted() function handles the tie-breaking automatically - when power values are equal, it maintains the original order of elements, which in our case is already in ascending order since we generate the range sequentially from lo to hi.

The elegance of this approach lies in its simplicity - we let Python's sorting mechanism handle all the complexity of ordering, while we just focus on defining what the "power" of a number means.

Learn more about Memoization, Dynamic Programming and Sorting patterns.

Solution Approach

The implementation consists of two main components: a helper function to calculate the power value and the main solution that performs the sorting.

Step 1: Calculate the Power Value

We define a function f(x) that computes the power of a number x:

@cache
def f(x: int) -> int:
    ans = 0
    while x != 1:
        if x % 2 == 0:
            x //= 2
        else:
            x = 3 * x + 1
        ans += 1
    return ans

The function uses a while loop that continues until x becomes 1. In each iteration:

  • Check if x is even using x % 2 == 0
  • If even, divide by 2 using integer division x //= 2
  • If odd, apply the transformation x = 3 * x + 1
  • Increment the step counter ans

The @cache decorator is crucial here - it memoizes the results, storing previously computed power values. This optimization prevents recalculating the power for numbers that appear in multiple Collatz sequences.

Step 2: Sort and Return the k-th Element

The main solution method is remarkably concise:

def getKth(self, lo: int, hi: int, k: int) -> int:
    return sorted(range(lo, hi + 1), key=f)[k - 1]

Breaking down this one-liner:

  • range(lo, hi + 1) generates all integers from lo to hi inclusive
  • sorted(..., key=f) sorts these integers using the power function f as the sorting key
  • When two numbers have the same power value, Python's stable sort maintains their original order (which is already ascending)
  • [k - 1] retrieves the k-th element (adjusting for 0-based indexing)

The time complexity is O(n log n) for sorting where n = hi - lo + 1, plus the cost of computing power values. The space complexity is O(n) for storing the sorted list, plus additional space for the memoization cache.

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 a small example with lo = 7, hi = 11, and k = 4.

Step 1: Calculate power values for each number in the range [7, 11]

For x = 7:

  • 7 → 22 (odd: 3×7+1=22)
  • 22 → 11 (even: 22÷2=11)
  • 11 → 34 (odd: 3×11+1=34)
  • 34 → 17 (even: 34÷2=17)
  • 17 → 52 (odd: 3×17+1=52)
  • 52 → 26 (even: 52÷2=26)
  • 26 → 13 (even: 26÷2=13)
  • 13 → 40 (odd: 3×13+1=40)
  • 40 → 20 (even: 40÷2=20)
  • 20 → 10 (even: 20÷2=10)
  • 10 → 5 (even: 10÷2=5)
  • 5 → 16 (odd: 3×5+1=16)
  • 16 → 8 (even: 16÷2=8)
  • 8 → 4 (even: 8÷2=4)
  • 4 → 2 (even: 4÷2=2)
  • 2 → 1 (even: 2÷2=1)
  • Power of 7 = 16 steps

For x = 8:

  • 8 → 4 → 2 → 1
  • Power of 8 = 3 steps

For x = 9:

  • 9 → 28 → 14 → 7 → (we already know 7 takes 16 steps) → 1
  • Power of 9 = 19 steps (3 + 16)

For x = 10:

  • 10 → 5 → 16 → 8 → 4 → 2 → 1
  • Power of 10 = 6 steps

For x = 11:

  • 11 → 34 → 17 → 52 → 26 → 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
  • Power of 11 = 14 steps

Step 2: Create pairs of (number, power) and sort

Original list with powers:

  • (7, 16)
  • (8, 3)
  • (9, 19)
  • (10, 6)
  • (11, 14)

Sorted by power value (ascending), then by number value if tied:

  1. (8, 3) - power = 3
  2. (10, 6) - power = 6
  3. (11, 14) - power = 14
  4. (7, 16) - power = 16
  5. (9, 19) - power = 19

Step 3: Return the k-th element

With k = 4, we return the 4th element from the sorted list, which is 7.

The solution leverages Python's sorted() function with a custom key function that computes the power value. The @cache decorator ensures that if we encounter the same number in different Collatz sequences (like how 9's sequence includes 7), we don't recalculate its power value.

Solution Implementation

1from functools import cache
2
3
4@cache
5def calculate_power(x: int) -> int:
6    """
7    Calculate the power value of x using the Collatz conjecture.
8    The power value is the number of steps to reach 1.
9  
10    Rules:
11    - If x is even: x = x / 2
12    - If x is odd: x = 3 * x + 1
13  
14    Args:
15        x: The starting integer
16  
17    Returns:
18        The number of steps required to reach 1
19    """
20    steps = 0
21  
22    # Continue until we reach 1
23    while x != 1:
24        if x % 2 == 0:
25            # Even number: divide by 2
26            x //= 2
27        else:
28            # Odd number: multiply by 3 and add 1
29            x = 3 * x + 1
30        steps += 1
31  
32    return steps
33
34
35class Solution:
36    def getKth(self, lo: int, hi: int, k: int) -> int:
37        """
38        Find the k-th integer in the range [lo, hi] sorted by power value.
39        If two integers have the same power value, sort by the integer value itself.
40      
41        Args:
42            lo: The lower bound of the range (inclusive)
43            hi: The upper bound of the range (inclusive)
44            k: The k-th position to return (1-indexed)
45      
46        Returns:
47            The k-th integer when sorted by power value
48        """
49        # Generate all integers in the range [lo, hi]
50        numbers = list(range(lo, hi + 1))
51      
52        # Sort by power value (and implicitly by the number itself for ties)
53        sorted_numbers = sorted(numbers, key=calculate_power)
54      
55        # Return the k-th element (convert from 1-indexed to 0-indexed)
56        return sorted_numbers[k - 1]
57
1class Solution {
2    /**
3     * Gets the k-th smallest integer in the range [lo, hi] after sorting by power value.
4     * Power value is the number of steps to transform a number to 1 using the Collatz conjecture.
5     * If two numbers have the same power value, they are sorted by their natural order.
6     * 
7     * @param lo The lower bound of the range (inclusive)
8     * @param hi The upper bound of the range (inclusive)
9     * @param k The position of the element to return (1-indexed)
10     * @return The k-th smallest integer based on power value
11     */
12    public int getKth(int lo, int hi, int k) {
13        // Create an array to store all integers in the range [lo, hi]
14        Integer[] numbers = new Integer[hi - lo + 1];
15      
16        // Populate the array with integers from lo to hi
17        for (int i = lo; i <= hi; i++) {
18            numbers[i - lo] = i;
19        }
20      
21        // Sort the array based on power values (primary) and natural order (secondary)
22        Arrays.sort(numbers, (a, b) -> {
23            int powerA = calculatePowerValue(a);
24            int powerB = calculatePowerValue(b);
25          
26            // If power values are equal, sort by natural order
27            // Otherwise, sort by power value
28            return powerA == powerB ? a - b : powerA - powerB;
29        });
30      
31        // Return the k-th element (k is 1-indexed, array is 0-indexed)
32        return numbers[k - 1];
33    }
34  
35    /**
36     * Calculates the power value of a number using the Collatz conjecture.
37     * The power value is the number of steps required to transform the number to 1.
38     * Rules: if n is even, divide by 2; if n is odd, multiply by 3 and add 1.
39     * 
40     * @param x The input number to calculate power value for
41     * @return The power value (number of steps to reach 1)
42     */
43    private int calculatePowerValue(int x) {
44        int steps = 0;
45      
46        // Continue transformation until x becomes 1
47        while (x != 1) {
48            if (x % 2 == 0) {
49                // Even number: divide by 2
50                x /= 2;
51            } else {
52                // Odd number: multiply by 3 and add 1
53                x = x * 3 + 1;
54            }
55            steps++;
56        }
57      
58        return steps;
59    }
60}
61
1class Solution {
2public:
3    int getKth(int lo, int hi, int k) {
4        // Lambda function to calculate the power value (number of steps to reach 1)
5        // using the Collatz conjecture (3n + 1 problem)
6        auto calculatePowerValue = [](int num) {
7            int steps = 0;
8          
9            // Continue until we reach 1
10            while (num != 1) {
11                if (num % 2 == 0) {
12                    // If even, divide by 2
13                    num /= 2;
14                } else {
15                    // If odd, multiply by 3 and add 1
16                    num = 3 * num + 1;
17                }
18                steps++;
19            }
20          
21            return steps;
22        };
23      
24        // Create a vector to store all numbers in the range [lo, hi]
25        vector<int> numbers;
26        for (int i = lo; i <= hi; i++) {
27            numbers.push_back(i);
28        }
29      
30        // Sort the numbers based on their power values
31        // If power values are equal, sort by the number itself (ascending)
32        sort(numbers.begin(), numbers.end(), [&](int a, int b) {
33            int powerA = calculatePowerValue(a);
34            int powerB = calculatePowerValue(b);
35          
36            if (powerA != powerB) {
37                // Sort by power value in ascending order
38                return powerA < powerB;
39            } else {
40                // If power values are equal, sort by number value in ascending order
41                return a < b;
42            }
43        });
44      
45        // Return the k-th element (1-indexed, so we use k-1)
46        return numbers[k - 1];
47    }
48};
49
1/**
2 * Finds the k-th integer in the range [lo, hi] sorted by their "power" value.
3 * The power of an integer is the number of steps to reduce it to 1 using the Collatz conjecture.
4 * If two integers have the same power, they are sorted by their value in ascending order.
5 * 
6 * @param lo - The lower bound of the range (inclusive)
7 * @param hi - The upper bound of the range (inclusive)
8 * @param k - The position (1-indexed) of the element to return after sorting
9 * @returns The k-th integer after sorting by power value
10 */
11function getKth(lo: number, hi: number, k: number): number {
12    /**
13     * Calculates the "power" of a number using the Collatz conjecture.
14     * Power is defined as the number of steps to transform the number to 1:
15     * - If the number is even, divide it by 2
16     * - If the number is odd, multiply by 3 and add 1
17     * 
18     * @param x - The number to calculate power for
19     * @returns The power value (number of steps to reach 1)
20     */
21    const calculatePower = (x: number): number => {
22        let steps = 0;
23      
24        // Continue until we reach 1
25        while (x !== 1) {
26            if (x % 2 === 0) {
27                // Even number: divide by 2 using right shift
28                x >>= 1;
29            } else {
30                // Odd number: multiply by 3 and add 1
31                x = x * 3 + 1;
32            }
33            steps++;
34        }
35      
36        return steps;
37    };
38  
39    // Create an array containing all integers from lo to hi
40    const numbers: number[] = new Array(hi - lo + 1)
41        .fill(0)
42        .map((_, index) => index + lo);
43  
44    // Sort the array by power value, then by the number itself if powers are equal
45    numbers.sort((a: number, b: number) => {
46        const powerA = calculatePower(a);
47        const powerB = calculatePower(b);
48      
49        if (powerA === powerB) {
50            // If powers are equal, sort by value in ascending order
51            return a - b;
52        }
53        // Sort by power in ascending order
54        return powerA - powerB;
55    });
56  
57    // Return the k-th element (k is 1-indexed, so we use k-1)
58    return numbers[k - 1];
59}
60

Time and Space Complexity

Time Complexity: O(n × log n × M)

  • n = hi - lo + 1 is the number of integers in the range [lo, hi]
  • The sorted() function has time complexity O(n × log n) for sorting n elements
  • For each element during sorting, the key function f(x) is called, which takes O(M) time where M is the maximum number of steps to reach 1 in the Collatz sequence
  • Due to the @cache decorator, each unique value's f(x) is computed only once and then memoized
  • The maximum value of M in this problem is bounded at most 178 for the given constraints
  • Total time complexity: O(n × log n × M)

Space Complexity: O(n)

  • The sorted() function creates a new list of size n containing the sorted elements from the range [lo, hi]
  • The @cache decorator stores the results of f(x) for each unique input, which in worst case could be O(n) different values from the range
  • Additional space for the recursion/iteration stack of f(x) is O(1) since it uses a while loop
  • Total space complexity: O(n)

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow During Calculation

The most critical pitfall is that during the power calculation, intermediate values can grow extremely large before eventually converging to 1. For example, starting with 27, the sequence reaches values over 9,000 before coming back down. While Python handles arbitrary precision integers automatically, in languages like Java or C++, this can cause integer overflow.

Problem Example:

# Without proper handling, x = 3 * x + 1 can overflow
# For x = 77671, the sequence reaches 1,570,824,736 before converging

Solution:

  • In Python, this isn't an issue due to unlimited integer precision
  • In other languages, use long long or equivalent 64-bit integers
  • The problem statement guarantees values fit in 32-bit signed integers for the final result, but intermediate calculations may need more space

2. Inefficient Recalculation Without Memoization

Without caching, the same power values get recalculated repeatedly. Many Collatz sequences converge to common paths (like 16→8→4→2→1), causing redundant computations.

Problem Example:

# Without @cache decorator
def calculate_power(x: int) -> int:
    steps = 0
    while x != 1:
        # ... transformation logic
    return steps

# This recalculates power(16), power(8), power(4), etc. many times

Solution:

from functools import cache

@cache  # Critical for performance
def calculate_power(x: int) -> int:
    # ... implementation

3. Off-by-One Error with k-th Element

The problem asks for the k-th element using 1-based indexing, but Python lists use 0-based indexing.

Problem Example:

# Incorrect: forgetting to adjust for 0-based indexing
return sorted_numbers[k]  # This would return the (k+1)-th element

Solution:

# Correct: Convert 1-based k to 0-based index
return sorted_numbers[k - 1]

4. Incorrect Sorting with Multiple Keys

When implementing custom sorting with both power value and the number itself, the secondary sort criterion might be implemented incorrectly.

Problem Example:

# Incorrect: This doesn't handle ties in power values properly
sorted_numbers = sorted(numbers, key=lambda x: calculate_power(x))

Solution: Python's stable sort naturally preserves the original order for equal keys, so the simple version works:

sorted_numbers = sorted(numbers, key=calculate_power)

Or explicitly specify both criteria:

sorted_numbers = sorted(numbers, key=lambda x: (calculate_power(x), x))

5. Recursive Implementation Stack Overflow

A recursive implementation without proper tail recursion optimization can cause stack overflow for numbers with high power values.

Problem Example:

def calculate_power_recursive(x: int) -> int:
    if x == 1:
        return 0
    if x % 2 == 0:
        return 1 + calculate_power_recursive(x // 2)
    else:
        return 1 + calculate_power_recursive(3 * x + 1)
# Can cause RecursionError for large power values

Solution: Use the iterative approach as shown in the original solution, which avoids stack overflow issues entirely.

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

A heap is a ...?


Recommended Readings

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

Load More