【剑指offer】——实现特定的库函数pow、atoi

在面试考题里面经常出现处理数值和字符串的函数,这篇文章,我们主要来讲解一个pow和atoi函数的实现~

一、pow函数

题目要求

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

题目分析

因为不需要考虑大数问题,首先一拿到这个问题,我们在脑子里面蹦出来的想法就是如下这样实现:

double Power(double base, int exponent)
{
	double result = 0;
	for (int i = 0; i <= exponent; i++)
	{
		result *= base;
	}
	return result;
}

但是这样书写肯定是不对的。这里面存在许多不足

改进一:考虑底数是0且指数是负数的情况
由于有这样一个特殊情况,所以我们要考虑错误处理的方式。
在我们所编写的程序中,通常有三种错误处理的方式,下面展示了他们对应的优缺点
在这里插入图片描述
做出的改进代码如下:

bool g_InvalidInput = false;

double Power(double base, int exponent)
{
	g_InvalidInput = false;

	if (equal(base, 0.0) && exponent < 0)
	{
		g_InvalidInput = true;
		return 0.0;
	}

	unsigned int absExponent = (unsigned int)(exponent);
	if(exponent < 0)
		absExponent = (unsigned int)(-exponent);

	double result = Powerresult(base, absExponent);
	if (exponent < 0)
		result = 1.0 / result;
	return result;
}		

在我们上述的代码中,我们采用全局变量来标识是否出错。如果出错了则返回的值是0。但是具体的为了区分是出错的时候返回的0还是底数为0的时候正常运行返回为0,我们设计了g_InvalidInput全局变量来区分。当出错时,这个变量被设为true,否则为false。

改进二:更加高效的Powerresult方法
如果我们输入的指数exponent为32时,在我们之前的循环里面,我们要做31次循环才可,但是我们仔细想一想似乎还有更简洁的方法。
在这儿我们就要提到在斐波拉契数列中我们提到的一种解法。在求斐波拉契数列里面有一种使用数学公式的方法其时间复杂度是O(logn)其考虑乘方的数学公式如下:
在这里插入图片描述
从上面的公式我们可以看出,我们想求改进方法得n次方,就要先得到n/2次方,再把n/2次方的结果平方一下即可。这可以用递归的思路实现。
有了斐波拉契数列的铺垫,我们就更容易思考出该函数的了。因为我们要求的是32次方,由此来计算16次方、8次方、4次方、平方。由此衍生的数学公式如下:
在这里插入图片描述
基于上述公式,代码实现如下:

double Powerresult(double base, unsigned int exponent)
{
	if (exponent == 0)
		return 1;
	if (exponent == 1)
		return base;

	//递归乘法的过程
	double result = Powerresult(base, exponent >> 1);
	result *= result;

	//指数为奇数的情况
	if ((exponent & 0x1) == 1)
		result *= base;

	return result;
}

二、atoi函数

题目要求

把字符串转换成整数,比如说输入字符串“123”,他的输出是数字123

题目分析

首先拿到这道题,根据题目要求,我们自然就只联想到了把“123”转成数字123的情况。但是这道题不止这么简单只考虑这样一种情况,它还存在很多小细节需要考虑的特殊输入情况。

情况一:检查字符串是空串的情况

str != nullptr

情况二:输入字符串是“0”的情况

在这里,当我们输入的字符串是“0”,他的返回值应该是0,但是他和当字符串是空串的时候返回0冲突了,所以我们要设置一个全局变量来区分。如果是非法输入,则返回0并把这个全局变量设为一个特殊标记;如果输入的是字符串“0”,则返回0,不会设置全局变量。

enum Status{kValid = 0,kInvaild};
int g_nStatus = kValid;

**情况三:**不是所有“0”~“9”之外的字符都是非法的,输入的字符串会有正负号的区别,所以加号和减号应该也是合法的输入

if (*str == '+')
   str++;
else if (*str == '-')
{
	str++;
	minus = true;
}

情况四:考虑最大的正整数值0x7FFFFFFF,最小的负整数0x80000000的情况

完整的代码实现如下:
我们分了两个函数来具体实现StrToInt函数主要是判断是否为合法输入,StrToIntCore为具体转换的核心代码

enum Status{kValid = 0,kInvaild};
int g_nStatus = kValid;
long long StrToIntCore(const char* digit, bool minus);

int StrToInt(const char* str)
{
	g_nStatus = kInvaild;
	long long num = 0;

	if (str != nullptr && *str != '\0')//空串判断
	{
		bool minus = false;//设置一个布尔值来保存符号
		//符号判断
		if (*str == '+')
			str++;
		else if (*str == '-')
		{
			str++;
			minus = true;
		}

		//其余字符串转换
		if (*str != '\0')
		{
			num = StrToIntCore(str, minus);
		}
	}
	return (int)num;
}
long long StrToIntCore(const char* digit, bool minus)
{
	long long num = 0;

	while (*digit != '\0')
	{
		if (*digit >= '0' && *digit <= '9')
		{
			int flag = minus ? -1 : 1;
			num = num * 10 + flag * (*digit - '0');//核心代码

			//溢出判断
			if ((!minus && num > 0x7FFFFFFF) || (minus && num < (signed int)0x80000000))
			{
				num = 0;
				break;
			}
			digit++;
		}
		else//非法输入
		{
			num = 0;
			break;
		}
	}
	//输入为'\0'的合法情况
	if (*digit == '\0')
	{
		g_nStatus = kValid;
	}
	return num;
}
发布了98 篇原创文章 · 获赞 9 · 访问量 3649

猜你喜欢

转载自blog.csdn.net/qq_43412060/article/details/105403765