第三次上机实验的程序设计思路分析及解答

来源:互联网 发布:淘宝麦兜足球装备 编辑:程序博客网 时间:2024/05/19 07:11

第三次上机实验的程序设计思路分析及解答

作者:汤伟

声明:本文档仅供菜鸟参考,高手就免了吧。

联系方式:tw0814@sina.comQQ: 17834798,各个群中,我都加入了。

 

1、  题目

编写一个程序要求用户输入一段文字,然后按照每个单词的开头字母对这段文字分类统计单词的数量并排序输出。例如,一次运行程序情况如下:

Please input a passage:

The topic of this assignment is about array, pointer and string. In particular, the goal of the assignment is to give you experience for dividing programs into modules and using the pointer for manipulation of string data.

Words begin with t: 7

Words begin with a: 6

Words begin with i: 4

Words begin with p: 4

Words begin with o: 3

Words begin with d: 2

Words begin with f: 2

Words begin with g: 2

Words begin with m: 2

Words begin with s: 2

Words begin with e: 1

Words begin with u: 1

Words begin with y: 1

 

Total words: 37

 

2、审题,题目要求。

1)、要求用户输入一段文字,而不仅仅指例子中的文字。

2)、对这段文字中的开头字母进行统计计数,从例子中可以看出,是不区别大小写,也就是说,大写字母应同小写字母一样对待。

3)、要将出现过的开头字母的数量打印输出,并且要根据数量排序,从例子中可以看出,是降序。

4)、要统计出现过的单词总数。

 

3、  关键知识点,后面会详细论述到的。

1)、函数gets()的用法。

2)、字符串定义

char buffer[1024*10];

3)、结构体定义

struct PAIR

{

       char        alpha;

       int           count;

};

4)、结构体数组

PAIR alpha_group[PAIR_COUNT];

5)、大写字母转换成小写字母的函数tolower()

5)、函数strtok()的用法

6)、排序

 

4、  解题思路及步骤

此题核心问题是:在一字符串中,有一些单词,这些单词被空格或其它标点隔开,请指出每个单词的第一个字母。比如,有一段话如下:

The topic of this assignment is about array

那么,我们都能指出符合条件的字符是其中用红色表示的字符。在C语言提供中的库中,就有这么一个函数能达到以上要求:strtok()。在MSDN中,该函数提供的示例说明如下:

 

/* STRTOK.C: In this program, a loop uses strtok
 * to print all the tokens (separated by commas
 * or blanks) in the string named "string".
 */
 
#include <string.h>
#include <stdio.h>
 
char string[] = "A string/tof ,,tokens/nand some  more tokens";
char seps[]   = " ,/t/n";
char *token;
 
void main( void )
{
   printf( "%s/n/nTokens:/n", string );
   /* Establish string and get the first token: */
   token = strtok( string, seps );
   while( token != NULL )
   {
      /* While there are tokens in "string" */
      printf( " %s/n", token );
      /* Get next token: */
      token = strtok( NULL, seps );
   }
}
 

以下是输出结果:

A string   of ,,tokens
and some  more tokens
 
Tokens:
 A
 string
 of
 tokens
 and
 some
 more
 tokens
该函数表示,有一个字符串(string),包含着一些被分隔符分隔的token (可理解成单词),依次定位到每个token,而分隔符是位于一个集合(seps)中。
原型:
char * strtok( char *strToken, const char *strDelimit );
strToken         包含着token字符串(string)
strDelimit       分隔符集合(seps)
返回值:            指向每个token的指针
 
请把函数strtok用法仔细理解,最好上机实验,否则请不要往下看,这是核心中的核心。
 
 
从上面的例子可以看出,找每个单词的第一个字母不成问题了,下面要解决的是:对找到字母进行计数。也就是说我们有一些字母如下:
T t o t a i a a
其中,计算出有几个a,几个b,几个z
注意:前面已经说过,不区分大小写,即看到A,就相当于看到a
最直接的方法,是定义
int a_count, b_count, … z_count;
可以想像一下,这是26个字母,要是260呢?
程序设计有一种思想,就是尽可能地把有规律地一系列事物做到一个统一的结构中。
所以可以这样写:
int alpha_count [26]
其中alpha_count[0]表示字符 'a'的数量
alpha_count[1]表示字符 'b'的数量
alpha_count[25]表示字符 'z'的数量
那么在实际写程序中,不可能把alpha_count [n]对应哪个字符背下来吧。这里有一个方法:
alpha_count[ch-'a']则对应ch的数量,这里ch表示'a'—'z'之间的字符。
这是因为,英文字符都对应一个整数,而且是连续分布的。实际上'a'就是97'c'就是99,所以alpha_count['c'-'a']就是alpha_count[99-97],即'c'就是对应alpha_count[2]
你把'a'做四则运算同97是一样的,比如:
int itest = 'a' * 100;
此时itest的值是9700,明白?
或者可以这样说,'a'就是整数97在字符上的显示。如果还不理解,就记住它。
注意:这里经常说字母和字符,它们的关系是:
字母表示6565+26之间(大写),以及9797+26之间的字符。
字符表示对应于0-127之间的显示,也就是说字母是字符的子集,这里字符仅指ASCII表,不针对于比较复杂的其它字符集。
 
