0%

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

题目:

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

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

1
extension Int {
2
    var binaryOneNumber : Int {
3
        var tmp = self
4
        var count = 0
5
        while tmp != 0 {
6
            if tmp & 1 == 1{
7
                count += 1
8
            }
9
            tmp = tmp >> 1
10
        }
11
        return count
12
    }
13
}
14
15
print(0.binaryOneNumber) //输出0
16
print(9.binaryOneNumber) //输出2
17
print((-9).binaryOneNumber)//死循环

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

1
extension Int {
2
    var binaryOneNumber : Int {
3
        var flag = 1
4
        var count = 0
5
        while flag != 0 {
6
            if flag & self > 0{
7
                count += 1
8
            }
9
            flag = flag << 1
10
        }
11
        //如果是负数的话,最高位会统计不上,这里在加1
12
        if self < 0 {
13
            count += 1
14
        }
15
        return count
16
    }
17
}
18
print(0.binaryOneNumber) //输出0
19
print(9.binaryOneNumber) //输出2
20
//这里输出63是正确的,mac os里Int是64位的,在64位里-9的补码是
21
//1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 0111
22
print((-9).binaryOneNumber)//输出63

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

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