Next Number:输入一个正整数num
,求和num
的二进制表示中1
的数量相同的最接近的两个数。
为了让num
变大,要保证将左边的一个0
翻转为1
,并将右边的1
翻转为0
。因为只让num
变大一点点,所以应该翻转最右边的0
,同时还要保证0
的右边有1
,所以应该翻转的是第2
个0
序列最右边的0
。使用c0
保存第1
个0
序列的长度,使用c1
保存第1
个1
序列的长度,则应该将第c0 + c1
位翻转为1
,同时将c1 - 1
个1
放在末尾。当第1
个1
序列向左延伸到第31
位时,不存在大一点的数。
为了让num
变小,要保证将左边的一个1
翻转为0
,并将右边的0
翻转为1
。因为只让num
变小一点点,所以应该翻转最右边的1
,同时还要保证1
的右边有0
,所以应该翻转的是第2
个1
序列最右边的1
。使用c1
保存第1
个1
序列的长度,使用c0
保存第1
个0
序列的长度,则应该将第c1 + c0
位翻转为0
,同时将c1 + 1
个1
放在该位后。当第1
个0
序列向左延伸到第32
位时,不存在小一点的数。
class Solution {
public:
vector<int> findClosedNumbers(int num) {
int next = NextCloset(num);
int prev = PrevCloset(num);
return { next, prev };
}
private:
int NextCloset(int num)
{
int n = num;
int c0 = 0, c1 = 0;
while((n & 0x1) == 0){
c0++;
n >>= 1;
}
while((n & 0x1) == 1){
c1++;
n >>= 1;
}
if(c1 + c0 == 31) return -1;
int mask = 1 << (c0 + c1);
num |= mask;
num &= ~(mask - 1);
num |= (1 << (c1 - 1)) - 1;
return num;
}
int PrevCloset(int num)
{
int n = num;
int c0 = 0, c1 = 0;
while((n & 0x1) == 1){
c1++;
n >>= 1;
}
if(n == 0) return -1;
while((n & 0x1) == 0){
c0++;
n >>= 1;
}
int mask = 1 << (c0 + c1 + 1);
num &= ~(mask - 1);
mask = ((1 << (c1 + 1)) - 1) << (c0 - 1);
num |= mask;
return num;
}
};
更巧妙的是使用算术计算的方法。在NextCloset()
中,通过+ 2 ^ c0 - 1
实现0
序列转1
序列,再+ 1
实现c1 + c0
位和1
序列翻转,最后再+ 2 ^ (c1 - 1) - 1
实现末尾转1
序列。在PrevCloset()
中,通过- (2 ^ c1 - 1)
实现1
序列转0
序列,再- 1
实现c0 + c1
位和0
序列翻转,最后再- (2 ^ (c0 - 1) - 1)
实现末尾转0
序列。
class Solution {
public:
vector<int> findClosedNumbers(int num) {
int next = NextCloset(num);
int prev = PrevCloset(num);
return { next, prev };
}
private:
int NextCloset(int num)
{
int n = num;
int c0 = 0, c1 = 0;
while((n & 0x1) == 0){
c0++;
n >>= 1;
}
while((n & 0x1) == 1){
c1++;
n >>= 1;
}
if(c1 + c0 == 31) return -1;
return num + (1 << c0) + (1 << (c1 - 1)) - 1;
}
int PrevCloset(int num)
{
int n = num;
int c0 = 0, c1 = 0;
while((n & 0x1) == 1){
c1++;
n >>= 1;
}
if(n == 0) return -1;
while((n & 0x1) == 0){
c0++;
n >>= 1;
}
return num - (1 << c1) - (1 << (c0 - 1)) + 1;
}
};