(说明:本博客中的题目、题目详细说明及参考代码均摘自 “何海涛《剑指Offer:名企面试官精讲典型编程题》2012年”)html
请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如把 9 表示成二进制是 1001,有 2 位是 1。所以若是输入 9,该函数输出是 2。python
计算一个整数的二进制表示中 1 的个数有多种算法。本文主要介绍两种算法,按位与运算算法和快速算法,更多算法,能够查看网友 zdd 的博文 “算法-求二进制数中1的个数”。ios
按位与运算算法思想很简单,即对整数的二进制表示的每一位与 1 求与,所得结果为非 0 的个数,即为一个整数二进制表示中 1 的个数,这种算法所需的移位次数至少为整数的二进制表示中,数字 1 所在的最高位的位次(例如 0b0101,高位和低位所在的位次分别为 2 和 0),不够高效;git
快速算法,则不采用移位操做,而是用整数 i 与这个整数减 1 的值 i - 1,按位求与,如此能够消除,整数的二进制表示中,最低位的 1 。整数的二进制表示有几个 1,则只需计算几回。在 C/C++ 实现时,负整数溢出后为最大整数,但 Python 数值类型(Numeric Type)不会出现溢出的状况,因此,此时,还须要对边界值进行限定。面试
注,整数的二进制表示方式和移位操做处理方式:
1)整数的二进制表示方式:在计算机中,整数的二进制表示中,最高位为符号位。最高位为 0 时,表示正数; 最高位为 1 时表示负数。
2)对整数的移位操做:
当整数是负数时,右移时,最低位丢弃,最高位补 1; 左移时,最高位丢弃,最低位补 0。
当整数是正数时,右移时,最低位丢弃,最高位补 0; 左移时,最高位丢弃,最低位补 0 。算法
/* * 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]) << ".\n" << 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]) << ".\n" << std::endl; } int main() { unitest(); return 0; }
为了更好的理解这个问题在 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 类型,若是不指定整型数的位数,是没有范围限制的。app
注:数值精度(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" % (n, number_of_1_in_binary_generic(n))) # Fast Method
print('\n' + "-" * 30 + " Fast Method " + "-" * 30) for n in nums: print("The number of 1 in the binary representation of %d is %d.\n" % (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 == '\0') return 0; int first = *strN - '0'; unsigned int length = static_cast<unsigned int>(strlen(strN)); if(length == 1 && first == 0) return 0; if(length == 1 && first > 0) return 1; // 假设strN是"21345" // numFirstDigit是数字10000-19999的第一个位中1的数目
int numFirstDigit = 0; if(first > 1) numFirstDigit = PowerBase10(length - 1); else if(first == 1) numFirstDigit = atoi(strN + 1) + 1; // numOtherDigits是01346-21345除了第一位以外的数位中1的数目
int numOtherDigits = first * (length - 1) * PowerBase10(length - 2); // numRecursive是1-1345中1的数目
int numRecursive = NumberOf1(strN + 1); return numFirstDigit + numOtherDigits + numRecursive; } int PowerBase10(unsigned int n) { int result = 1; for(unsigned int i = 0; i < n; ++ i) result *= 10; return result; } // ====================测试代码====================
void Test(char* testName, int n, int expected) { if(testName != NULL) printf("%s begins: \n", testName); if(NumberOf1Between1AndN_Solution1(n) == expected) printf("Solution1 passed.\n"); else printf("Solution1 failed.\n"); if(NumberOf1Between1AndN_Solution2(n) == expected) printf("Solution2 passed.\n"); else printf("Solution2 failed.\n"); printf("\n"); } void Test() { Test("Test1", 1, 1); Test("Test2", 5, 1); Test("Test3", 10, 2); Test("Test4", 55, 16); Test("Test5", 99, 20); Test("Test6", 10000, 4001); Test("Test7", 21345, 18821); Test("Test8", 0, 0); } int _tmain(int argc, _TCHAR* argv[]) { Test(); return 0; }
5. 参考代码下载
项目 10_NumberOf1 下载: 百度网盘
何海涛《剑指Offer:名企面试官精讲典型编程题》 全部参考代码下载:百度网盘
[1] 何海涛. 剑指 Offer:名企面试官精讲典型编程题 [M]. 北京:电子工业出版社,2012. 77-82.
[2] Python Software Foundation. Python 2.7.14 Documentation, The Python Standard Library, 5.4. Numeric Types — int, float, long, complex [OL]. https://docs.python.org/2/library/stdtypes.html#numeric-types-int-float-long-complex. 2017.
[3] Python Software Foundation. Python 3.6.4rc1 Documentation, The Python Standard Library, 4.4. Numeric Types — int, float, complex [OL]. https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex. 2017.
[4] Stack Overflow Users. How do I interpret precision and scale of a number in a database [OL]. https://stackoverflow.com/questions/2377174/how-do-i-interpret-precision-and-scale-of-a-number-in-a-database.