数字(int)转字符串和字符串转数字(int)
室友去面试,问了一个字符串转成数字的算法题,室友没搞出来,我心想,这个不是很简单的吗?于是动手在纸上画了画代码。画完后,总感觉哪里不对,最后一个个挖掘,才发现,尼玛,这到处都是坑啊~~~特此记录一下中坑心路。
1. 数字转字符串
首先看一下数字转成字符串。输入一个整型数字,写一个函数,返回整型数字对应的字符串形式。如:
输入:345
输出:"345"
这个问题第一思路应该是:对整型数字每次求最高位数字,如3,将其转换为对应字符 '3' ,然后将此整型值取下面的数,直到整型值为0,输出字符串。这个问题是,怎么求最高位的数字,可以用一个循环将number累除10,直到number小于10,即为最高位。解法如下:
- 代码A
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的哪里呢?这里有两种思路:
- 先求出整型数字nb的位数M,然后将转换后的字符直接存进str对应的位置。如代码B
- 将nb从低位开始转换的字符依次从前向后存入str,即str中存入的其实是要输出字符串的逆串,然后再将字符串给正过来,即求逆串。如代码C
- 代码B
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是有最大数限制的。
- 代码C
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,所以,这个谁更好,就难说了~~(不知道这个分析的是否有问题~~~)
总结一下主要的坑:
- 负数时返回的字符串第一位要有 '-' 号,正数从人的角度上考虑不该加 '+'
- 转换的字符串在结尾要有 '\0',否则可能出错
字符串转数字
这个其实就是实现一下C库函数的atoi函数。当然我们有个简单的处理,就是使用C++的stringstream
类,代码如下:
- 代码D
#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流也能将下面的输入正确输出:
输入: "234dsdf"
输出:234
而我们知道,这其实输入一个非法的输入。当然我们可以更改代码进行遍历去判断这个,但其实如果在面试中,我觉得考官应该不是希望我们使用这个库函数的。而应该是去重新实现C版本的那个atoi函数。所以上代码E:
- 代码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
}
至于思路,就是先去除非法输入部分,然后一个数字一个数字的转换为对应字符。注释里说的都已经很清楚了,所以就不多说了。
总结一下主要的坑:
- 怎么返回非法输入的结果,并且怎么进行区分
- 转换为整型后,可能会溢出,怎么进行溢出判断
- 非法输入:
- 空指针,空字符串
- 只有一个正负号,正负号不是出现在第一个位置
- 字符串中含有非正负号和数字的其他字符
3. 最后上代码文件
附录
- GitHub-Blog
- 关注微信订阅号 LomperWay:
数字(int)转字符串和字符串转数字(int)的更多相关文章
- C++ 判断字符串是否全是数字
在实际的工作中,需要提取程序中的字符串信息,但是程序中经常将一些数字当做字符串来进行处理,例如表盘的刻度信息,这时候就需要判断字符串是否全为数字,来进行真正意义上的字符串提取.下面介绍了判断字符串是否 ...
- java中判断一个字符串是否“都为数字”和“是否包含数字”和“截取数字”
在javascript中有一个方法isDigit()使用来判断一个字符串是否都是数字,在java的字符串处理方法中没有这样的方法,觉得常常需要用到,于是上网搜了一下,整理出了两个用正则表达式匹配的判断 ...
- C# 判断一字符串是否为合法数字(正则表达式)
判断一个字符串是否为合法整数(不限制长度) public static bool IsInteger(string s) { string pattern = @"^\d*$"; ...
- [Arduino] 在串口读取多个字符串,并且转换为数字数组
功能如题目.在串口收到逗号分割的6串数字比如100,200,45,4,87,99然后在6个PWM端口3, 5, 6, 9, 10, 11输出对应PWM值代码注释很详细了,就不再说明了. //定义一个c ...
- Oracle中如何判断字符串是否全为数字,以及从任意字符串中提取数字
本文介绍了判断字符串是否全为数字的4种办法,另外还介绍了一个translate函数的小技巧,从任意字符串中提取数字(调用2次translate函数).这个办法是一个公司同事发现的,用起来很方便,但理解 ...
- asp.net检查验证字符串是否为纯数字方法小结
原文 asp.net检查验证字符串是否为纯数字方法小结 在asp.net中验证字符串是不是为数字我们没有像php中那么多丰富的函数来直接使用,这里我整理了一些比较实例的验证字符串是否为纯数字方法代码 ...
- python基础入门--input标签、变量、数字类型、列表、字符串、字典、索引值、bool值、占位符格式输出
# 在python3 中: # nian=input('>>:') #请输入什么类型的值,都成字符串类型# print(type(nian)) # a = 2**64# print(typ ...
- Sql Server 中由数字转换为指定长度的字符串
一个列的数据类型是 int ,从 1 开始自动增长,另一个列是字符串,现在想把 int 列转换成 九个字符,比如 1 转换后就是 000000001 ,添到字符串列,怎么实现呢? set @imaxU ...
- 20181123_SQL Server 2008_找出以逗号分隔的字符串中最大的数字
--select [dbo].[Fun_GetMaxNum]('棉 20%,麻 190%,涤纶60%') CREATE FUNCTION [dbo].[Fun_GetMaxNum] ( @StrAll ...
随机推荐
- java中的线程(2):如何正确停止线程之3种常见停止方式
1.常见停止方式 自定义线程,其中含退出标志位,在run中判断它. 使用interrupt()方法中断线程 使用stop方法暴力终止(已经弃用) 2.使用标志位 class TestThread ex ...
- linux中安装软件,查看、卸载已安装软件方法
各种主流Linux发行版都采用了某种形式的包管理系统(PMS)来控制软件和库的安装. 软件包存储在服务器上,可以利用本地Linux系统上的PMS工具通过互联网访问.这些服务器称为仓库. 由于Linux ...
- oracle 备份恢复篇(三)---rman spfile的丢失
一,环境准备 1, 拥有全备 数据 2, 查看spfile文件位置 SQL> SQL> SELECT NAME, VALUE, DISPLAY_VALUE FROM V$PARAMETER ...
- oracle 单实例DG(搭建篇一)
一,介绍 lodding... 二,安装前环境配置 01,依赖包的安装: yum install binutils-* yum install compat-libstdc++-* yum insta ...
- (转)Linux curl命令参数详解
Linux curl命令参数详解 命令:curl在Linux中curl是一个利用URL规则在命令行下工作的文件传输工具,可以说是一款很强大的http命令行工具.它支持文件的上传和下载,是综合传输工具, ...
- BSON入门
1.概念BSON(Binary Serialized Document Format)是一种类json的一种二进制形式的存储格式,简称Binary JSON,它和JSON一样,支持内嵌的文档对象和数组 ...
- spring-boot 1.4.x遇到的cpu高的问题
如果你的spring-boot应用里tomcat线程耗cpu较高,并主要耗在做读取jar的操作上(堆栈类似下面),可能跟我们遇到同样的问题. CRC32.update(byte[], int, int ...
- linux下为.net core应用创建守护进程
1.Supervisor 安装 yum install python-setuptools easy_install supervisor 2.配置 Supervisor mkdir /etc/sup ...
- java中json解析,xml解析
抓取网页内容,会返回json或者xml(html)格式的数据. 为了方便的对上述两种格式的数据进行解析,可采用解析工具. JsonPath https://github.com/jayway/Json ...
- Hashtable语法简介
Hashtable简述 Hashtable是System.Collections命名空间提供的一个容器 Hashtable中keyvalue键值对均为object类型,所以Hashtable可以支持任 ...