好,数组定义好后,只要在每个找到字母的地方,使其相应的alpha_count[c-'a']自增就可以了。
 
 
可以将MSDN中的例子修改如下:
#include <string.h>
#include <stdio.h>
 
char string[] = "A string/tof ,,tokens/nand some  more tokens";
char seps[]   = " ,/t/n";
char *token;
 
void main( void )
{
    int alpha_count[26];
    int i;
 
    /* initialize, set to zero */
    for (i=0; i<26; i++)
    {
           alpha_count[i] = 0;
    }
    
    /* Establish string and get the first token: */
    token = strtok( string, seps );
    while( token != NULL )
    {
           /* add 1 */
           alpha_count[token[0]-'a']++;
 
           /* Get next token: */
           token = strtok( NULL, seps );
    }
 
    /* output */
    for (i=0; i<26; i++)
    {
           if (alpha_count[i] == 0)
                   continue;
 
           printf ("Words begin with %c: %d/n", 'a'+i, alpha_count[i]);
    }
}
 
这里有一个问题:
token[0]表示每个单词的第一个字母,小写的肯定正常,如果是大写字母,则token[0]-'a',总是负数,放在数组索引中,肯定不对。我们可以用一个函数tolower()将大写字母,转换成小写字母,如果本来就不是大写字母,则不变。所以将token[0]-'a'改为tolower(token[0])-'a'就行了。
注:tolower()位于ctype.h中,只要
#include <ctype.h>
OK
另外,可以增加了一个变量total_word_count,用来计算总的单词数,只要在找到时自增就可以了,最后输出,这个简单!
 
 
继续!
字符串要求用户输入,不能写死,要用字符串的输入函数。
C语言库中,提供了很多字符串的输入函数,但这里要求以回车做为结束的标志。只有gets()比较合适。
原型:
char *gets( char *buffer );
表示将从屏幕中得到的以回车做为结束的字符串,放到buffer中。前提是要求,这个buffer,必须首先定义好,并且有足够的大小,可容纳这些字符串。
所以,我们可以改下面语句:
char string[] = "A string/tof ,,tokens/nand some  more tokens";
char string [10240];
gets(string)
 
程序修改如下:
#include <string.h>
#include <stdio.h>
#include <ctype.h>
 
char seps[]   = " ,.?/t/n";
char *token;
 
void main( void )
{
    int alpha_count[26];
    char string[1024*10];
    int total_word_count = 0;
    int i;
 
    /* 1. initialize, set to zero */
    for (i=0; i<26; i++)
    {
           alpha_count[i] = 0;
    }
    
    /* 2. input */
    gets(string);
 
    /* 3. Establish string and get the first token: */
    token = strtok( string, seps );
    while( token != NULL )
    {
           /* add 1 */
           alpha_count[tolower(token[0])-'a']++;
           total_word_count++;
 
           /* Get next token: */
           token = strtok( NULL, seps );
    }
 
    /* 4. output */
    for (i=0; i<26; i++)
    {
           if (alpha_count[i] == 0)
                   continue;
 
           printf ("Words begin with %c: %d/n", 'a'+i, alpha_count[i]);
    }
    
    printf ("/nTolal words: %d/n", total_word_count);
}
 
好,到此为止,我们已经满足了2中的(1),(2),(4),只剩下排序了。
在这里,有两种思路:
1、  对原思路进行改进,确立字母和其数目之间在任何时间均能找到对应关系。
2、 建立一个alpha_count [26]的拷贝sort[26],将sort排序,通过sort的元素值,寻找在alpha_count中的位置,从而得到位于该位置所对应的字母。这条思路在题目中,已有源码。
 
先谈谈思路1
通过前面的论述,我们知道,字母ch对应的在alpha_count中数量是alpha_count[ch-'a'],也就是说
alpha_count[0]一定是表示'a'的数量,如果直接将alpha_count排序,则alpha_count[0]很可能不是表示'a'的数量,有没有一种方法,直接将字母和数量关联起来呢?
有!
我们可以定义一个结构
struct PAIR
{
    char           alpha;
    int            count;
};
表示两者的关联。其中alpha表示一个具体的字母,count该字母的数量。
总共有26个字母,写成数组就是
PAIR alpha_group[26];
关于语法上,可以这样理解
PAIR看成一种自定义类型,类似于int
PAIR alpha_group[26];就类似于int alpha_group[26];
访问方式如下:
取元素还是alpha_group[0]alpha_group[1]
取具体的alphacount则可以用
alpha_group[0].alpha, alpha_group[0].count
alpha_group[1].alpha, alpha_group[1].count等。
可修改程序如下:
#include <string.h>
#include <stdio.h>
#include <ctype.h>
 
