【数据结构与算法】程序员面试金典 5.1-5.8

面试题 05.01. 插入

思路:

例 N = 1011 1111, M = 101, i = 2, j = 4
     (1<<(j-i+1))-1)<<i = 0001 1100 
取反后
     1110 0011
对N于运算
     1011 1111 & 1110 0011 = 1010 0011
M左移动i位
    101 << i = 0001 0100
最后M|N
    0001 0100 | 1010 0011 = 1011 0111 = 183
class Solution {
public:
    int insertBits(int N, int M, int i, int j) {
        int mask = ( (1<<(j-i+1)) -1 ) << i;
        mask = ~mask;
        N &= mask;
        return N|(M<<i);
    } 
};

面试题 05.02. 二进制数转字符串

class Solution {
public:
    string printBin(double num) {
        string res = "0.";
        int i = 30;
        while(num > 0 && i--){
            num *= 2;
            if(num >= 1){
                res.push_back('1');
                --num;
            }else{
                res += "0";
            }
        }
        return num ? "ERROR" : res;
    }
};

面试题 05.03. 翻转数位

class Solution {
public:
    int reverseBits(int num) {
        int count = 0, precount = 0;
        int max = 0;
        while(num > 0){
            if(1 & num){ // 最右一位是1
                ++count;
            }else{ // 最右一位是0
                if(count + precount + 1 > max){
                    max = count + precount + 1;
                }
                precount = count;
                count = 0;
            }
            num >>= 1;
        }
        if(count + precount + 1  > max){
            max = count + precount + 1;
        }
        return max;
    }
};

面试题 05.04. 下一个数
非常好理解的一种思路,双100%:
先计算num的1的个数,然后num分别从num-1和num+1开始减小和增加,直到找到第一个1的个数等于num的值就break。

class Solution {
public:
    vector<int> findClosedNumbers(int num) {
        int bigger = -1, smaller = -1;
        int numOne = countOne(num);
        // find bigger
        for(int i=num+1; i<=2147483647; ++i){ // 初始值是num+1
            if(countOne(i) == numOne){
                bigger = i;
                break;
            }
        }
        // find smaller
        for(int i=num-1; i>=0; --i){ // 初始值是num-1
            if(countOne(i) == numOne){
                smaller = i;
                break;
            }
        }
        return {bigger, smaller};
    }

    int countOne(int num){
        int carry = 1;
        int numOne = 0;
        for(int i=0; i<31; ++i){
            if(num & carry){
                ++numOne;
            }
            carry <<= 1;
        }
        return numOne;
    }
};

面试题 05.06. 整数转换
整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B。

class Solution {
public:
    int convertInteger(int A, int B) {
        int count = 0;
        if(A == B){
            return count;
        }

        uint32_t num, a, b; // 务必转换成无符号数才行
        a = A;
        b = B;
        num = A ^ B; // 异或大法好
        
        // 统计1的个数:在不知道多少位的时候,用这个方法靠谱
        while(num){
            num &= num-1; // 去掉最右边的一个1
            ++count;
        }

        // 统计1的个数:如果没有明确说是32位数字,还是不要默认32位,否则出错
        // int carry = 1;
        // for(int i=0;i<31;++i){
        //     if(num & carry){
        //         ++count;
        //     }
        //     carry <<= 1;
        // }

        return count;
    }
};

这个STL的方法也可以:

class Solution {
public:
    int convertInteger(int A, int B) {
        bitset<32> ret(A ^ B);
        return ret.count();
    }
};

面试题 05.07. 配对交换

class Solution {
public:
    int exchangeBits(int num) {
        int odd = num & 0x55555555; // 用于提取奇数位
        int even = num & 0xaaaaaaaa; // 用于提取偶数位
        odd <<= 1;
        even >>= 1;
        return odd | even; 
    }
};

面试题 05.08. 绘制直线

核心思想:定位到开始和结束的int,将这两个和其中间的所有int全部置为-1,然后再把线段两侧位置(两个int)通过位移运算置为0。

由题意得我们需要构建一个vector数组,int数组内形成一长串比特位,既:
/0(共32个0)0/0(共32个0)0/0(共32个0)0/… 共w/32个int,此为一行。
/0(共32个0)0/0(共32个0)0/0(共32个0)0/… 共w/32个int,此为一行。
/0(共32个0)0/0(共32个0)0/0(共32个0)0/… 共w/32个int,此为一行。
/…
/…共 length/每行int数 行。

通过题目后会变成:在某一行上,
/…
/n(n大于等与0)个0, 至少2个1 ,n(n大于等与0)个0/
/…

我们先大体定位到直线两个点所经过的int,将这些int全部置为0xffffffff,即-1;
让后我们通过x1,x2精准定位开始点所在的int左边需要空出几个0,结束点所在的int右边需要空出几个0。
然后通过位移运算把需要的0给空出来。
当x1与x2在同一个int上时,需要在右移时多移动 右边需要空出几个0 的位置,
以便把右边的1挤掉,然后通过左移再移回去,借以实现空出右边0的目的。
位移时应注意对有符号的整数进行右位移时,会以这个整数的符号补最左边的位置,
详情请google逻辑位移与算数位移。

class Solution {
public:
    vector<int> drawLine(int length, int w, int x1, int x2, int y) {
        vector<int> p(length, 0);
        if(length == 0){
            return p;
        }

        int intNum_in_oneRow = w / 32;  // 1行有多少个int
        int start = intNum_in_oneRow * y + x1 / 32;  // 线段头所在的int
        int zeroNum_start = x1 % 32;  // 线段头所在的int的左边需要空出的0的个数
        int end = intNum_in_oneRow * y + x2 / 32; //线段尾所在的int
        int end_zeroNum = 31 - x2 % 32;  // 线段尾所在的int的右边需要空出的0的个数

        for(int i=start; i<=end; ++i){
            p[i] = 0xffffffff; // 设置为-1,反映在二进制数上,就是全是1
        }

        if(start == end){ // 对头尾在一个int中的情况单独处理:当x1与x2在同一个int上时,需要在右移时多移动 右边需要空出几个0 的位置,以便把右边的1挤掉,然后通过左移再移回去,借以实现空出右边0的目的。
            p[start] = (unsigned int) p[start] >> zeroNum_start + end_zeroNum;
            p[end] = (unsigned int) p[end] << end_zeroNum;
        }else{
            p[start] = (unsigned int) p[start] >> zeroNum_start;
            p[end] = (unsigned int) p[end] << end_zeroNum;
        }
        
        return p;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值