Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1356. Sort Integers by The Number of 1 Bits - Leetcode Solution

Code Implementation

class Solution:
    def sortByBits(self, arr):
        return sorted(arr, key=lambda x: (bin(x).count('1'), x))
      
class Solution {
public:
    vector<int> sortByBits(vector<int>& arr) {
        sort(arr.begin(), arr.end(), [](int a, int b) {
            int countA = __builtin_popcount(a);
            int countB = __builtin_popcount(b);
            if (countA == countB)
                return a < b;
            return countA < countB;
        });
        return arr;
    }
};
      
class Solution {
    public int[] sortByBits(int[] arr) {
        Integer[] nums = new Integer[arr.length];
        for (int i = 0; i < arr.length; i++) {
            nums[i] = arr[i];
        }
        Arrays.sort(nums, (a, b) -> {
            int countA = Integer.bitCount(a);
            int countB = Integer.bitCount(b);
            if (countA == countB) return a - b;
            return countA - countB;
        });
        for (int i = 0; i < arr.length; i++) {
            arr[i] = nums[i];
        }
        return arr;
    }
}
      
var sortByBits = function(arr) {
    return arr.sort((a, b) => {
        const countA = a.toString(2).split('1').length - 1;
        const countB = b.toString(2).split('1').length - 1;
        if (countA === countB) return a - b;
        return countA - countB;
    });
};
      

Problem Description

You are given an array of integers, arr. The task is to sort the array in ascending order, but with a twist: instead of sorting by the integer values directly, you must first sort the numbers by the number of 1 bits in their binary representation (also called their "Hamming weight"). If two numbers have the same number of 1 bits, they should be ordered by their value in ascending order.

Key constraints:
  • Each element in arr is a non-negative integer.
  • There is only one valid sorted output for any input.
  • All elements are used exactly once; do not reuse or omit elements.
Example:
Input: arr = [0,1,2,3,4,5,6,7,8]
Output: [0,1,2,4,8,3,5,6,7]

Thought Process

When faced with this problem, the first instinct might be to simply sort the array. However, the twist is that the sorting criterion is not the value itself, but the count of 1-bits in the binary representation. For example, 3 (binary 11) has two 1-bits, while 4 (binary 100) has only one.

A brute-force approach would be to count the 1-bits for each number, store them alongside the numbers, and then sort based on this count. However, we can optimize by leveraging custom sorting functions and built-in methods for counting 1-bits.

The key realization is that sorting with a custom key—one that reflects both the bit count and the value—makes the solution concise and efficient. This is a classic case for a stable sort with a compound key.

Solution Approach

The solution can be broken down into the following steps:
  1. Count 1-bits: For every integer in arr, compute the number of 1s in its binary representation. This is often called the Hamming weight or population count. Most languages offer a built-in or easy way to do this (e.g., bin(x).count('1') in Python, __builtin_popcount in C++).
  2. Custom Sort: Sort the array using a custom key. The key should be a tuple: first, the bit count; second, the number itself. This ensures that if two numbers have the same bit count, they are sorted by their actual value.
  3. Return the Result: After sorting, return the modified array.
Why use a tuple as a key?
  • The tuple (bit_count, value) ensures the primary sort is by 1-bits, and the secondary sort is by the integer value.
  • This approach is concise, readable, and leverages the language's built-in sorting stability.
Design Choices:
  • We use built-in functions for counting 1-bits for efficiency and clarity.
  • No extra data structures are needed beyond the temporary sort keys.

Example Walkthrough

Let's walk through the sample input arr = [0,1,2,3,4,5,6,7,8] step by step:
  1. Compute the number of 1-bits for each number:
    • 0: 0b0 → 0
    • 1: 0b1 → 1
    • 2: 0b10 → 1
    • 3: 0b11 → 2
    • 4: 0b100 → 1
    • 5: 0b101 → 2
    • 6: 0b110 → 2
    • 7: 0b111 → 3
    • 8: 0b1000 → 1
  2. Group numbers by their 1-bit counts:
    • 0 bits: [0]
    • 1 bit: [1, 2, 4, 8]
    • 2 bits: [3, 5, 6]
    • 3 bits: [7]
  3. Sort within each group by value:
    • [0]
    • [1, 2, 4, 8]
    • [3, 5, 6]
    • [7]
  4. Concatenate the groups: [0, 1, 2, 4, 8, 3, 5, 6, 7]
The process ensures the output is sorted first by bit count, then value.

Time and Space Complexity

Brute-force approach:
  • Counting 1-bits for every number: O(N * B), where N is the number of elements and B is the number of bits in the largest number.
  • Sorting: O(N log N)
  • Total: O(N * B + N log N)
Optimized approach:
  • If the bit-count operation is fast (constant time with hardware intrinsics), the overall complexity is dominated by sorting: O(N log N).
  • Space: O(N) for the sort (depending on implementation; in-place sorts may be O(1) extra).
Why?
  • Each number's bit count is computed once.
  • Sorting with a custom key is efficient and leverages the language's optimized sort algorithms.

Summary

The "Sort Integers by The Number of 1 Bits" problem is a great example of custom sorting with compound keys. By counting the 1-bits for each number and sorting first by this count, then by value, we achieve the required ordering efficiently. The solution elegantly leverages built-in bit-counting and sorting facilities, making it concise, clear, and performant.