(说明:本博客中的题目、题目详细说明及参考代码均摘自 “何海涛《剑指Offer:名企面试官精讲典型编程题》2012年”)
题目
请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如把 9 表示成二进制是 1001,有 2 位是 1。因此如果输入 9,该函数输出是 2。
算法设计思想
计算一个整数的二进制表示中 1 的个数有多种算法。本文主要介绍两种算法,按位与运算算法和快速算法,更多算法,可以查看网友 zdd 的博文 “算法-求二进制数中1的个数”。
按位与运算算法思想很简单,即对整数的二进制表示的每一位与 1 求与,所得结果为非 0 的个数,即为一个整数二进制表示中 1 的个数,这种算法所需的移位次数至少为整数的二进制表示中,数字 1 所在的最高位的位次(例如 0b0101,高位和低位所在的位次分别为 2 和 0),不够高效;
快速算法,则不采用移位操作,而是用整数 i 与这个整数减 1 的值 i - 1,按位求与,如此可以消除,整数的二进制表示中,最低位的 1 。整数的二进制表示有几个 1,则只需计算几次。在 C/C++ 实现时,负整数溢出后为最大整数,但 Python 数值类型(Numeric Type)不会出现溢出的情况,所以,此时,还需要对边界值进行限定。
注,整数的二进制表示方式和移位操作处理方式:
1)整数的二进制表示方式:在计算机中,整数的二进制表示中,最高位为符号位。最高位为 0 时,表示正数; 最高位为 1 时表示负数。
2)对整数的移位操作:
当整数是负数时,右移时,最低位丢弃,最高位补 1; 左移时,最高位丢弃,最低位补 0。
当整数是正数时,右移时,最低位丢弃,最高位补 0; 左移时,最高位丢弃,最低位补 0 。
C++ 实现
/*
* Author: klchang
* Date: 2017.12.16
* Description: Compute the number of 1 in the binary representation of an integer.
*/
#include <iostream>
#define INT_BITS 32
// Generic method: bitwise and operation with a number that has only one 1 in binary.
int numberOf_1_InBinary_Generic(int i)
{
int count = 0;
int shiftCount = 0;
while (i && shiftCount < INT_BITS)
{
if (i & 1) {
++ count;
}
i = i >> 1;
++ shiftCount;
}
return count;
}
// Fast method: bitwise and operation between integer i and (i-1).
int numberOf_1_InBinary_Fast(int i)
{
int count = 0;
while (i)
{
std::cout << "iter " << count << ": " << i << std::endl;
i = i & (i - 1);
count ++;
}
return count;
}
void unitest()
{
int data[] = {-5, 0, 5};
std::cout << "---------------------- Generic Method -----------------------" << std::endl;
for (int i = 0; i < 3; ++i)
std::cout << "The number of 1 in the binary representation of " << data[i] << " is "
<< numberOf_1_InBinary_Generic(data[i]) << ".
" << std::endl;
std::cout << "----------------------- Fast Method --------------------------" << std::endl;
for (int i = 0; i < 3; ++i)
std::cout << "The number of 1 in the binary representation of " << data[i] << " is "
<< numberOf_1_InBinary_Fast(data[i]) << ".
" << std::endl;
}
int main()
{
unitest();
return 0;
}
Python 实现
原理
为了更好的理解这个问题在 Python 中的实现,先简单介绍 Python 数值类型,需要注意 Python 2 和 Python 3 的数值类型是有些区别的。Python 2 的数值类型有 4 种类型,即 int, long, float 和 complex 。而在 Python 3 中,int 和 long 类型已经整合到一起,成为新的 int 类型,也就是说,Python 3 中,只有 3 种类型,即 int, float 和 complex。 对 bool 类型,在 Python 2 中,其是普通整型 int (at least 32 bits of precision)的子类型;在 Python 3 中,其是 int 类型(unlimited precision)的子类型。
Python 2 的数值类型 int 是普通整型,是有范围的,可以通过 sys.maxint 获取其最大值,至少 32 bit。当 Python 2 程序中的整数值超出范围后,自动转换为 long 类型,而 long 类型是没有范围限制的,即 unlimited precision。在 Python 3 中,这两种类型被统一起来,表示为 int 类型,与 Python 2 的数值类型 long 相同,没有范围限定(unlimited precision)。也就是说,在 Python 中,整型数是没有溢出的(overflow)。在 Python 程序中,当对一个负整数与其减 1 后的值按位求与,若结果为 0 退出,循环执行此过程。由于整型数可以有无限的数值精度,其结果永远不会是 0,如此编程,在 Python 中,只会造成死循环。而在 C/C++ 中,整数(32 bit)的范围是 [ - 2147483648, 2147483647 ],与此相对, Python 2 中的 long 类型和 Python 3 中 int 类型,如果不指定整型数的位数,是没有范围限制的。
注:数值精度(numeric precision)是指数值中的数字位数(the number of digits);数值尺度(numeric scale)是指数值中的小数位数(the number of digits after the decimal point)。例如 123.45 可表示为 decimal(p = 5, s = 2),即 10进制,数值精度为 5, 数值尺度为 2。
实现
#!/usr/bin/python
# -*- coding: utf8 -*-
"""
# Author: klchang
# Date: 2017.12.16
# Description: Compute the number of 1 in the binary representation of an integer.
"""
INT_BITS = 32
MAX_INT = (1 << (INT_BITS - 1)) - 1 # Maximum Integer for INT_BITS
# Generic method: bitwise and operation with a number that has only one 1 in binary.
def number_of_1_in_binary_generic(num):
count, bit = 0, 1
while num and bit <= MAX_INT + 1:
if bit & num:
count += 1
num -= bit
bit = bit << 1
return count
# Fast method: bitwise and operation between integer num and (num-1).
def number_of_1_in_binary_fast(num):
count = 0
while num:
if num < - MAX_INT - 1 or num > MAX_INT:
break
print("iter %d: %d" % (count, num))
count += 1
num = num & (num-1)
return count
def unitest():
nums = [-5, 0, 5]
# Generic Method
print("-" * 30 + " Generic Method " + "-" * 30)
for n in nums:
print("The number of 1 in the binary representation of %d is %d.
" % (n, number_of_1_in_binary_generic(n)))
# Fast Method
print('
' + "-" * 30 + " Fast Method " + "-" * 30)
for n in nums:
print("The number of 1 in the binary representation of %d is %d.
" % (n, number_of_1_in_binary_fast(n)))
if __name__ == '__main__':
unitest()
参考代码
1. targetver.h
#pragma once
// The following macros define the minimum required platform. The minimum required platform
// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
// your application. The macros work by enabling all features available on platform versions up to and
// including the version specified.
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista.
#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
#endif
2. stdafx.h
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
// TODO: reference additional headers your program requires here
3. stdafx.cpp
// stdafx.cpp : source file that includes just the standard includes
// NumberOf1.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file
4. NumberOf1.cpp
// NumberOf1.cpp : Defines the entry point for the console application.
//
// 《剑指Offer——名企面试官精讲典型编程题》代码
// 著作权所有者:何海涛
#include "stdafx.h"
#include <string.h>
#include <stdlib.h>
// ====================方法一====================
int NumberOf1(unsigned int n);
int NumberOf1Between1AndN_Solution1(unsigned int n)
{
int number = 0;
for(unsigned int i = 1; i <= n; ++ i)
number += NumberOf1(i);
return number;
}
int NumberOf1(unsigned int n)
{
int number = 0;
while(n)
{
if(n % 10 == 1)
number ++;
n = n / 10;
}
return number;
}
// ====================方法二====================
int NumberOf1(const char* strN);
int PowerBase10(unsigned int n);
int NumberOf1Between1AndN_Solution2(int n)
{
if(n <= 0)
return 0;
char strN[50];
sprintf(strN, "%d", n);
return NumberOf1(strN);
}
int NumberOf1(const char* strN)
{
if(!strN || *strN < '0' || *strN > '9' || *strN == '