zoukankan      html  css  js  c++  java
  • C语言初学者代码中的常见错误与瑕疵(9)

    题目

    字母的个数

    现在给你一个由小写字母组成字符串,要你找出字符串中出现次数最多的字母,如果出现次数最多字母有多个那么输出最小的那个。
    输入:第一行输入一个正整数T(0<T<25)
    随后T行输入一个字符串s,s长度小于1010。
    输出:每组数据输出占一行,输出出现次数最多的字符;
    样例:

    输入
    3
    abcd
    bbaa
    jsdhfjkshdfjksahdfjkhsajkf

    输出:
    a
    a
    j

    原代码

    #include <stdio.h>
    #include <string.h>
    int maxchar(char x[1010])
    {
     int i,j,temp,max;
     int a[26]={0};
     
     for (i = 0,temp =0;i<strlen(x);i++)
     {
      temp=x[i]-97;
      a[temp]+=1;
     }
    
     for(i=1,max = a[0],j=0;i<26;i++)
     {
      if(max<a[i])
      {
       j=i;
       max = a[i];
      }
     }
    
     return j+97;
    }
    
    int maxchar(char x[1010]);
    int main()
    {
    
     char s[1010],c[26];
     int T,i;
     scanf("%d",&T);
    
     for (i=0;i<T;i++)
     {
      scanf("%s",s);
      c[i]=maxchar(s);
     }
    
     for (i=0;i<T;i++)
     {
      printf("%c
    ",c[i]);
     }
    
     return 0;
    }

    评析:

    总体:


      已经学会把函数类型声明写在函数定义外面了,但把其他函数定义写在main()之前,总体上还是有头重脚轻之感。

    main():


     char s[1010],c[26];
     int T,i;

      s数组显然不应该定义在这里,因为这个数组只在第一个for语句内部用到。
      c数组不应该定义为26个元素,因为题目中说的是“0<T<25”。

     scanf("%d",&T);

      这条语句无可指责,但输入后没考虑数据有效性。 

     for (i=0;i<T;i++)
     {
      scanf("%s",s);
      c[i]=maxchar(s);
     }
    
     for (i=0;i<T;i++)
     {
      printf("%c
    ",c[i]);
     }

      原作者显然不了解这类ACM问题的套路(参见 C语言初学者代码中的常见错误与瑕疵(5)   11楼~13楼)。不过话说回来,ACM对这类问题的描述确实容易让外人误解,ACM是否应该自我检讨一下呢?
      除了不了解ACM的套路,这里的一个有欠考虑的地方是没有注意检查输入数据的有效性(当然,ACM对此是不管不顾的)。如果考虑输入数据的有效性应该不理会无效数据:

    scanf("%d",&T);
    if ( 0 <T && T <25 )
    {
       //求解问题
    }

    或者“死等”

    while ( scanf("%d",&T) , !( 0 <T && T <25 ) )
    {
        //提示重新输入
    }

      考虑的更周到些的话,可以

    while ( scanf("%d",&T)!= 1 || !( 0 <T && T <25 ) )
    {
        while ( getchar()!='
    ')
            ;
        //提示重新输入
    }

      此外,这段代码中的

    scanf("%s",s);

    这句如果写成

    scanf("%1009s",s);

    就无可挑剔了。尽管ACM的测试数据不会出问题,但这是应该考虑到的一个问题。程序员最重要的品质应该是思虑周到。

    maxchar()函数 :


    int maxchar(char x[1010])
    {
     int i,j,temp,max;
     int a[26]={0};
     
     for (i = 0,temp =0;i<strlen(x);i++)
     {
      temp=x[i]-97;
      a[temp]+=1;
     }
    
     for(i=1,max = a[0],j=0;i<26;i++)
     {
      if(max<a[i])
      {
       j=i;
       max = a[i];
      }
     }
    
     return j+97;
    }
    
    int maxchar(char x[1010]);

       函数定义和函数类型声明都存在同一个问题,就是[]内的1010。这个是不应该写的,写了编译器也不“看”,写得毫无意义。

    int a[26]={0};

       这个写成

     int a['z'-'a'+1]={0};

     为好。注意这里应该是假定使用ASCII码制,如果不是ASCII码,代码不能这样写。

     for (i = 0,temp =0;i<strlen(x);i++)
     {
      temp=x[i]-97;
      a[temp]+=1;
     }

      这个槽点较多。首先temp明显多余,其次那个97太难看了,典型的谭浩强风格。应该写为'a'。

    a[temp]+=1;

      可以直接写为 

    a[ x[i] - 'a']+=1;

      再有,i<strlen(x)写得很糟糕,因为在这里调用strlen(x),意味着在循环过程中每次循环都要调用这个函数,然而对于这个循环来说,strlen(x)其实是一个常量,并不需要每次都调用。这也是初学者常见的一个毛病,总忍不住有调用库函数的冲动,而不考虑有没有更好的写法。strlen(x)是被滥用最多的库函数之一。其实这里简单地写x[i]!=''就可以了。由此可见源文件开头的

    #include <string.h>

    也完全是多余的。
      另外这条语句的功能与下一条for语句的功能相对各自独立,各抽象为一个独立的函数为好。

     for(i=1,max = a[0],j=0;i<26;i++)
     {
      if(max<a[i])
      {
       j=i;
       max = a[i];
      }
     }

      这个地方写得有点笨,主要是变量太多。作者用max记录最大值元素,用j记录其下标,其实只要一个j就够了

     for(i=1,j=0;i<26;i++)
     {
      if(a[j]<a[i])
      {
       j=i;   
      }
     }

       最后

    return j+97;

    这个也是谭浩强之流不入流的写法,非常难看。应该写为

    return j+'a';

     重构

    /*
    字母的个数 
    现在给你一个由小写字母组成字符串,要你找出字符串中出现次数最多的字母,
    如果出现次数最多字母有多个那么输出最小的那个。 
    输入:第一行输入一个正整数T(0<T<25) 
    随后T行输入一个字符串s,s长度小于1010。 
    输出:每组数据输出占一行,输出出现次数最多的字符; 
    样例:输入 
    3 
    abcd 
    bbaa 
    jsdhfjkshdfjksahdfjkhsajkf 
    输出: 
    a 
    a 
    j
    
    作者:薛非
    出处:http://www.cnblogs.com/pmer/   “C语言初学者代码中的常见错误与瑕疵”系列博文 
    
    */
    
    #include <stdio.h>
    
    #define S_LEN 1009
    #define MAX_LEN (S_LEN + 1) 
    #define N(x) N_(x)
    #define N_(x) #x
    
    char find_major( char * );
    void count( int [] , char * ) ;
    unsigned be_most( int [], unsigned );
    
    int main( void )
    {
       int T ;
    
       puts("行数?");
       scanf("%d" , &T);
       
       if ( ! ( 0 < T && T < 25 ) )
          return 1;
       
       while ( T -- > 0 )
       {
          char s[ MAX_LEN ];
          
          scanf("%"N(S_LEN)"s" , s );
          printf("%c
    " , find_major( s ) );
       }
       
       return 0;
    }
    
    char find_major( char * s )
    {
       int num[ 'z' - 'a' + 1 ] = { 0 } ;
       
       count( num , s ) ;                 //统计字母个数 
       return 'a' + be_most( num , sizeof num / sizeof num[0] );//返回出现最多字符 
    }
    
    void count( int num[] , char * s ) 
    {
       while ( *s != '' )
          num[ * s ++ - 'a' ] ++ ; 
    }
    
    unsigned be_most( int a[] , unsigned n )
    {
       unsigned max = 0u ;
       unsigned i ;
       
       for ( i = 1u ; i < n ; i ++ )
          if ( a[max] < a[i] )
             max = i ;
    
       return max ;//最大值元素下标
    }
  • 相关阅读:
    几种垃圾回收算法
    Web前端研发工程师编程能力飞升之路
    Visual Studio使用小技巧6 – 为代码加上Using(Resolve using)和管理Using(Organize using) (转载)
    ASP.NET Session的七点认识
    经典SQL语句大全(转载)
    3种类型的程序员:“虫族”,“人族”,“神族”(转载)
    数据库设计中的14个技巧(转载)
    导致Asp.net站点重启的10个原因(转载)
    jQuery前端数据通用验证库,解放你的双手
    关于团队执行力的思考
  • 原文地址:https://www.cnblogs.com/pmer/p/3481275.html
Copyright © 2011-2022 走看看