leetcode.1356. 根据数字二进制下 1 的数目排序——如何计算二进制中1的个数

题目

给你一个整数数组 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;
}

来自youngyangyang04

方法三,与运算消除法

int bitCount(int n) {
    int count = 0;
    while (n) {
        n &= (n - 1); // 清除最低位的1
        count++;
    }
    return count;
}

以计算12的二进制1的数量为例,如图所示:

在这里插入图片描述
每次消去最右边的 1,直到消完为止。
来自youngyangyang04

其它,十进制小数转二进制

十进制小数转换成二进制小数采用"乘2取整,顺序排列"法。具体做法是:

用2乘十进制小数,可以得到积,将积的整数部分取出,再用2乘余下的小数部分,又得到一个积,再将积的整数部分取出,如此进行,直到积中的小数部分为零,或者达到所要求的精度为止。然后把取出的整数部分按顺序排列起来,

先取的整数作为二进制小数的高位有效位,后取的整数作为低位有效位。

例一
在这里插入图片描述
图中的B说明这是一个二进制数。

例二
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值