关于书写的规范性

清晰的书写+清晰的布局+合理的命名

1、清晰的书写:在手写代码时先形成清晰的思路并把思路用编程语言清晰的写出来,字迹清晰

2、清晰的布局:合理的缩进、空格、空行(循环块 定义变量块 等)

3、合理的命名:用完整的英文单词组合命名变量和函数,一眼就可以看出变量、函数的用途

代码完整性

1.在编写代码之前考虑单元测试,通常我们可以从功能测试、边界值测试和负面测试三方面设计测试用例,以确保代码完整性

*功能测试:检查代码是否实现基本功能

*边界值测试:输入一些边界值(结束循环的边界条件、递归终止边界)看代码是否符合条件

*负面测试:输入不合法的值看代码运行情况(是否能做出合理的错误处理)

将代码将来的可能的需求全部考虑,在需求变化是时能尽量减少代码改动的风险,使程序可扩展与可维护

位运算

与&0&0=01&0=00&1=01&1=1
或|0|0=01|0=10|1=11|1=1
异或^0^0=01^0=10^1=11^1=0

题目:1

实现一个函数,输入一个整数,输出该数的二进制表示中1的个数,例如9的二进制是1001,有两个1,输入9,输出1

程序1.0

写法1:模除法
int CountOne(int n)
{
	
	int count = 0;
	while (n)
	{
		int ret = 0;
		ret = n % 2;
		if (1 == ret)
			count++;
		n /= 2;
	}
	return count;
}

程序2.0

位运算,由于除法的效率要远远低于位运算,所以在需要用乘除时尽量用位运算代替

而右移运算符又会引入一个新问题,若输入一个负数,则会导致无限循环(右移时左边补符号位)

int CountOne(int n)
{
	int count = 0;
	while (n)
	{
		if ((n & 1) == 1)
		{
			count++;
		}
		n=n >> 1;
	}
	return count;
}

算法2.1

位运算改进,不对输入的数进行移位

int CountOne(int n)
{
	int count = 0;
	size_t flag = 1;
	while (flag)
	{
		if (n&flag)
			count++;
		flag <<= 1;
	}
	return count;
}

算法3.0 

把一个整数减去1,再和原整数做与运算,会把该整数的最右边的一个1变成0,那么一个整数的二进制表示中有多少个1,就可以做多少次这样的操作

int CountOne(int n)
{
	int count = 0;
	while (n)
	{
		++count;
		n = (n - 1)&n;
	}
 	return count;
}

*把一个整数减去1再和原来的整数做位与运算得到的结果相当于把这个整数中表示最右边的一个1变成0

举一反二

  1. 用一条语句判断一个整数是不是2的整数次方,一个整数如果是2的整数次方那么它的二进制表示中有且仅有一位是1,其他位都是0;

分析:这个数减去1再和自己位与,这个整数中唯一的1就会变成0


2. 输入两个整数n和m,计算需要改变m的二进制表示中的多少位才能得到n,比如10的二进制位1010,13的二进制1101,需要1010改变3位才能得到1101,

分析:第一步求这两个数的异或,第二部统计异或结果中1的位数

题目二

    实现函数double Power(double base,int exponen),求base的exponent次方,不得使用库函数,同时不需要考虑大数问题

程序1.0

最简单的写法,但是还是没有考虑效率的问题

double Power(double base, int exponen)
{
	double ret = 1.0;

	double ex = 0.0000000001;//考虑base等于0的时候
	if (base<ex&&base>-ex)
		return 0;

	if (exponen > 0)
	{
		while (exponen > 0)
		{
			ret *= base;
			exponen--;
		}
		return ret;
	}

	if (exponen < 0)
	{
		while (exponen < 0)
		{
			ret *= (1.0 / base);
			exponen++;
		}
		return ret;
	}

	else return 1;
}

程序2.0

采用全局变量来标识是否出错,若出错则返回0,,设置为全局的好处是可以把函数直接传递给其他变量

同时,浮点数在判断是否相等时,采用比较它们的差是否在一个很小的范围内,若两数相差很小则判断相等

bool q_InvalidInput = false;
double PowerWithUnsignedExponent(double base, unsigned int exponent)
{
	double result = 1.0;
	for (int i = 1; i <= exponent; i++)
	{
		result *= base;
	}
	return result;
}
bool equal(double num1, double num2)
{
	if ((num1 - num2 > -0.000000001) && (num1 - num2 < 0.000000001))
		return true;
	else
		return false;
}
double Power(double base, int exponent)
{
	q_InvalidInput = false;

	if (equal(base, 0.0) && exponent < 0)//判断输入的是否是0,若是,则在exponent<0时直接返回0
	{
		q_InvalidInput = true;
		return 0.0;
	}
	
	unsigned int absExponent = (unsigned int)(exponent);
	
	if (exponent < 0)
	{
		absExponent = (unsigned int)(-exponent);//将负数去符号
	}
	double result = PowerWithUnsignedExponent(base, absExponent);
	
	if (exponent < 0)
	{
		result = 1.0 / result;
	}
	return result;
}

程序2.1

用位操作符

在上面函数中用一次次的循环来乘出结果,若要算32的次方,我们就要做31次乘法,如果已经知道16此次,只要在16次方的基础上再平方一次,而16是8的次方……

改写PowerWithUnsignedExponent()

double PowerWithUnsignedExponent(double base, int exponent)
{
	if (exponent == 0)
		return 1;
	if (exponent == 1)
		return base;
	
	double result = PowerWithUnsignedExponent(base, exponent >> 1);//右移1位表示除以2
	
	result *= result;
	
	if (exponent & 0x1 == 1)//用位与代替求余(%),来判断是奇数还是偶数
		result *= base;
	
	return result;
}