题目
给你一个整数数组 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 ,所以你需要按照数值大小将它们排序。
示例 3:
输入:arr = [10000,10000]
输出:[10000,10000]
示例 4:
输入:arr = [2,3,5,7,11,13,17,19]
输出:[2,3,5,17,7,11,13,19]
示例 5:
输入:arr = [10,100,1000,10000]
输出:[10,100,10000,1000]
提示:
1 <= arr.length <= 500
0 <= arr[i] <= 10^4
思路
统计二进制1
的个数可以分别获取每个二进制位数,然后再统计其1
的个数,此方法效率比较低。这里介绍另外一种高效的方法,同样以 34520
为例,我们计算其 a &= (a-1)
的结果:
第一次:计算前:1000 0110 1101 1000
计算后:1000 0110 1101 0000
第二次:计算前:1000 0110 1101 0000
计算后:1000 0110 1100 0000
第二次:计算前:1000 0110 1100 0000
计算后:1000 0110 1000 0000
我们发现,每计算一次二进制中就少了一个 1
因此可以用此方法求1
的个数,即下面的count_one
函数。
代码一
int count_one(int a){
int cnt=0;
while(a) a=a&(a-1),cnt++;
return cnt;
}
bool cmp(int a,int b){
int numa=count_one(a),numb=count_one(b);
return numa!=numb?numa<numb:a<b;
}
class Solution {
public:
vector<int> sortByBits(vector<int>& arr) {
sort(arr.begin(),arr.end(),cmp);
return arr;
}
};
或者
class Solution {
private:
static int count_one(int a){
int cnt=0;
while(a) a=a&(a-1),cnt++;
return cnt;
}
static bool cmp(int a,int b){
int numa=count_one(a),numb=count_one(b);
return numa!=numb?numa<numb:a<b;
}
public:
vector<int> sortByBits(vector<int>& arr) {
sort(arr.begin(),arr.end(),cmp);
return arr;
}
};
代码二 ,暴力法
package sort_integers_by_the_number_of_1_bits;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Solution1 {
public int[] sortByBits(int[] arr) {
int[] bit = new int[10001];
//对每个十进制的数转二进制的时候统计一下 1 的个数
List<Integer> list = new ArrayList<Integer>();
for (int x : arr) {
list.add(x);
bit[x] = get2(x);
}
Collections.sort(list, new Comparator<Integer>() {
public int compare(Integer x, Integer y) {
if (bit[x] != bit[y]) {
return bit[x] - bit[y];
} else {
return x - y;
}
}
});
//返回结果
for (int i = 0; i < arr.length; ++i) {
arr[i] = list.get(i);
}
return arr;
}
//方法一,统计一下 1 的个数
public int get(int x) {
int res = 0;
print(x);
while (x != 0) {
res += x % 2;
x /= 2;
}
return res;
}
//方法二,统计一下 1 的个数
public int get2(int x) {
int res = 0;
while (x != 0) {
x &= (x - 1);
res++;
}
return res;
}
private void print(int x) {
System.out.println("##### x="+x+" #####");
int res = 0;
while (x != 0) {
int tmp1 = x;
int tmp2 = x % 2;
res += tmp2;
System.out.printf("%5d%s%2d = %d,",tmp1," %",2,tmp2);
System.out.printf("%5d%s%2d = %d\n",tmp1," /",2,(x/=2));
}
System.out.println("res="+res);
}
public static void main(String[] args) {
int[] arr = {0,1,2,3,4,5,6,7,8,13,10000};
int[] originalArr = Arrays.copyOf(arr,arr.length);
int[] resArr = new Solution1().sortByBits(arr);
int[] expectArr = {0,1,2,4,8,3,5,6,7,13,10000};
System.out.println("原数组 "+Arrays.toString(originalArr));
System.out.println("排序后 "+Arrays.toString(resArr));
System.out.println("预期值 "+Arrays.toString(expectArr));
}
}
输出
##### x=0 #####
res=0
##### x=1 #####
1 % 2 = 1, 1 / 2 = 0
res=1
##### x=2 #####
2 % 2 = 0, 2 / 2 = 1
1 % 2 = 1, 1 / 2 = 0
res=1
##### x=3 #####
3 % 2 = 1, 3 / 2 = 1
1 % 2 = 1, 1 / 2 = 0
res=2
##### x=4 #####
4 % 2 = 0, 4 / 2 = 2
2 % 2 = 0, 2 / 2 = 1
1 % 2 = 1, 1 / 2 = 0
res=1
##### x=5 #####
5 % 2 = 1, 5 / 2 = 2
2 % 2 = 0, 2 / 2 = 1
1 % 2 = 1, 1 / 2 = 0
res=2
##### x=6 #####
6 % 2 = 0, 6 / 2 = 3
3 % 2 = 1, 3 / 2 = 1
1 % 2 = 1, 1 / 2 = 0
res=2
##### x=7 #####
7 % 2 = 1, 7 / 2 = 3
3 % 2 = 1, 3 / 2 = 1
1 % 2 = 1, 1 / 2 = 0
res=3
##### x=8 #####
8 % 2 = 0, 8 / 2 = 4
4 % 2 = 0, 4 / 2 = 2
2 % 2 = 0, 2 / 2 = 1
1 % 2 = 1, 1 / 2 = 0
res=1
##### x=13 #####
13 % 2 = 1, 13 / 2 = 6
6 % 2 = 0, 6 / 2 = 3
3 % 2 = 1, 3 / 2 = 1
1 % 2 = 1, 1 / 2 = 0
res=3
##### x=10000 #####
10000 % 2 = 0,10000 / 2 = 5000
5000 % 2 = 0, 5000 / 2 = 2500
2500 % 2 = 0, 2500 / 2 = 1250
1250 % 2 = 0, 1250 / 2 = 625
625 % 2 = 1, 625 / 2 = 312
312 % 2 = 0, 312 / 2 = 156
156 % 2 = 0, 156 / 2 = 78
78 % 2 = 0, 78 / 2 = 39
39 % 2 = 1, 39 / 2 = 19
19 % 2 = 1, 19 / 2 = 9
9 % 2 = 1, 9 / 2 = 4
4 % 2 = 0, 4 / 2 = 2
2 % 2 = 0, 2 / 2 = 1
1 % 2 = 1, 1 / 2 = 0
res=5
原数组 [0, 1, 2, 4, 8, 3, 5, 6, 7, 13, 10000]
排序后 [0, 1, 2, 4, 8, 3, 5, 6, 7, 13, 10000]
代码三
手写快排
class Solution {
public int[] sortByBits(int[] arr) {
int[] bitsCount = new int[arr.length];
//将arr中每个数的bitCount填入数组中
for (int i = 0; i < arr.length; i++) {
int curNum = arr[i];
while (curNum != 0) {
curNum &= curNum - 1;
bitsCount[i]++;
}
}
//快排
quickSort(arr, bitsCount, 0, arr.length - 1);
return arr;
}
private void quickSort(int[] arr, int[] bitsCount, int lo, int hi) {
if (hi <= lo) return;
int mi = partiton(arr, bitsCount, lo, hi);
quickSort(arr, bitsCount, lo, mi - 1);
quickSort(arr, bitsCount, mi + 1, hi);
}
/*
先根据bitCount比较大小,若相等就按照arr中的原值比较大小
*/
private int partiton(int[] arr, int[] bitsCount, int lo, int hi) {
int i = lo, j = hi + 1;
int v = bitsCount[lo];
int c = arr[lo];
while (true) {
while (bitsCount[++i] < v || (bitsCount[i] == v && arr[i] < c)) {
if (i == hi) break;
}
while (bitsCount[--j] > v || (bitsCount[j] == v && arr[j] > c)) {
if (j == lo) break;
}
if (i >= j) break;
swap(arr, i, j);
swap(bitsCount, i, j);
}
swap(arr, lo, j);
swap(bitsCount, lo, j);
return j;
}
private void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
如何计算二进制中1的个数
方法一,取余计算
public int get(int x) {
int res = 0;
print(x);
while (x != 0) {
res += x % 2;
x /= 2;
}
return res;
}
这种方法,结束的条件是商为0,也就是说,一定要除到商为0。
再看一个
方法二,移动法
int bitCount(int n) {
int count = 0; // 计数器
while (n > 0) {
if((n & 1) == 1) count++; // 当前位是1,count++
n >>= 1 ; // n向右移位
}
return count;
}
方法三,与运算消除法
int bitCount(int n) {
int count = 0;
while (n) {
n &= (n - 1); // 清除最低位的1
count++;
}
return count;
}
以计算12的二进制1的数量为例,如图所示:
每次消去最右边的 1,直到消完为止。
来自youngyangyang04
其它,十进制小数转二进制
十进制小数转换成二进制小数采用"乘2取整,顺序排列"法。具体做法是:
用2乘十进制小数,可以得到积,将积的整数部分取出,再用2乘余下的小数部分,又得到一个积,再将积的整数部分取出,如此进行,直到积中的小数部分为零,或者达到所要求的精度为止。然后把取出的整数部分按顺序排列起来,
先取的整数作为二进制小数的高位有效位,后取的整数作为低位有效位。
例一:
图中的B说明这是一个二进制数。
例二: