Cracking the coding interview--Q5.3

题目

原文:

Given an integer, print the next smallest and next largest number that have the same number of 1 bits in their binary representation.

译文:

给一个整数,打印两个数,并且他们的二进制表示中的1的个数和这个整数的一样,其中一个是比该数大的数中最小的,另一个是比该数小的数中最大的。

解答

用简单粗暴的方法就是,直接求出该整数的二进制的1的个数,然后该整数逐减一并判断其二进制的1的个数是否与该整数的相同,相同即可返回比该数小的数中的最大值,同理,该整数逐加一并判断其二进制的1的个数是否与该整数的相同,相同即可返回比该数大的数中的最小值,代码如下:

int next(int x){
    int max_int = ~(1<<31);
    int num = count_one(x);
    if(num == 0 || x == -1) return -1;
    for(++x; count_one(x) != num && x < max_int; ++x);
    if(count_one(x) == num) return x;
    return -1;    
}
int previous(int x){
    int min_int = (1<<31);
    int num = count_one(x);
    if(num == 0 || x == -1) return -1;
    for(--x; count_one(x) != num && x > min_int; --x);
    if(count_one(x) == num) return x;
    return -1; 
}

那如何求整数的二进制中的1的个数呢?通常做法是模2取余累加或不断地移位判断最低位是否为1然后计数器累加的方法:

int count_one(int x){
    int cnt = 0;
    for(int i=0; i<32; ++i){
        if((x & 1)==1) ++cnt;
        x >>= 1;
    }
    return cnt;
}
不过还有一种更高效的方法:位操作的归并算法,即Hamming_weight,http://en.wikipedia.org/wiki/Hamming_weight

public static int hammingWeight(int n){
		n=(n&(0x55555555))+((n>>1)&(0x55555555));
		n=(n&(0x33333333))+((n>>2)&(0x33333333));
		n=(n&(0x0f0f0f0f))+((n>>4)&(0x0f0f0f0f));
		n=(n&(0x00ff00ff))+((n>>8)&(0x00ff00ff));
		n=(n&(0x0000ffff))+((n>>16)&(0x0000ffff));
		return n;
	}

更高效的方案(转自:http://hawstein.com/posts/5.3.html):假设给定的数的二进制表示为: 1101110,我们从低位看起,找到第一个1,从它开始找到第一个0,然后把这个0变为1, 比这个位低的位全置0,得到1110000,这个数比原数大,但比它少了两个1, 直接在低位补上这两个1,得到,1110011,这就是最终答案。 我们可以通过朴素版本来模拟这个答案是怎么得到的:

1101110->1101111->1110000->1110001->1110010->1110011

接下来,我们来考虑一些边界情况,这是最容易被忽略的地方(感谢细心的读者)。 假设一个32位的整数, 它的第31位为1,即:0100..00,那么按照上面的操作,我们会得到1000..00, 很不幸,这是错误的。因为int是有符号的,意味着我们得到了一个负数。 我们想要得到的是一个比0100..00大的数,结果得到一个负数,自然是不对的。 事实上比0100..00大的且1的个数和它一样的整数是不存在的,扩展可知, 对于所有的0111..,都没有比它们大且1的个数和它们一样的整数。对于这种情况, 直接返回-1。-1的所有二进制位全为1,不存在一个数说1的个数和它一样还比它大或小的, 因此适合作为找不到答案时的返回值。

另一个边界情况是什么呢?就是对于形如11100..00的整数,它是一个负数, 比它大且1的个数相同的整数有好多个,最小的当然是把1都放在最低位了:00..0111。

代码如下:

int next1(int x){
    int xx = x, bit = 0;
    for(; (x&1) != 1 && bit < 32; x >>= 1, ++bit);
    for(; (x&1) != 0 && bit < 32; x >>= 1, ++bit);
    if(bit == 31) return -1; //011.., none satisify
    x |= 1;
    x <<= bit; // wtf, x<<32 != 0,so use next line to make x=0
    if(bit == 32) x = 0; // for 11100..00
    int num1 = count_one(xx) - count_one(x);
    int c = 1;
    for(; num1 > 0; x |= c, --num1, c <<= 1);
    return x;
}

int previous1(int x){
    int xx = x, bit = 0;
    for(; (x&1) != 0 && bit < 32; x >>= 1, ++bit);
    for(; (x&1) != 1 && bit < 32; x >>= 1, ++bit);
    if(bit == 31) return -1; //100..11, none satisify
    x -= 1;
    x <<= bit;
    if(bit == 32) x = 0;
    int num1 = count_one(xx) - count_one(x);
    x >>= bit;
    for(; num1 > 0; x = (x<<1) | 1, --num1, --bit);
    for(; bit > 0; x <<= 1, --bit);
    return x;
}

---EOF---
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
牙科就诊管理系统利用当下成熟完善的SSM框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的Mysql数据库进行程序开发。实现了用户在线查看数据。管理员管理病例管理、字典管理、公告管理、药单管理、药品管理、药品收藏管理、药品评价管理、药品订单管理、牙医管理、牙医收藏管理、牙医评价管理、牙医挂号管理、用户管理、管理员管理等功能。牙科就诊管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 管理员在后台主要管理病例管理、字典管理、公告管理、药单管理、药品管理、药品收藏管理、药品评价管理、药品订单管理、牙医管理、牙医收藏管理、牙医评价管理、牙医挂号管理、用户管理、管理员管理等。 牙医列表页面,此页面提供给管理员的功能有:查看牙医、新增牙医、修改牙医、删除牙医等。公告信息管理页面提供的功能操作有:新增公告,修改公告,删除公告操作。公告类型管理页面显示所有公告类型,在此页面既可以让管理员添加新的公告信息类型,也能对已有的公告类型信息执行编辑更新,失效的公告类型信息也能让管理员快速删除。药品管理页面,此页面提供给管理员的功能有:新增药品,修改药品,删除药品。药品类型管理页面,此页面提供给管理员的功能有:新增药品类型,修改药品类型,删除药品类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值