室友去面試,問了一個字符串轉成數字的算法題,室友沒搞出來,我心想,這個不是很簡單的嗎?因而動手在紙上畫了畫代碼。畫完後,總感受哪裏不對,最後一個個挖掘,才發現,尼瑪,這處處都是坑啊~~~特此記錄一下中坑心路。git
首先看一下數字轉成字符串。輸入一個整型數字,寫一個函數,返回整型數字對應的字符串形式。如:github
輸入:345 輸出:"345"
這個問題第一思路應該是:對整型數字每次求最高位數字,如3,將其轉換爲對應字符 '3' ,而後將此整型值取下面的數,直到整型值爲0,輸出字符串。這個問題是,怎麼求最高位的數字,能夠用一個循環將number累除10,直到number小於10,即爲最高位。解法以下:面試
char * int2Str(int nb){ char * str= new char[12];//整型最長11位 str[11]='\0'; unsigned int nindex=0; if(nb<0){ //負數時的狀況 str[0]='-'; nb*=-1; //轉換爲正數 ++nindex; //第0位記錄了符號'-',因此下移一位開始記錄數字 //若是是正數和0的狀況,直接從第0位開始記錄數字 }else if(0==nb){ //0時的狀況 str[0]='0'; str[1]='\0'; return str; } int tmpNum,len; while(nb!=0){ len=1; tmpNum=nb; while(tmpNum>=10){ len*=10; //記錄最高位的位度 tmpNum/=10; //循環累除10求最高位數字 } str[nindex++]=tmpNum+'0'; nb=nb-tmpNum*len; //nb 減去最高位 if(nb==0){ //判斷最後一位是否爲 0 的狀況 str[nindex++]='0'; break; } } str[nindex]='\0'; //添加結束符號 return str; }
在代碼A中考慮了全部可能出現的狀況,如:整數nb爲0或負數時的狀況,整數nb的最後一位爲0時的狀況(僅限此種思路的狀況)。能夠看到,算法主要的計算部分在兩個while循環中,假設nb共有M位。則第一個循環須要循環M次,第二個循環總循環次數爲:\((M-1)+(M-2)+...+3+2+1=M(M-1)/2\) ,總共的循環次數爲\((M-1)M/2\),時間複雜度爲:\(O(M^2)\)。固然,因爲整型數字,最長才只有10位(不包含正負號),因此常數時間內便可解決。但有沒有其餘解法了呢?算法
若是咱們從低位開始轉換進字符數組裏,而不是從高位開始,那不就能省掉第二個while循環了嘛!問題是,咱們一開始並不知道整數nb有多少位!因此咱們轉換的低位放到字符數組str的哪裏呢?這裏有兩種思路:數組
char * int2StrB(int nb){ char * str= new char[12];//整型最長帶上符號共11位 str[11]='\0'; unsigned int nindex=0; if(nb<0){ str[0]='-'; nb*=-1; //轉換爲正數 nindex=1; }else if(0==nb){ str[0]='0'; str[1]='\0'; return str; } unsigned int len=0; //記錄nb的位數 int tmpNb=nb; while(0!=tmpNb){ ++len; tmpNb/=10; } if(nindex==0)--len; //在str中定位最後一位數字應該在的位置 str[len+1]='\0'; //設置結束符號 while(0!=nb){ str[len--]=nb%10+'0'; nb/=10; } return str; }
代碼B中,首先一個循環求出整數nb(若是nb爲負數,此時已經轉換爲對應的正數)總共位數M,須要循環M次,時間複雜度爲\(O(M)\);第二個循環依然是依次遍歷整數nb的每一位,時間複雜度依然爲\(O(M)\),因此總時間複雜度爲:\(O(M)+O(M)=O(M)\)。固然,其實M是有最大數限制的。微信
char * int2StrC(int nb){ char * str= new char[12];//整型最長帶上符號共11位 str[11]='\0'; unsigned int nindex=0; if(nb<0){ str[0]='-'; nb*=-1; //轉換爲正數 nindex=1; }else if(0==nb){ str[0]='0'; str[1]='\0'; return str; } unsigned int nstar=nindex; //記錄要逆序的初始位置 while(0!=nb){ str[nindex++]=nb%10+'0'; nb/=10; } str[nindex]='\0'; //字符串逆序 --nindex; while(nstar<nindex){ char tmp=str[nstar]; str[nstar]=str[nindex]; str[nindex]=tmp; ++nstar; --nindex; } return str; }
代碼C中也有兩個循環,第一個循環完成將整數nb從低位到高位逆序轉換進str中,須要時間複雜度爲\(O(M)\);第二個循環將對應數字的部分進行逆序,時間複雜度爲\(O(M/2)=O(M)\),因此總時間複雜度也爲:\(O(M)\)。函數
至於B和C哪一個更好,我是建議用B的,簡潔明瞭。至於誰更快寫,確定都比A快,其次因爲M是一常數,天然(M+M)>(M+M/2)的,可是在C中比B中的循環多出三條賦值操做,由於M不會大於11,因此,這個誰更好,就難說了~~(不知道這個分析的是否有問題~~~)spa
這個其實就是實現一下C庫函數的atoi函數。固然咱們有個簡單的處理,就是使用C++的stringstream
類,代碼以下:指針
#include <sstream> typedef long long dlong; enum Status{kInvalid=0,kValid}; int g_status=kValid; //合法輸入 int str2IntA(const char* str){ g_status=kInvalid; if(str==nullptr || *str=='\0')return 0; //指針不爲空,字符串不爲空 if(*str!='+' && *str!='-' && (*str<'0' && *str>'9'))return 0; if(*str=='+' || *str=='-'){ //只有 "+" 和 "-" 或 '+'/'-'後跟的不是數字 if(*(str+1)=='\0' || (*(str+1)<'0' && *(str+1)>'9'))return 0; } stringstream stream(str); dlong num=INT_MAX+10; //大於整型最大數,判斷溢出 stream>>num; if(num>INT_MAX || num<INT_MIN)return 0; g_status=kValid; return (int)num; }
代碼D中,首先咱們要處理的一個問題就是,當輸入的字符串非法時,返回什麼?返回0嗎?但返回0怎麼區分這個0不是合法輸入返回的呢,因此要引入一個全局變量,當輸入非法的時候將全局變量置爲非法,不然置爲合法。註釋中已經說明了可能的非法輸入狀況,這裏就不在多說。可是stringstream流也能將下面的輸入正確輸出:code
輸入: "234dsdf" 輸出:234
而咱們知道,這其實輸入一個非法的輸入。固然咱們能夠更改代碼進行遍歷去判斷這個,但其實若是在面試中,我以爲考官應該不是但願咱們使用這個庫函數的。而應該是去從新實現C版本的那個atoi函數。因此上代碼E:
typedef long long dlong; enum Status{kInvalid=0,kValid}; int g_status=kValid; //合法輸入 int str2IntB(const char* str){ g_status=kInvalid; if(str==nullptr || *str=='\0')return 0; //指針不爲空,字符串不爲空 char const* pstr=str; int flag=1; //判斷正負數,默認爲正數 dlong num=0; //防溢出 if(*pstr=='-')flag=-1; //負數 else if(*pstr=='+') flag=1; //正數 else if(*pstr<'0' && *pstr>'9')return 0;//非法輸入 else num=(*pstr-'0')*flag; //上來就是數字,爲正數 ++pstr; if(*pstr!='\0'){ //防止字符串爲"+",或 "-"的狀況 while(*pstr!='\0'){ //循環求數字 if(*pstr>='0' && *pstr<='9'){ num=num*10+(*pstr-'0')*flag; if((flag==1 && num>INT_MAX) || (flag==-1 && num<INT_MIN)) //防溢出 return 0;//溢出 ++pstr; }else return 0; //非法輸入 } if(*pstr=='\0') g_status=kValid; //直達字符串末尾,說明輸入合法 } return (int)num; //將 dlong 型強制轉換爲 int }
至於思路,就是先去除非法輸入部分,而後一個數字一個數字的轉換爲對應字符。註釋裏說的都已經很清楚了,因此就很少說了。