zoukankan      html  css  js  c++  java
  • 算法练习之一

    今天瞎逛论坛,看到一个很不错的题目,拿来练练手,记录一下,以便日后回看。

    先看题目:

    输入一个表示整数的字符串,把该字符串转换成整数并输出,例如输入字符串"345", 则输出整数 345。
    给定函数原型 int StrToInt(const char *str) ,完成函数 StrToInt,实现字符串转换成整数的功能,不得用库函数 atoi(即便准许使用,其对于溢出情况的处理也达不到题目的要求.)

    输入:       输出:

    123        123

    -123        -123

    23a4        23

    2147483648    2147483647

    -2147483649   -2147483648

    10522545459   2147483647

    题目分析:

    初看这套题貌似很简单,但是真正开始动手敲键盘的时候才发现里面有太多的陷阱。

    陷阱一:是否考虑到了正负号的问,即代码能正确处理123与-123

    陷阱二:是否考虑到字符串中有非法字符,例如23a4,该如何处理。我们目前假设输出23是正确的

    陷阱三:如果输入的字符串超出了int型整数所能表示的最大范围怎么办?例如输入10522545459,我们知道int型最大范围为-2147483648~2147483647。我们假设一旦溢出时,输出最大    或最小数。在写代码过程中,溢出问题是最大的障碍。

    陷阱四:字符串是空怎么办?默认字符串为空是输出0.

    OK,将以上陷阱考虑清楚以后开始写代码了

      1 #include <iostream>
      2 #include <string.h>
      3 #include <limits>
      4 #include <limits.h>
      5 using namespace std;
      6 
      7 const int MAXSIZE=1000;
      8 enum Status{ kValid = 0, kInvalid};
      9 int g_nStatus = kValid;
     10 
     11 /*版本一
     12 此版本基本实现了将字符串型整数转换为int整数,但是对于一些非法的输入依旧是不行的
     13 例如:23a4,它只会返回0,而不是23;还有一旦越界后,它也是输出的0
     14 */
     15 int StrToInt( const char* str )
     16 {
     17     g_nStatus = kInvalid;
     18     long long num=0;
     19 
     20     if( str != NULL )
     21     {
     22         const char* digit = str;
     23         bool minus = false;
     24 
     25         if( *digit == '+' )
     26         {
     27             digit++;
     28         }
     29         else if( *digit == '-' )
     30         {
     31             digit++;
     32             minus=true;
     33         }
     34 
     35         while( *digit != '' )
     36         {
     37             if( *digit >='0' && *digit <='9' )
     38             {
     39                 num = num*10 + ( *digit - '0' );
     40 
     41                 if( num > numeric_limits<int>::max() )
     42                 {
     43                     num=0;
     44                     break;
     45                 }
     46                 digit++;
     47             }
     48             else
     49             {
     50                 num=0;
     51                 break;
     52             }
     53         }
     54         if( *digit == '' )
     55         {
     56             g_nStatus = kValid;
     57             if( minus )
     58             {
     59                 num=0-num;
     60             }
     61         }
     62     }
     63 
     64     return static_cast<int>(num);
     65 }
     66 
     67 /*版本二:
     68 此版本较上面的有了更好的改进,首先对于溢出问题有了很好的处理,INT_MAX是标准库里定义的int型最大值INT_MIN是最小值
     69 其次,在遇到非法字符时,循环跳出,但之前记录的数依旧会返回,也就是实现了23a4返回23的功能。
     70 小贴士:INT_MAX=2147483647   INT_MIN=-2147483648 (需要头文件limits.h) 这也是( cur-1 > INT_MAX - res*10 )这样写的原因
     71 
     72 但是,(显然这个版本还是有错),如果输入10522545459他会输出1932610867,
     73 原因是如果之前所得数已经较大了,再乘10就必定溢出,也就是超出int所能表示最大范围,会发生逻辑错误。
     74 */
     75 int StrToInt_2( const char* str )
     76 {
     77     int res=0;
     78     int i=0;
     79     int signal = '+';
     80     int cur;
     81 
     82     if( !str )
     83     {
     84         return 0;
     85     }
     86 
     87     while( isspace(str[i]))
     88     {
     89         i++;
     90     }
     91 
     92     if( str[i] == '+' || str[i] == '-')
     93     {
     94         signal = str[i];
     95         i++;
     96     }
     97 
     98     while( str[i] >= '0' && str[i] <= '9' )
     99     {
    100         cur=str[i]-'0';
    101         if( (signal == '+' ) && ( cur > INT_MAX - res*10 ) )
    102         {
    103             res=INT_MAX;   
    104             break;
    105         }
    106         else if( (signal == '-' ) && ( cur-1 > INT_MAX - res*10 ) )
    107         {
    108             res=INT_MIN;   
    109             break;
    110         }
    111 
    112         res=res*10+cur;
    113         i++;
    114     }
    115 
    116     return signal=='-'?-res:res;
    117 }
    118 
    119 /*版本三:
    120 此版本我没有想出来,而是参考july的博文写出的。
    121 它的做法最重要的是巧妙的规避了计算 n*10 这一乘法步骤,转换成计算除法 MAX_INT/10 代替,不能不说此法颇妙。
    122 这样的话在超出int型最大范围前就能提早发现它将要溢出。关键代码在***处
    123 目前这个版本的代码是最为接近正确答案的了,不过也许会有更好的代码,待日后发现再说吧!
    124 
    125 这个版本所存在的问题就显得无足轻重了:它的实现与 linux 内核的 atoi 函数的实现, 都有一个共同的问题: 
    126 即使出错, 函数也返回了一个值, 导致调用者误认为自己传入的参数是正确的, 但是可能会导致程序的其他部分产生莫名的错误且很难调试”。
    127 */
    128 int StrToInt_3( const char* str )
    129 {
    130     const int MAX_DIV=INT_MAX/10;
    131     const int MIN_DIV=INT_MIN/10;
    132     const int MAX_R=INT_MAX%10;
    133     const int MIN_R=INT_MIN%10;
    134     //另一种实现,可以不需要limit.h的头文件,便于移植
    135     /*
    136     const int MAX = (int)((unsigned)~0 >> 1);
    137     const int MIN = -(int)((unsigned)~0 >> 1) - 1;
    138     const int MAX_DIV = (int)((unsigned)~0 >> 1) / 10;
    139     const int MIN_DIV = (int)((((unsigned)~0 >> 1) + 1) / 10);
    140     const int MAX_R = (int)((unsigned)~0 >> 1) % 10;
    141     const int MIN_R = (int)((((unsigned)~0 >> 1) + 1) % 10);
    142     */
    143 
    144     int res=0;
    145     int i=0;
    146     int signal = '+';
    147     int cur;
    148 
    149     if( !str )
    150     {
    151         return 0;
    152     }
    153 
    154     while( isspace(str[i]))
    155     {
    156         i++;
    157     }
    158 
    159     if( str[i] == '+' || str[i] == '-')
    160     {
    161         signal = str[i];
    162         i++;
    163     }
    164 
    165     while( str[i] >= '0' && str[i] <= '9' )
    166     {
    167         cur=str[i]-'0';
    168         if( (signal == '+' ) && ( res > MAX_DIV || (res == MAX_DIV && cur >= MAX_R)) )  //***
    169         {
    170             res=INT_MAX;   /*或res=MAX*/
    171             break;
    172         }
    173         else if( (signal == '-' ) && ( res > MIN_DIV || (res == MIN_DIV && cur >= MIN_R)) ) //***
    174         {
    175             res=INT_MIN;   /*或res=MIN*/
    176             break;
    177         }
    178 
    179         res=res*10+cur;
    180         i++;
    181     }
    182 
    183     return signal=='-'?-res:res;
    184 }
    185 
    186 
    187 int main( void )
    188 {
    189     char str[MAXSIZE];
    190     memset( str, 0, MAXSIZE);
    191     while( cin.getline( str, MAXSIZE ))
    192     {
    193         cout<<str<<"          "<<StrToInt_2(str)<<endl;
    194     }
    195 
    196     return 0;
    197 }

    写完这道题以后,也给我自己提个醒:

    1.拿到一道题时候要充分考虑输入的各种情况,能够对所有非法输入有一个正确处理,这就是所谓的程序的鲁棒性(健壮性)

    2.所有题目题目的解法都离不开基础,程序中用的都是基础的数据结构。int+array=all

    3.善于分析题目中隐含的小知识,细节决定成败。这也是我目前所或缺的。

  • 相关阅读:
    表的相关操作
    存储引擎介绍
    库的相关操作
    初始数据库
    linux版本的mysql安装
    mysql在windows上的安装即配置
    线程实际操作篇
    用户模板和用户场景
    顶会热词统计
    移动端疫情展示
  • 原文地址:https://www.cnblogs.com/HXloveLL/p/3683796.html
Copyright © 2011-2022 走看看