トピック:
単純な文字列式の値を計算するための基本的な計算機を実装します。
文字列式には、負でない整数、+、-、*、/ 4つの演算子とスペースのみが含まれます。整数除算は整数部分のみを保持します。
例:
入力: "3 + 2 * 2"
出力:7
ソース:
問題解決のアイデア:スタック
足し算、引き算、掛け算、割り算、正の整数しかないため、判断ロジックは比較的単純です。
2つのスタックを定義します。1つは数値用、もう1つは演算子用で、+演算子のみです。
最初の数字をスタックに置き、次の文字列をトラバースすると、演算子と数字がペアで表示されます。
- プラス記号とマイナス記号の場合、数値はスタックにプッシュされ、演算子はスタックにプッシュされます。
- 乗算と除算記号の場合、スタックの最上位の数値は新しい数値で計算され、スタックの最上位の数値が変更されます
最後に、演算子のスタックがトラバースされ、加算または減算が最初から最後まで順番に計算されます。
cコード:
配列を使用してスタックを実装し、スタックサイズに注意を払い、範囲外のintに注意を払います。
char *get(char *s, long *n) {
char *p = s;
while (*p == ' ') p++;
*n = 0;
while (*p >= '0' && *p <= '9') {
*n = *n * 10 + *p - '0';
p++;
}
while (*p == ' ') p++;
return p;
}
// +-*/正整数
int calculate(char * s) {
long num[210000];
char opt[210000];
int topn = -1;
int topo = -1;
long n = 0;
char *p = get(s, &n);
num[++topn] = n;
while (*p != 0) {
char ch = *p;
p = get(p+1, &n);
if (ch == '*') {
num[topn] *= n;
} else if (ch == '/') {
num[topn] /= n;
} else {
num[++topn] = n;
opt[++topo] = ch;
}
}
long ret = num[0];
for (int i = 0; i <= topo; i++) {
if (opt[i] == '+') {
ret += num[i+1];
} else {
ret -= num[i+1];
}
}
return ret;
}
スペースの消費量は非常に多いと思いましたが、結果的にはそれほど大きくありませんでした。
最適化のアイデア1:
ブラケットがないので、たくさんの収納スペースは必要ありません。
演算子の数が2に等しい場合、スタック内の数をマージできます。現時点では、スタックには3つの数値しかなく、これらの3つの数値は加算と減算の関係にあるため、1番目と2番目の数値をマージすることのみが可能です。
最適化後のコードは次のとおりです。
char *get(char *s, long *n) {
char *p = s;
while (*p == ' ') p++;
*n = 0;
while (*p >= '0' && *p <= '9') {
*n = *n * 10 + *p - '0';
p++;
}
while (*p == ' ') p++;
return p;
}
// +-*/正整数
int calculate(char * s) {
long num[3];
char opt[2];
int topn = -1;
int topo = -1;
long n = 0;
char *p = get(s, &n);
num[++topn] = n;
while (*p != 0) {
char ch = *p;
p = get(p+1, &n);
if (ch == '*') {
num[topn] *= n;
} else if (ch == '/') {
num[topn] /= n;
} else {
num[++topn] = n;
opt[++topo] = ch;
if (topo == 1) {
// 合并第一个和第二个数字
if (opt[0] == '+') {
num[0] += num[1];
} else {
num[0] -= num[1];
}
num[1] = num[2];
topn = 1;
opt[0] = opt[1];
topo = 0;
}
}
}
long ret = num[0];
for (int i = 0; i <= topo; i++) {
if (opt[i] == '+') {
ret += num[i+1];
} else {
ret -= num[i+1];
}
}
return ret;
}
演算結果:
最適化のアイデア2:
上記のコードはまだ非常に複雑なので、もう一度最適化しました。
最適化のアイデアは次のとおりです:
1.符号が-の場合、数値は負になり、マイナス記号はプラス記号になるため、2番目のスタックはすべてプラス記号になり、存在する必要はありません。
2.最初のスタックに必要なスペースは3つだけなので、1つは前の結果を格納し、1つはスタックの最上位要素を格納し、もう1つは新しい番号を格納し、3つの変数を定義できます。スタックの最上位にあるポインタを削除するには、文字列sを0 + sに変更して、ポインタが常にスタックの最上位を指すようにします。
最適化されたコードは次のとおりです。
char *get(char *s, long *n) {
char *p = s;
while (*p == ' ') p++;
*n = 0;
while (*p >= '0' && *p <= '9') {
*n = *n * 10 + *p - '0';
p++;
}
while (*p == ' ') p++;
return p;
}
int calculate(char * s) {
long ret = 0, top = 0;
char *p = get(s, &top); // 将第一个数字放入栈顶top
while (*p != 0) {
char ch = *p;
long n = 0;
p = get(p+1, &n);
if (ch == '*') {
top *= n;
} else if (ch == '/') {
top /= n;
} else {
if (ch == '-') n = 0 - n;
ret += top;
top = n;
}
}
return ret + top;
}
記事は何度も改訂されており、問題を解決するための他のアイデアが見つからない限り、これまで再度改訂するべきではありません。製品を完璧に近づけるには、何度も焼き戻しを行う必要があります。