インタビューの質問では、数値や文字列を処理するための関数がよくありますが、この記事では主にpow and atoi関数の実装について説明します〜
1つは、pow関数
トピックの要件
関数double Power(double base、int exponent)を実装し、baseの指数指数を求めます。ライブラリ関数を使用しないでください。また、多数を考慮する必要はありません。
トピック分析
多数の問題を考慮する必要がないため、まずこの問題が発生すると、私たちの頭脳から生まれたアイデアは次のように実現されます。
double Power(double base, int exponent)
{
double result = 0;
for (int i = 0; i <= exponent; i++)
{
result *= base;
}
return result;
}
しかし、このように書くのは間違いです。多くの欠点があります
改善1:底が0で指数が負の
場合を考えるこのような特殊なケースのため、エラー処理の方法を考慮する必要があります。
私たちが作成するプログラムには、通常3つのエラー処理方法があります。以下に、対応する長所と短所を示します
。改善されたコードは次のとおりです。
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に設定されます。
改善2:より効率的なPowerresultメソッド
入力指数が32の場合、前のループでは31ループを実行する必要がありますが、慎重に考えると、より簡潔なメソッドがあるようです。
ここでは、フィボナッチシーケンスで言及したソリューションについて説明します。フィボナッチ列に数式を使用する方法があります。その時間の複雑さはO(logn)です。パワーを考慮した数式は次のとおりです。
上記の数式から、メソッドをn倍改善したいことがわかります。二乗、最初にn / 2乗を取得し、次にn / 2乗の結果を二乗する必要があります。これは、再帰的なアイデアで実現できます。
フィボナッチ数列の前兆があるため、関数について考えるのは簡単です。私たちは32乗を求めているので、そこから16乗、8乗、4乗、そして2乗を計算します。これから導出される数式は次のとおりです。
上記の数式に基づいて、コードは次のように実装されます。
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;
}
2、atoi関数
トピックの要件
文字列を整数に変換します。たとえば、文字列「123」を入力します。彼の出力は数値123です。
トピック分析
まず、この質問を取得します。質問の要件に従って、「123」を数123に変換する場合のみを考えます。しかし、この質問は非常に単純であるだけでなく、そのような状況を考慮するだけでなく、細部まで考慮する必要がある多くの特別な入力状況もあります。
ケース1:文字列が空かどうかを確認する
str != nullptr
ケース2:入力文字列が「0」の場合
ここで、入力した文字列が「0」の場合、戻り値は0になりますが、文字列が空の場合は0の戻り値と競合するため、区別するためにグローバル変数を設定する必要があります。不正な入力の場合は0を返し、グローバル変数を特殊マークとして設定します。入力が文字列 "0"の場合は0を返し、グローバル変数は設定されません。
enum Status{kValid = 0,kInvaild};
int g_nStatus = kValid;
**ケース3:**「0」〜「9」以外のすべての文字が無効であるとは限りません。入力文字列には正負の符号の違いがあるため、プラス記号とマイナス記号も有効な入力である必要があります
if (*str == '+')
str++;
else if (*str == '-')
{
str++;
minus = true;
}
ケース4:最大の正の整数値が0x7FFFFFFFであり、最小の負の整数値が0x80000000であるケースを検討します。
完全なコード実装は次のとおりです
。StrToInt関数を具体的に実装するために2つの関数を分割し、主にそれが有効な入力であるかどうかを判断します。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;
}