剑指offer刷题记录11——二进制中1的个数

题目描述

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

将输入的整数分成0,正,负三种情况分别讨论。正数和0不必多说,当负数时,要将反码的32位用0补全,补码是取反加一,再求1的个数,所以可以不必求补码,直接转化成反码加0,1和0的代数关系进行调换,即1 + 0 = 0, 0 + 0进一位0原位写1,最终求0的个数,代码如下:

解法一:

public class Solution {
    public int NumberOf1(int n) {
        if(n == 0) {
            return 0;
        }
        if(n > 0) {
            return NumberOf1(n / 2) + n % 2;
        }
        int num = 0;
        if(n < 0) {
            if(n == Integer.MIN_VALUE) {
                 return 1;
            }
            n = -n;
            StringBuffer sb = new StringBuffer();
            int flag = 1;
            while(n > 0) {
                sb.append(n % 2);
                n /= 2;
            }
            while(sb.length() < 32) {
                sb.append("0");
            }
            for(char c : sb.toString().toCharArray()) {
                if(c == '0') {
                    if(flag == 0) {
                        num++;
                    }
                    else {
                        flag = 1;
                    }
                }
                else {
                    if(flag == 1) {
                        num++;
                        flag = 0;
                    }
                }
            }
        }
        return num;
    }
}

运行时间:16ms

占用内存:9404k

要记得考虑到当整数位Integer.MIN_VALUE时,其补码为1加上31个0,直接返回1即可。

不过想来这种解法实在繁琐,进行了整数转化为二进制,再进行二进制的计算两步,但是否可以直接用整数的二进制进行位运算呢,于是有了如下的解法。

解法二:递归

public class Solution {
    public int NumberOf1(int n) {
        if(n == 0) {
            return 0;
        }
        return NumberOf1(n & (n - 1)) + 1;
    }
}

在二进制的位面看待这些数,n - 1即是找到最后面的一个1,并将其置0,这个1后面的0全部置1,结果与n相与后刚刚置为1的那些位又重新变成0,即n & (n - 1)是将n的最后一个1找到并置0,其他位置不变,那么这个操作可以递归调用多少次,n的二进制表示就有多少个1。

运行时间:14ms

占用内存:9288k

然而递归的实现是通过调用函数本身,函数调用的时候,每次调用时要做地址保存,参数传递等,这是通过一个递归工作栈实现的。具体是每次调用函数本身要保存的内容包括:局部变量、形参、调用函数地址、返回值。那么,如果递归调用N次,就要分配N*局部变量、N*形参、N*调用函数地址、N*返回值。这势必是影响效率的。

以上文字引用自其他人的博客 。

因为递归使得效率低于普通的循环调用,简单问题能不用递归就不用递归,将解法二进行改进↓

解法二改进:循环

public class Solution {
    public int NumberOf1(int n) {
        int res = 0;
        while(n != 0){
            res++;
            n = n & (n - 1);
         }
        return res;
    }
}

运行时间:12ms

占用内存:9272k

继续利用位运算,题目要求找到有多少个1,那么想到设置一个mask,这个mask的二进制表示只有一个1,且这个1从最低位一直移动到最高位,每次移动之后与n进行与运算,结果不为0的次数就是所求1的个数。

解法三:

public class Solution {
    public int NumberOf1(int n) {
        int res = 0;
        int mask = 1;
        for(int i = 0; i < 32; i++) {
            if((mask & n) != 0) {
                res++;
            }
            mask <<= 1;
        }
        return res;
    }
}

运行时间:12ms

占用内存:9280k

方法总结:

&:位的与运算

<<:左移一位

猜你喜欢

转载自blog.csdn.net/qq_37684824/article/details/82895504