力扣-下一个数-位运算

下一个数

题目描述:

下一个数。给定一个正整数,找出与其二进制表达式中1的个数相同且大小最接近的那两个数(一个略大,一个略小)。

示例1:

  • 输入:num = 2(或者0b10)
  • 输出:[4, 1] 或者([0b100, 0b1])

示例2:

  • 输入:num = 1

  • 输出:[2, -1]

提示:num的范围在[1, 2147483647]之间;
如果找不到前一个或者后一个满足条件的正数,那么输出 -1。

思路

  • 首先判断 输入是否符合 [1, 2147483647] 之间
    • 不符合 输出 res={-1,-1}
    • 符合
      • 寻找上一个数
      • 寻找下一个数

关键 如何寻找上下一个相邻的数,并且二进制中1的个数相同

寻找上一个数

n = 13948 举例

image.png

首先我们想要把这个数变大,比如十进制下加一,同时在二进制中1的个数又要保持不变。

给定一个数n和两个位的下标i和j,假设将位i从1翻转为0,位j从0翻转为1。若i>j,n就会减小;若i<j,则n就会变大。

继而得到以下几点,

1)若将某个0翻转为1,就必须将某个1翻转为0。

2)进行位翻转时,如果0变1的位处于1变0的左边,这个数字就会变大。

3)所以必须翻转最右边的0,而且它的右边必须还有个1。

所以我们要翻转的 0 的位置 需要符合 0 前有 一个或者若干连续的 1 ,且在这些 1 前面绝无 1,只能有 0 或者无 0 。

这里我们令c = n 以不改变n的大小。

step 1: 确定 需要翻转0的位置P

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fbSiEtYJ-1645690803959)(D:\Markdown\imge\image-20220224091405924.png)]

  • 首先确定前面 0 的个数 c 0 c_0 c0,同时右移
  • 确定P前面1的个数 c 1 c_1 c1
  • P前面共有 c 1 + c 0 c_1+c_0 c1+c0​个数
  • 这里需要注意 11111000000 这种形式没有比它大的数并且1的个数相同 所以 如果 c 1 + c 0 c_1+c_0 c1+c0==32 结束即可

step 2: 翻转 P

image.png

n = n | (1<<( c 1 + c 0 c_1+c_0 c1+c0​))

step 3:P后面置0

image.png

n = n & (~((1<<(c0+c1))-1));

step 4: n取最后 c 1 − 1 c_1-1 c11位为1

n = n | ((1<<(c1-1))-1);

寻找下一个数

与选择上一个数相似这是寻找 非拖尾1 的位置P

image.png

实现方法与取得较大数非常相似。

1)计算 c 0 c_0 c0 c 1 c_1 c1。注意 c 1 c_1 c1是拖尾1的个数,而c0为紧邻拖尾1的左方一连串0的个数。

2)将最右边、非拖尾1变为0,其位置为p = c1+c0。

3)将位p右边的所有位清零。

4)在紧邻位置p的右方,插入c1+1个1。

Step 1: 确定P位置前 0的个数 c 0 c_0 c0和 1的个数 c 1 c_1 c1

image.png

Step 2:P位置翻转

image.png

step 3: P位置后置1

step 4:取 c 0 − 1 c_0-1 c01置为0

image.png

package 力扣;

/**
 * @author yyq
 * @create 2022-02-24 10:16
 */
public class day01 {
    public static void main(String[] args) {

        findClosedNumbers(2147483647);
    }

    public static int[] findClosedNumbers(int num) {
        int[] res={-1,-1};
        if(num<=0||num>2147483647){
            return res;
        }else {
            res[0] = preNum(num);
            res[1] = nextNum(num);
            System.out.println("较大的数为"+res[0]+",较小的数为"+res[1]);

        }
        return res;
    }

    //  取相邻的较小的数
    private static int nextNum(int n) {
        // 10011 1(P) 00111
        int c0=0;
        int c1=0;
        int c=n;
        //step 1 获取P前 1和0的个数
        while((c&1)==1){
            c1++;
            c=c>>1;
        }
        while((c&1)==0 && c0+c1!=32){
            c0++;
            c=c>>1;
        }
        // 000111111 没有比它小并且有相同个数的1
        int x = c0+c1;
        System.out.println("c0+c1="+x);
        if(c0+c1==32) return -1;


        //step 2 将P置0
        n = n & (~(1<<(c0+c1)));

        //step 3 将P后都置为 1
        n = n | ((1<<(c0+c1))-1);

        //step 4 将最后c0-1 个数置为0
        n = n & (~((1<<(c0-1))-1));

        return n;
    }

    //  取相邻的较大的数
    public static int preNum(int n){
        // 11100 0(P) 1111000
        // step 1 确定P前 0的个数c0和1的个数c1
        int c=n;
        int c1=0;
        int c0=0;
        while((c&1)==0){
            c0++;
            c=c>>1;
        }
        while((c&1)==1 ){
            c1++;
            c=c>>1;
        }

        System.out.println("c0:"+c0);
        System.out.println("c1:"+c1);
        // step 2 这种形式  11111111100000000 没有比它大的并且1的个数相同的数
        if((c0+c1)==31) return -1;

        // step 3 置P为1
        n = n | (1<<(c0+c1));
//        System.out.println(n);
        // step 4 置P后为0
        n = n & (~((1<<(c0+c1))-1));
//        System.out.println(n);
        // step 5 取P后c1-1位为1
        n = n | ((1<<(c1-1))-1);
        return n;
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值