Easy
题目描述
给你一个整数数组 arr。请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序。
如果存在多个数字二进制中 1 的数目相同,则必须将它们按照数值大小升序排列。
请你返回排序后的数组。
示例 1:
输入:arr = [0,1,2,3,4,5,6,7,8]
输出:[0,1,2,4,8,3,5,6,7]
解释:[0] 是唯一一个有 0 个 1 的数。
[1,2,4,8] 都有 1 个 1 。
[3,5,6] 有 2 个 1 。
[7] 有 3 个 1 。
按照 1 的个数排序得到的结果数组为 [0,1,2,4,8,3,5,6,7]
示例 2:
输入:arr = [1024,512,256,128,64,32,16,8,4,2,1]
输出:[1,2,4,8,16,32,64,128,256,512,1024]
解释:所有整数二进制下都只有 1 个 1 ,所以你需要按照数值大小将它们排序。
约束条件:
1 <= arr.length <= 5000 <= arr[i] <= 10^4
解题思路
这道题的核心是按照两个条件进行排序:
- 首先按照数字二进制表示中1的个数升序排列
- 如果1的个数相同,则按照数值大小升序排列
解决思路:
方法一:自定义排序(推荐)
- 使用内置的排序函数,自定义比较器
- 比较器逻辑:先比较二进制中1的个数,相同则比较数值大小
- 计算二进制中1的个数可以使用位运算或内置函数
方法二:预处理+排序
- 先遍历数组,计算每个数字的1的个数
- 将(数值, 1的个数)配对存储
- 按照规则排序后提取数值
对于计算二进制中1的个数,有多种方法:
- 使用
__builtin_popcount()(C++)、bin(x).count('1')(Python) 等内置函数 - 使用位运算:
n & (n-1)可以消除最低位的1 - 逐位检查:不断右移并检查最低位
时间复杂度主要由排序决定,为O(n log n)。
代码实现
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:
def sortByBits(self, arr: List[int]) -> List[int]:
return sorted(arr, key=lambda x: (bin(x).count('1'), x))
public class Solution {
public int[] SortByBits(int[] arr) {
Array.Sort(arr, (a, b) => {
int countA = CountBits(a);
int countB = CountBits(b);
if (countA == countB) {
return a.CompareTo(b);
}
return countA.CompareTo(countB);
});
return arr;
}
private int CountBits(int n) {
int count = 0;
while (n > 0) {
count += n & 1;
n >>= 1;
}
return count;
}
}
var sortByBits = function(arr) {
const countBits = (n) => {
let count = 0;
while (n > 0) {
count += n & 1;
n >>= 1;
}
return count;
};
return arr.sort((a, b) => {
const bitsA = countBits(a);
const bitsB = countBits(b);
return bitsA === bitsB ? a - b : bitsA - bitsB;
});
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n log n) | 排序的时间复杂度,其中计算每个数字的1的个数为O(log k),k为数字的最大值 |
| 空间复杂度 | O(1) | 原地排序,只使用常数额外空间(不考虑排序算法本身的栈空间) |