剑指Offer算法题-二进制中1的个数

题目:

请实现一个函数,计算一个整数二进制表示中1的个数,例如:把9表示成二进制是1001,有2位是1

方案一

判断该数最后一位是不是1,然后把该数右移一位;这样每次移动一位直到这个数变为0为止。但是该思路有个问题就是该数是负数时,会变成死循环,因为负数的最高位是1,即使右移之后,为了保证该数还是负数,仍会把最高位置为1。

extension Int {
    var binaryOneNumber : Int {
        var tmp = self
        var count = 0
        while tmp != 0 {
            if tmp & 1 == 1{
                count += 1
            }
            tmp = tmp >> 1
        }
        return count
    }
}

print(0.binaryOneNumber) //输出0
print(9.binaryOneNumber) //输出2
print((-9).binaryOneNumber)//死循环

方案二

对方案一会产生死循环的一种改良,把该数首先与1(即为flag)做&运算判断判断最低位是否为1,然后把flag左移一位,判断第二位是否为1,直到flag为0。在4字节的Int类型里也就是移动32次。

extension Int {
    var binaryOneNumber : Int {
        var flag = 1
        var count = 0
        while flag != 0 {
            if flag & self > 0{
                count += 1
            }
            flag = flag << 1
        }
        //如果是负数的话,最高位会统计不上,这里在加1
        if self < 0 {
            count += 1
        }
        return count
    }
}
print(0.binaryOneNumber) //输出0
print(9.binaryOneNumber) //输出2
//这里输出63是正确的,mac os里Int是64位的,在64位里-9的补码是
//1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 0111
print((-9).binaryOneNumber)//输出63

方案三

在二进制中,把一个整数减去1之后再和原来的整数做&运算,得到的结果相当于把该整数的二进制表示中最右边的1变成0。因此可以每次都把n & (n - 1)的结果赋值为n,直到n为0结束。

extension Int {
    var binaryOneNumber : Int {
        var tmp = self
        var count = 0
        while tmp != 0 {
            //这里减一,不能直接用-,有可能会有溢出错误的
            //因为负数的最大值的补码形式是1000 0000...,此时在进行减1,就会有溢出错误
            tmp = tmp & (tmp &- 1)
            count += 1
        }
    
        return count
    }
}
print(0.binaryOneNumber) //输出0
print(9.binaryOneNumber) //输出2
print((-9).binaryOneNumber)//输出63
print((-1).binaryOneNumber)//输出64

你可能感兴趣的:(剑指Offer算法题-二进制中1的个数)