實現一個 myAtoi(string s) 函式,使其能將字串轉換成一個 32 位有符號整數(類似 C/C++ 中的 atoi 函式)。
演算法如下:
第一步:
讀入字串並丟棄無用的前導空格
第二步:
檢查下一個字元(假設還未到字元末尾)為正還是負號,讀取該字元(如果有)。 確定最終結果是負數還是正數。 如果兩者都不存在,則假定結果為正。
第三步:
讀入下一個字元,直到到達下一個非數字字元或到達輸入的結尾。字串的其餘部分將被忽略。
第四步:
將前面步驟讀入的這些數字轉換為整數(即,"123" -> 123, "0032" -> 32)。如果沒有讀入數字,則整數為 0 。必要時更改符號(從步驟 2 開始)。
考慮邊界條件:
如果整數數超過 32 位有符號整數範圍 [−2^31, 2^31 − 1] ,需要截斷這個整數,使其保持在這個範圍內。具體來說,小於 −2^31 的整數應該被固定為 −2^31 ,大於 2^31 − 1 的整數應該被固定為 2^31 − 1 。
返回值:
返回有符號整數作為最終結果。
注意:
本題中的空白字元只包括空格字元 ' ' 。
除前導空格或數字後的其餘字串外,請勿忽略 任何其他字元。
舉個例子:
輸入:s = " -42"
輸出:-42
解釋:
第 1 步:" -42"(讀入前導空格,但忽視掉)
第 2 步:" -42"(讀入 '-' 字元,所以結果應該是負數)
第 3 步:" -42"(讀入 "42")
解析得到整數 -42 。
由於 "-42" 在範圍 [-231, 231 - 1] 內,最終結果為 -42 。
演算法核心步驟:
ans = ans * 10;//核心步驟1ans = ans + (s[j] - '0'); //核心步驟2
本次從字串取出的字元s[j],轉換為對應的數字(s[j] - '0'),與上一次取出的結果乘以10 相加,迴圈下去,就可以取出每一位字元,並轉換為一個有符號整數。
(s[j] - '0')這一步實現了,將一個列印字元轉換為對應的數字。
ASCII碼錶
下面就說說為什麼字元減’0’可以到相應的整數。現在比如我們要字元‘1’轉換成數字1,就這麼一個變化,我們注意到字元型常量用’ '括起來的原因是,它們在計算機中都以各自的ASCII表示。而‘1’的對應編碼是49的二進位制碼,但是我們的數字1,就等於1呀,所以為了由原來的‘1’實際上就是49的二進位制變成現在的1對應的二進位制1,只好用49-48=1了。但是在ASCII碼裡‘0’對應的剛好是48的二進位制碼,所以我們轉換的時候只需要‘1’-‘0’=1;就可以了。而數字的ASCII碼是按順序規定的。所以其它字元要轉換成數字都可以用減‘0’來表示。比如‘2’的ASCII是50,而我們要得到數字2,於是用‘2’-48=2了。看來當我們知道資料在計算機中的儲存規則的時候,問題就迎刃而解了。
根據已有演算法寫出實現程式碼:
第一步:讀入字串並丟棄無用的前導空格
while (' ' == s[i])//判斷有幾個前導空格 { i++; }
第二步:檢查下一個字元(假設還未到字元末尾)為正還是負號,讀取該字元(如果有)。 確定最終結果是負數還是正數。 如果兩者都不存在,則假定結果為正。
if ('+' == s[i] || '-' == s[i]) {//判斷是否存在符號,+或者- if ('-' == s[i]) {//如果存在-符號,flag取為-1 flag = -1; } //i只加一次,符號只判斷一次,連著兩個符號非法 i++; }
第三步:讀入下一個字元,直到到達下一個非數字字元或到達輸入的結尾。字串的其餘部分將被忽略。
for (int j = i; j < strlen(s); j++) { //空格、符號後邊必須是數字,如不是直接返回 if (s[j] < '0' || s[j] > '9')break;
第四步:將前面步驟讀入的這些數字轉換為整數(即,"123" -> 123, "0032" -> 32)。如果沒有讀入數字,則整數為 0 。必要時更改符號(從步驟 2 開始)。
ans = ans * 10;//核心步驟1ans = ans + (s[j] - '0'); //核心步驟2
考慮邊界條件:如果整數數超過 32 位有符號整數範圍 [−2^31, 2^31 − 1] ,需要截斷這個整數,使其保持在這個範圍內。具體來說,小於 −2^31 的整數應該被固定為 −2^31 ,大於 2^31 − 1 的整數應該被固定為 2^31 − 1 。
//為了防止溢位,要提前判斷是否有小於32位整數最小值的可能 if (ans < IntMin / 10 || (ans == IntMin / 10 && -(s[j] - '0') < IntMin % 10)) return IntMin; {//為了防止溢位,要提前判斷是否有大於32位整數最大值的可能 if (ans > IntMax / 10 || (ans == IntMax / 10 && (s[j] - '0') > IntMax % 10)) return IntMax;
由於邊界情況非常複雜,根據符號分為正數和負數處理部分:
if(flag == -1)//處理負數部分 { //為了防止溢位,要提前判斷是否有小於32位整數最小值的可能 if (ans < IntMin / 10 || (ans == IntMin / 10 && -(s[j] - '0') < IntMin % 10)) return IntMin; ans = -ans * 10;//核心步驟1 ans = -ans + flag * (s[j] - '0');//核心步驟2 } else//處理正數部分 {//為了防止溢位,要提前判斷是否有大於32位整數最大值的可能 if (ans > IntMax / 10 || (ans == IntMax / 10 && (s[j] - '0') > IntMax % 10)) return IntMax; ans = ans * 10;//核心步驟1 ans = ans + (s[j] - '0'); //核心步驟2 }
該題有點像腦經急轉彎題。
完整C語言程式碼:
#include<stdio.h>#include<stdlib.h>#include<string.h>#define INTMAX ((unsigned)(-1)>>1)#define INTMIN (~INTMAX)int myAtoi(char * s){ int IntMax = INTMAX; int IntMin = INTMIN; int ans = 0; int i = 0; int flag = 1; while (' ' == s[i])//判斷有幾個前導空格 { i++; } if ('+' == s[i] || '-' == s[i]) {//判斷是否存在符號,+或者- if ('-' == s[i]) {//如果存在-符號,flag取為-1 flag = -1; } //i只加一次,符號只判斷一次,連著兩個符號非法 i++; } for (int j = i; j < strlen(s); j++) { //空格、符號後邊必須是數字,如不是直接返回 if (s[j] < '0' || s[j] > '9')break; //數字分為整數和負數來判斷,方便確定邊界值 if(flag == -1) { //為了防止溢位,要提前判斷是否有小於32位整數最小值的可能 if (ans < IntMin / 10 || (ans == IntMin / 10 && -(s[j] - '0') < IntMin % 10)) return IntMin; ans = -ans * 10;//核心步驟1 ans = -ans + flag * (s[j] - '0');//核心步驟2 } else {//為了防止溢位,要提前判斷是否有大於32位整數最大值的可能 if (ans > IntMax / 10 || (ans == IntMax / 10 && (s[j] - '0') > IntMax % 10)) return IntMax; ans = ans * 10;//核心步驟1 ans = ans + (s[j] - '0'); //核心步驟2 } } return ans;}int main(){ char s[] = "-91283472332"; int ans = myAtoi(s); printf("%d\n", ans); return 0;}
LeetCode系列:
二叉樹系列: