题目:下一个数
给定一个正整数,找出与其二进制表达式中 1 的个数相同且大小最接近的那两个数(一个略大,一个略小)(比该数大且最接近的数 和 比该数小且最接近的数)
一、思路
1.蛮力法:
在n的二进制表示中,数出1的个数,然后增加或减小,直至找到1的个数相同的数字。
2.位操作法:取得后一个较大的数
现在给定一个数n和两个位置i和j,假设将位i从1翻转为0,位j从0翻转成1。若i>j,n就会减小;若i<j,n则会变大。
结论:
- 若将某个0翻转成1,就必须将某个1翻转为0。
- 进行位翻转时,如果0变1的位处于1变0的位的左边,这个数字就会变大。
- 我们想让这个数变大,但又不致太大。因此,必须翻转最右边的0,且它的右边必须有个1。
以13948为例,二进制表示如上。
得到大于13948且最相近的数。
步骤(1):翻转最右边、非拖尾的0
将7号位翻转后,n就会变大,为了尽量缩小数值,需要将p右方清零,把清掉的1的数量减一然后补全。
步骤(2):将p右方的所有位清零,设拖尾0数量为c0=2,p右方0的个数c1=5, p=7
清零,需要创建一个掩码,前面是一连串的1,后面跟着p个0,做法如下。
a= 1 << p; //除位p为1外,其余位均为0
b = a - 1; //前面全位0,后面跟p个1
mask = ~b; //前面全是1,后面跟p个0
n = n & mask; // 将右边p个位清零
简化为n &= ~((1<<p)-1)。
步骤(3):回填c1-1个1
a = 1 << (c1 - 1); // 位c1 - 1为1,其余均为0;
b = a - 1; // 位0到位c1-1的位为1,其余位均位0;
n = n | b; // 在位0到位c1 - 1处插入1;
可简化为n|= (1 << (c1 - 1)) - 1;
3.位操作法:获取前一个较小的数
c1是拖尾1的个数 ,c0为紧邻拖尾1的左方一连串0的个数,p为最右边、非拖尾的1
步骤(1):初始数字,p=7,c1=2,c0=5
步骤(2)和步骤(3):将位0到位p清零
int a = 0xFFFFFFFF; //所有位置1
int b = a << (p+1);// 位p左方的所有位为1,后跟p+1个0
n &= b;// 将位0到位p清零
步骤(4):在紧邻p的右方,插入c1+1个1
int a = 1 << (c1 +