char seps[]   = " ,.?/t/n";
char *token;
 
struct PAIR
{
    char    alpha;
    int            count;
};
 
void main( void )
{
    struct PAIR alpha_group[26];
    char string[1024*10];
    int total_word_count = 0;
    int i;
 
    /* 1. initialize, set to zero */
    for (i=0; i<26; i++)
    {
           alpha_group[i].alpha = 'a'+i;
           alpha_group[i].count = 0;
    }
    
    /* 2. input */
    gets(string);
 
    /* 3. Establish string and get the first token: */
    token = strtok( string, seps );
    while( token != NULL )
    {
           /* add 1 */
           alpha_group[tolower(token[0])-'a'].count++;
           total_word_count++;
 
           /* Get next token: */
           token = strtok( NULL, seps );
    }
 
    /* 4. output */
    for (i=0; i<26; i++)
    {
           if (alpha_group[i].count == 0)
                   continue;
 
           printf ("Words begin with %c: %d/n", 
                   alpha_group[i].alpha, 
                   alpha_group[i].count);
    }
    
    printf ("/nTolal words: %d/n", total_word_count);
}
 
再来谈谈排序:
排序中,以冒泡排序最容易掌握,常见的样式如下:
for (int i=0; i<n; i++)
  for (int j=i; j<n; j++) 
  {
    if (a[i] > a[j]) /* 升序用 >,降序用 < */
    {
       temp = i;
       i = j;
       j = temp;
}  
}
注:老师在复习资料中给的样式与此不同,但实质都一样。
针对于alpha_group的排序,用上面的样式基本上也适用,大致如下:
for (i=0; i<n; i++)
{
        for (j=i; j<n; j++)
        {
               if (alpha_group[i].count < alpha_group[j].count)
               {
                       alpha = alpha_group[i].alpha;
                       itemp = alpha_group[i].count;
 
                       alpha_group[i].alpha = alpha_group[j].alpha;
                       alpha_group[i].count = alpha_group[j].count;
 
                       alpha_group[j].alpha = alpha;
                       alpha_group[j].count = itemp;
               }
        }
}
 
完整程序如下:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
 
struct PAIR
{
        char    alpha;
        int            count;
};
 
#define PAIR_COUNT 26  
/*
#define TEST
*/
 
