在面试考题里面经常出现处理数值和字符串的函数,这篇文章,我们主要来讲解一个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;
}