程序员面试金典: 9.5位操作 5.3给定正整数,找出与其二进制表示中1的个数相同、且大小最接近的两个数

来源:互联网 发布:depthmap软件 编辑:程序博客网 时间:2024/05/16 07:34
#include <iostream>#include <stdio.h>#include <string>#include <vector>#include <stack>#include <sstream>using namespace std;/*问题:给定一个正整数,找出与其二进制表示中1的个数相同、且大小最接近的那两个数(一个略大,一个略小)输入:111001111001110010111输出11011110, 111010111110(略小的数) ,10101(略大的数)10001 , 10100No Less Number , 1011 关键部分:1 位运算中关键技巧:  技巧1:以第i位将一个数拆num分上高位部分和低位部分,然后高位部分和低位部分分别进行操作,最后将操作后的高位部分和         低位部分进行或运算,即可得到最终运算结果 低位第i位以下部分全为1的掩模lowMask= 1 << i - 1; 高位第i位及以上部分全为1的掩模highMask = ~( (1 << i) - 1 ) 低位lowNum = num & lowMask 高位highNum = num & highMask  技巧2:将数num的第i位变成0:                              highMask = ~( 1 << (i + 1) - 1 );注意高位部分的掩模不含第i位本身,则第i位变成0                              lowMask= 1 << i - 1; 然后利用技巧1获取高位数和低位数部分进行处理  int lowNumber = num & lowMask;  int highNumber = num & highMask;  int result = highNumber | lowNumber;         将数num的第i位变成1:num != (1 << position); 【另第i位变成1】,变为1只需要与2^i进行或运算 将数num的第i位之后全变成0:只需要与一个最后i位都为0,前面都为1的数做与运算                           mask = ( (~0) << i ) 将数num前面n位设置为1,后面m位设置为0:                       先生成n个1,然后向左移动m位   mask = (1 << n) -1;   mask <<= m;   num |= mask;  技巧3:寻找最低位的0:将数num和1做相与运算,如果相与结果为0,则当前位即为所求;否则令num >>= 1,即令数向右移动,之所以不用num与2^i运算,会溢出,                        且无法判定何时停止运算         寻找最低位的1:                                 不为0  技巧4:(n & (n-1) ) == 0判断一个整数是否为2的某次方         需要n个1,就 (1 << n) - 1         全是1的数= ~0         判断是否为全是1的数: 将num和1做与运算,如果相与结果为0,肯定不是;否则,令num >> = 1,                       如果num变成0了,则必定为全1(会溢出,不能用该方法:将该数加1后与2^i比较,当2^i > 该数时,则必定不是全为1)2 寻找略小的数考虑到本质上是将一个1变成0,将一个0变成1.变小,所以改变的1的位置比0的位置要大,所以寻找的应该是类似这种"10"3 寻找略大的数那么要改变的0的位置要在1的前面,应该是寻找这种"01",寻找到这样的0的位置P:使其右边一位为1,该位置P最小。统计P右侧的0的个数为C0,1的个数为C1。将P本身变为1,并将P右侧全部清零,P右侧应该存放C1 - 1 个1,并需要将这些1放在离P最远的位置。例如: 1001110, P = 4,C0=1,C1=3, 将位置P从1变成0,并将P右侧清零得到   :1010000将C1+1个1放在P右侧离P最远的的地方得到:1010011*///需要将字符串转化为二进制对应的十进制整数int toTenSystem(string& num){if(num.empty()){return -1;}int size = num.size();int value;int total = 0;int count = 0;for(int i = size - 1 ; i >= 0 ; i--){value = num[i] - '0';total += value * ( (int) pow(2 , count) );count++;}return total;}//判断一个数是不是全为1,比如:111,1111,就返回true: 该数+1后应该等于2^i ,如果不等,继续增加i,如果2^i已经超过整数最大值bool isAllOnes(int num){while( ( (num & 1) == 1 ) && ( num != 0 )  ){num >>= 1;}if(num == 0){return true;}else{return false;}}typedef struct Result{Result(bool isResult , int result){_isResult = isResult;_result = result;}bool _isResult;int _result;}Result;/*统计一个十进制数转换为2进制数中1的个数,统计方法采用:将该数不断与2^i,进行相与处理,如果相与后结果不为0,表示当前位为1如果2^i超过最大整数或小于最小整数,则统计停止*/int count1(int num){int count = 0;//采用num & 1比较, num >>= 1的方法做,不会溢出while( num != 0 ){if( (1 & num) == 1 ){count++;}num >>= 1;}return count;}//统计一个整数中0的个数int count0(int num){int count = 0;while( num != 0){if( ( num & 1)  == 0){count++;}num >>= 1;}return count;}//将十进制整数转换为二进制整数,打印出来string toBinarySystem(int num){int total = 0;int count = 0;stack<int> stackResult;do{int value = num % 2;stackResult.push(value);total += value * ( int( pow(2, count) ) );num /= 2;}while(num);stringstream ss;while(!stackResult.empty()){int value = stackResult.top();ss << value ;stackResult.pop();}return ss.str();}//找到类似“01”这种位置,用于找到略大的数int find01Position(int num){int value = num;int pos = 0;//必须先找到1while( value && ( (value & 1) == 0 ) ){pos++;value >>= 1;}//说明这个数中没有1,直接返回-1if(value == 0){return -1;}//接下来找相邻的0//pos--;//下面会多计算一次,因此位置要减一while( value && (value & 1) == 1 ){pos++;value >>= 1;}if(value == 0){return -1;}return pos;}//找出类似"10"的位置,用于寻找略小的数int find10Position(int num){//先找到0int value = num;int pos = 0;while( value && ( (value & 1) == 1 ) ){pos++;value >>= 1;}if(0 == value){return -1;}//寻找1while( value && ( (value & 1) == 0 ) ){pos++;value >>= 1;}if(0 == value){return -1;}return pos;}Result getLessNumber(int num){int pos = find10Position(num);if(-1 == pos){return Result(false , -1);}//统计位置P右侧1的个数int rightValue = num & ( ( 1 << pos) - 1 );int countOne = count1(rightValue);int countZero = pos - countOne;//0的位置不需要统计,直接拿P-countOne//将位置P的1变成0,并将P右侧全部变成0int mask = ~( (1 << (pos + 1) ) -1 );num &= mask;//将P右侧离P最近的 C1+1个位置全部设置为1 , C1-1个0,组成,总位数就是P,可以先生成C1+1个1mask = ( 1 << (countOne + 1) ) - 1;//将这个C1+1个1向左移动C0-1位,使其包含C0-1个0mask <<= countZero -1;num |= mask;return Result(true , num);}/*那么要改变的0的位置要在1的前面,应该是寻找这种"01",寻找到这样的0的位置P:使其右边一位为1,该位置P最小。统计P右侧的0的个数为C0,1的个数为C1。将P本身变为1,并将P右侧全部清零,P右侧应该存放C1 - 1 个1,并需要将这些1放在离P最远的位置。例如: 1001110, P = 4,C0=1,C1=3, 将位置P从1变成0,并将P右侧清零得到   :1010000将C1+1个1放在P右侧离P最远的的地方得到:1010011*/Result getGreaterNumber(int num){int pos = find01Position(num);if(-1 == pos){return Result(false , -1);}//计算P位置右侧的1的个数int rightValue = num & ( (1 << pos) - 1);int countOne = count1( rightValue );//找到位置P,将P位置本身变为1num |= (1 << pos);//将P位置右侧全部清零,不含P位置int mask = ~( (1<<pos) - 1);num &= mask;//从P右侧,离P最远的C1 - 1个0全部变成1,记住:需要n个1,就 (1 << n) - 1num |= ( (1 << countOne - 1) - 1  );return Result(true , num);}void process(){string sNum;int num;while(cin >> sNum){num = toTenSystem(sNum);//转化为十进制整数后,下面就是移位Result lessResult = getLessNumber(num);Result greaterResult = getGreaterNumber(num);if(lessResult._isResult){cout << toBinarySystem(lessResult._result) << " ,";}else{cout << "No Less Number ," ;}if(greaterResult._isResult){cout << toBinarySystem(greaterResult._result) << endl;}else{cout << "No Greater Number" << endl; }}}int main(int argc, char* argv[]){process();getchar();return 0;}

0 0
原创粉丝点击