int main(int argc, char* argv[])
{
        PAIR alpha_group[PAIR_COUNT];
        int total_word_count = 0;
        int i,j;
        char buffer[1024*10] = {0};
        char seps[] = " ,.?!/t";
        char *token;
        char alpha;
        int itemp;
 
 
        /* 1. init */
        for (i=0; i<PAIR_COUNT; i++)
        {
               alpha_group[i].alpha = 'a' + i;
               alpha_group[i].count = 0;
        }
 
 
#ifndef TEST
 
        /* 2. input */
        printf("Please input a passage:/n");
        gets(buffer);
 
#else
 
        /* 2. test */
        strcpy(buffer, "The topic of this assignment is about array, /
               pointer and string. In particular, /
               the goal of the assignment is to give you experience /
               for dividing programs into modules and using the pointer /
                       for manipulation of string data.");
        
#endif
        
        /* 3. parse string */
        token = strtok(buffer, seps);
        while(token != NULL)
        {
               alpha = token[0];
               if (isalpha(alpha))
               {
                       alpha_group[tolower(alpha)-'a'].count++;
                       total_word_count ++;
               }
 
               token = strtok(NULL, seps);
        }
 
        /* 4. sort */
        for (i=0; i<PAIR_COUNT; i++)
        {
               for (j=i; j<PAIR_COUNT; j++)
               {
                       /* swap */
                       if (alpha_group[i].count < alpha_group[j].count)
                       {
                               alpha = alpha_group[i].alpha;
                               itemp = alpha_group[i].count;
 
                               alpha_group[i].alpha = alpha_group[j].alpha;
                               alpha_group[i].count = alpha_group[j].count;
 
                               alpha_group[j].alpha = alpha;
                               alpha_group[j].count = itemp;
                       }
               }
        }
 
        /* 5. output */
        for (i=0; i<PAIR_COUNT; i++)
        {
               if (alpha_group[i].count == 0)
                       continue;
 
               printf ("Words begin with %c: %d/n", 
                       alpha_group[i].alpha, 
                       alpha_group[i].count);
        }
 
        printf ("/nTolal words: %d/n", total_word_count);
        
        return 0;
}
 
注意:这是我自己的程序,其中定义了一个TEST宏,表示测试用,以免每次都输入一大段话。只要去掉加在
#define TEST
上的注释,就可以直接看到结果了。
 
 
现在谈谈思路2
思路1中已经说明,不能将alpha_count随便排序,否则找不到对应关系,那么找一个复制品,再对其排序还是可以的。如下
int sort[26];
for (int i=0; i<26; i++)
        sort[i] = alpha_count[i];
对数组sort排序,冒泡排序的算法,在思路1中已经说明
遍历sort中的元素,找到在alpha_count中的位置n,根据n得到对应字母'a'+n
for (int i=0; i<26; i++)
{
        if (sort[i] == 0)
               continue;
        for (int j=0; j<26; j++)
        {
               if (sort[i] == alpha_count[j])
               {
                       /* 打印字母'a'+j,及其数量sort[i] */
                       
 
                       alpha_count[j] = 0; 
                       /* 这句很重要,防止两个字母的数量相同 */
               }
        }
}
 
完整源码如下:
#include <string.h>
#include <stdio.h>
#include <ctype.h>
 
char seps[]   = " ,.?/t/n";
char *token;
 
void main( void )
{
        int alpha_count[26];
        int sort[26];
        char string[1024*10];
        int total_word_count = 0;
        int i, j, temp;
 
        /* 1. initialize, set to zero */
        printf("Please input a passage:/n");
        for (i=0; i<26; i++)
        {
               alpha_count[i] = 0;
        }
        
        /* 2. input */
        gets(string);
 
        /* 3. Establish string and get the first token: */
        token = strtok( string, seps );
        while( token != NULL )
        {
               /* add 1 */
               alpha_count[tolower(token[0])-'a']++;
               total_word_count++;
 
               /* Get next token: */
               token = strtok( NULL, seps );
        }
 
        /* 4. sort */
        for (i=0; i<26; i++)
               sort[i] = alpha_count[i];
        for (i=0; i<26; i++)
        {
               for (j=i; j<26; j++)
               {
                       /* swap */
                       if (sort[i] < sort[j])
                       {
                               temp = sort[i];
                               sort[i] = sort[j];
                               sort[j] = temp;
                       }
               }
        }
 
        /* 5. output */
        for (i=0; i<26; i++)
        {
               if (sort[i] == 0)
                       continue;
               
               for (j=0; j<26; j++)
               {
                       if (sort[i] == alpha_count[j])
                       {
                               printf ("Words begin with %c: %d/n", 'a'+j, sort[i]);
                               alpha_count[j] = 0; 
                       }
               }
        }
 
        
        printf ("/nTolal words: %d/n", total_word_count);
}
 
题目中的设计提示与思路2的总体思想一致,只是将strtok()再包装了一下,我填充的完整源码如下:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
 
char seps[] = " ,.?!/t";
char* g_token = NULL;
 
void InitScanner(char* buffer)
{
        g_token = strtok(buffer, seps);
}
 
int AtEndOfLine()
{
        if (g_token == NULL)
               return 1;
        else
               return 0;
}
 
char* GetNextToken()
{
        char* temp = g_token;
        g_token = strtok(NULL, seps);
        return temp;
}
 
void SortIntegerArray(int a[], int n)
{
        int i, j, temp;
 
        for (i=0; i<n; i++)
        {
               for (j=i; j<n; j++)
               {
                       /* swap */
                       if (a[i] < a[j])
                       {
                               temp = a[i];
                               a[i] = a[j];
                               a[j] = temp;
                       }
               }
        }
}
 
int main()
{
        char passage[500];
        int total, count[26], sort[26];
        char *token;
        int i, j;
        
        for(i=0; i<26; i++)
               count[i]=sort[i]=0;
        total = 0;
        
        printf("Please input a passage:/n");
        gets(passage);
        
        /* test */
        /*
        strcpy(passage, "The topic of this assignment is about array, /
               pointer and string. In particular, /
               the goal of the assignment is to give you experience /
               for dividing programs into modules and using the pointer /
                       for manipulation of string data.");
        */
 
        InitScanner(passage);
        while (!AtEndOfLine()) {
               token = GetNextToken();
               if(isalpha(token[0])){
                       count[tolower(token[0])-'a']++;
                       total++;
               }
        }
        
        for(i = 0; i<26; i++)
               sort[i] = count[i];
        SortIntegerArray(sort, 26);
        
        for(i = 0; i<26; i++){
               if(sort[i]!=0){
                       for(j=0; j<26; j++){
                               if(count[j]==sort[i]){
                                      printf("Words begin with %c: %d/n", j+'a', sort[i]);
                                      count[j] = 0;
                               }
                       }
               }
        }
        printf("/nTotal words: %d/n", total);
        
        return 0;
}
 
原创粉丝点击