程序员面试金典 5.4

Next Number:输入一个正整数num,求和num的二进制表示中1的数量相同的最接近的两个数。

为了让num变大,要保证将左边的一个0翻转为1,并将右边的1翻转为0。因为只让num变大一点点,所以应该翻转最右边的0,同时还要保证0的右边有1,所以应该翻转的是第20序列最右边的0。使用c0保存第10序列的长度,使用c1保存第11序列的长度,则应该将第c0 + c1位翻转为1,同时将c1 - 11放在末尾。当第11序列向左延伸到第31位时,不存在大一点的数。

为了让num变小,要保证将左边的一个1翻转为0,并将右边的0翻转为1。因为只让num变小一点点,所以应该翻转最右边的1,同时还要保证1的右边有0,所以应该翻转的是第21序列最右边的1。使用c1保存第11序列的长度,使用c0保存第10序列的长度,则应该将第c1 + c0位翻转为0,同时将c1 + 11放在该位后。当第10序列向左延伸到第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;
    }
};
发布了194 篇原创文章 · 获赞 2 · 访问量 7725

猜你喜欢

转载自blog.csdn.net/RayoNicks/article/details/105400372
今日推荐