给定一个整数N,找出一个比N大且最接近N,但二进制权值与该整数相同 的数

1,问题描述

给定一个整数N,该整数的二进制权值定义如下:将该整数N转化成二进制表示法,其中 1 的个数即为它的二进制权值。

比如:十进制数1717 的二进制表示为:0000 0110 1011 0101 故它的二进制权值为7(二进制表示中有7个1)

现在要求一个比N大,且最靠近N的数,且这个数的二进制权值与N相同。(这里不考虑Integer.MAX_VALUE 和负数情形。)

对于有符号的32位整数而言:它们的补码如下:

Integer.MAX_VALUE= 0111 1111 1111 1111 1111  1111  1111 1111 (2^32-1)

Integer.MIN_VALUE=  1000 0000 0000 0000 0000 0000 0000 0000 (-2^32)

0 = 0000 0000 0000 0000 0000 0000 0000 0000

-1= 1111 1111 1111 1111 1111 1111 1111 1111

(负数的补码是在原码的基础上,符号位不变,其余位取反,末位加1)参考:原码, 反码, 补码 详解

2,问题分析

思路①

先求出N的二进制权值,然后从N+1开始递增,依次判断这个数的二进制权值是否与N相同,直到找到一个相同的二进制权值的数。

而求解二进制权值的算法可以用移位操作来实现。可参考:JAVA中常用的二进制位操作

//求解正数的二进制表示法中的 1 的位数
    private static int countBit(int num){
        int count = 0;
        for(; num > 0; count++)
        {
            num &= (num - 1);
        }
        return count;
    }

思路①这种方式,当N很大时,效率会很慢。

那有没有更好的思路呢?

其实我们的目的是找到一个数,只要这个数的二进制权值与N相同,且该数大于N且最接近N即可。

那么,可以先将N用二进制表示出来。然后,从低位开始寻找N的二进制中出现 1 之后,第一次出现0的位,假设是第 i 位,那么将第 i 位置为1,得到一个新的数M,此时 M 的二进制中 1 的个数比N多一个。再把M的二进制中的 第 i-1 位的 1 设置为0 ,就得到了大于N且最接近N的二进制权值一样的数。

示例如下:

N= 0010 1001 0101 1111

将第5位置为0,得到了M(最右边的位为第0位)

M= 0010 1001 0111 1111

由于是从低位开始寻找第一次出现0的位。故第5位的右边全是1,再将M的 第 i-1 位(第四位)设置为0,得到了H

H= 0010 1001 0110 1111

H所对应的十进制数,就是题目中要寻找的数。

再比如:

N= 0010 1001 0101 1100

M= 0010 1001 0111 1100

H= 0010 1001 0110 1100

再比如:

N= 0000 1000

M= 0001 1000

H= 0001 0000

3,代码实现:

思路①实现:

import java.util.Scanner;

public class Main{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNextLong())
        {
            int num = sc.nextInt();
            long start = System.currentTimeMillis();
            int weight = countBit(num);
            int k = num + 1;
            while(countBit(k) != weight)
            {
                k++;
            }
            long end = System.currentTimeMillis();
            System.out.println("res:" + k + " time: " + (end - start));
        }
        sc.close();
    }

    private static int countBit(int num){
        int count = 0;
        for(; num > 0; count++)
        {
            num &= (num - 1);
        }
        return count;
    }
}

②思路②实现:

import java.util.Scanner;

public class Larger_Near_Than_N {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNextInt())
        {
            int number = sc.nextInt();

            long start = System.currentTimeMillis();
            int result = findNearThanN(number);
            long end = System.currentTimeMillis();
            System.out.println("res:" + result + " time: " + (end - start));
        }
        sc.close();
    }

    private static int findNearThanN(int number){
        int result = -1;
        int first_indexOf_1 = getFirst_1(number);
        if(first_indexOf_1 != -1)//找到了一个 二进制位 1
        {
            //如果找到了一个二进制位1, indexOf_0不可能为0
            int indexOf_0 = getFirst_0(number, first_indexOf_1);

            int tmp = setBit_1(number, indexOf_0);
            result = setBit_0(tmp, indexOf_0 - 1);
        }
        return result;
    }

    //第 i位为1 返回true,为0 返回false
    private static boolean getBit(int number, int i){
        return ((number & (1 << i)) != 0);
    }

    //从右到左(低位开始)查找number的二进制位 1 的位置
    private static int getFirst_1(int number){
        int index = -1;
        for(int i = 0; i <= 31; i++)
            if(getBit(number, i))
            {
                index = i;
                break;
            }
        return index;//返回二进制位 1 在 number 中的位置
    }

    //从 start+1 位置开始,查找 number的二进制中,第一个出现的0的位置
    private static int getFirst_0(int number, int start){
        int index = -1;
        for(int i = start + 1; i <= 31; i++)
        {
            if(!getBit(number, i))
            {
                index = i;
                break;
            }
        }
        return index;
    }

    //将 number 的二进制表示法中的第 i 位设置为 1
    private static int setBit_1(int number, int i){
        return (number | (1 << i));
    }

    //将 number 的二进制表示法中的第 i 位设置为 0
    private static int setBit_0(int number, int i){
        int mask = ~(1 << i);
        return (number & mask);
    }
}

