C語言初學者代碼中的常見錯誤與瑕疵(9)

題目

字母的個數 html

如今給你一個由小寫字母組成字符串,要你找出字符串中出現次數最多的字母,若是出現次數最多字母有多個那麼輸出最小的那個。
輸入:第一行輸入一個正整數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\n",c[i]);
 }

 return 0;
}

評析:

整體:測試


  已經學會把函數類型聲明寫在函數定義外面了,但把其餘函數定義寫在main()以前,整體上仍是有頭重腳輕之感。spa

main():code


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

  s數組顯然不該該定義在這裏,由於這個數組只在第一個for語句內部用到。
  c數組不該該定義爲26個元素,由於題目中說的是「0<T<25」。htm

 scanf("%d",&T);

  這條語句無可指責,但輸入後沒考慮數據有效性。 blog

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

 for (i=0;i<T;i++)
 {
  printf("%c\n",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()!='\n')
        ;
    //提示從新輸入
}

  此外,這段代碼中的

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]!='\0'就能夠了。因而可知源文件開頭的

#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\n" , 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 != '\0' )
      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 ;//最大值元素下標
}
相關文章
相關標籤/搜索