给定一个正整数,找出一个数:与其二进制表示中1的个数相同,比该数小,而且最接近

#include <iostream>
using namespace std;

/*
input:	正整数n(32位)
return:比n小,最接近n,而且二进制中1的个数与n相同(当然,0的个数也就相同了) 
算法思想:从右往左找到第一个1,他的右边是0,记录下他右边1和0的个数,分别为x和y,
对于返回的结果,这个1左边的位肯定不变,不用考虑,把这个1变成0,这样数字变小,
同时0的个数+1,1的个数-1,为了平衡,要把他右边1的个数+1,0的个数-1,
为了保证在满足数字比n小的情况下最大,1当然靠左,0靠右,
也就是说,紧靠这个1的位置,是x+1个1,最右边y-1个都是0
最终,原来的数字n变成:左边(32-x-y-1)位不变,0,(x+1)个1,(y-1)个0
*/
int getPrev(int n)
{
	int t = n;
	int c0 = 0;
	int c1 = 0;
	while ((t & 1) == 1)
	{
		c1++;
		t >>= 1;
	}
	if (t == 0)//都是1,即111..111,1变0之后1少了
	{
		return -1;
	}
	while ((t & 1) == 0 && t!= 0)
	{
		c0++;
		t >>= 1;
	}
	int p = c0 + c1;
	n &= ((~0) << (p + 1));//把p及其右边清0,第p位变成0,保证数字变小
	int mask = (1 << (c1 + 1)) - 1;//最右边c1+1个1
	n |= mask << (c0 - 1);//把右边这些1左移c0-1位,这样右边c0-1位为0,因为前面把一个1变成了0,所以这里要少一个0,又在小的数里面找最大的,所以1靠左边,0在右边
	return n;
}

int main()
{
	cout << getPrev(20) << endl;//20的二进制10100变成10010,即18
	getchar();
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值