时间: 2025-01-03 05:51:12

给定一个整数N,找出一个比N大且最接近N,但二进制权值与该整数相同 的数的相关文章

找出一个只出现一次的字符

一,问题描述 给定一个字符串,找出一个 其中只出现一次的字符 如"abaccdeff" 只出现一次的字符有 'b'    'd'     'e' 二,问题分析 ①字符集是个常量 ,字符只有那么多.比如ASCII 一共256个,比如 字母表一共只有26个,再比如数字,一共0-9 只有10个 ②出现一次,说明是次数.将字符映射成出现的次数----Map ③数组就是一种特殊的Map,数组的下标是不变的,相当于Key,下标 i 处存储的值就相当于Value 比如,定义一个存储26个字母出现频率

给定一个整数数组和一个目标值,找出数组中和为目标值的两个数

题目描述: 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数.   你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用.示例:给定 nums = [2, 7, 11, 15], target = 9返回 [0, 1] 思路: 第一层for循环从索引0到倒数第二个索引拿到每个数组元素,第二个for循环遍历上一层for循环拿到的元素的后面的所有元素. 具体代码: 1 public class Solution { 2 public int[] twoSum(int[] nums,

程序员面试100题之十:快速找出一个数组中的两个数字,让这两个数字之和等于一个给定的值(转)

能否快速找出一个数组中的两个数字,让这两个数字之和等于一个给定的值,为了简化起见,我们假设这个数组中肯定存在至少一组符合要求的解. 假如有如下的两个数组,如图所示: 5,6,1,4,7,9,8 给定Sum= 10 1,5,6,7,8,9 给定Sum= 10 分析与解法 这个题目不是很难,也很容易理解.但是要得出高效率的解法,还是需要一番思考的. 解法一 一个直接的解法就是穷举:从数组中任意取出两个数字,计算两者之和是否为给定的数字. 显然其时间复杂度为N(N-1)/2即O(N^2).这个算法很简

9.5位操作(三)——给定一个正整数,找出与其二进制表示中1的个数相同,且大小最接近的那两个数

/** * 功能:给定一个正整数,找出与其二进制表示中1的个数相同,且大小最接近的那两个数. * (一个略大一个略小.) */ 三种方法: 方法一:蛮力法 方法二:位操作法 <span style="white-space:pre"> </span>/** * 方法:位操作法 * 思路:获取后一个较大的数 * 1)计算c0和c1.c1是拖尾1的个数,c0是紧邻拖尾1的作坊一连串0的个数. * 2)将最右边.非拖尾0变为1,其位置为p=c1+c0. * 3)将位p

1152: 零起点学算法59——找出一个数组中出现次数最多的那个元素

1152: 零起点学算法59--找出一个数组中出现次数最多的那个元素 Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: 990  Accepted: 532[Submit][Status][Web Board] Description 找出一个数组中出现次数最多的那个元素 Input 第一行输入一个整数n(不大于20) 第二行输入n个整数 多组数据 Output 找出n个整数中出现次数最多的那个整数(

hdu1298 T9(手机输入法,每按一个数字,找出出现频率最高的字串,字典树+DFS)

Problem Description A while ago it was quite cumbersome to create a message for the Short Message Service (SMS) on a mobile phone. This was because you only have nine keys and the alphabet has more than nine letters, so most characters could only be

算法题:找出一个数组中依次最大的k个元素

package arithmetic; import java.util.Arrays; /** * 找出一个数组中依次最大的k个元素 * @author SHI */ public class FindMaxFigure { public static void main(String[] args) { int[] a=new int[]{1,5,-1,8,0,2}; System.out.println(Arrays.toString(findBigFigure(a, 3))); } /*

给出两个单词(start和end)与一个字典,找出从start到end的最短转换序列

问题 给出两个单词(start和end)与一个字典,找出从start到end的最短转换序列.规则如下: 一次只能改变一个字母 中间单词必须在字典里存在 例如: 给出 start = "hit"end = "cog"dict = ["hot","dot","dog","lot","log"] 返回 [ ["hit","hot",&

找出一个字符串中最长重复次数的子字符串,并计算其重复次数

原题 找出一个字符串中最长重复次数的子字符串,并计算其重复次数.例如:字符串"abc fghi bc kl abcd lkm abcdefg",并返回"abcd"和2. 我的思路 为了方便表述,我们使用变量src作为原字符串,sub_str作为子字符串. 由于题目要求寻找至少重复2次的最长的子字符串,重点在于最长的子字符串,而不在于重复的最多次数.因此我们可以从长度最长的字符串入手,计算其重复次数.只要重复达到2次,即可返回该字符串. 显然长度最长的子字符串就是原串