任务2(字符数组和字符串):给出一个字符数组char str[],在程序中赋初值为一个句子,例如char str[]=”he threw threefree throws”,自编函数完成下面的功能:
(1)求出字符数组中字符的个数(从第一个字符读起,直到读到’\0’结束计数)(对于例句,输出为26);
(2)计算句子中各字符出现的频数
算法提示:
(a) i=0
(b)取字符串中的第i个符号c
(c)如果在c未在i之前出现过,在i位置上是第一次出现,频数f=1,从i+1位置到字符串结束,c每出现一次,f++,最后输出c-f(即字符-频数)对;如果在c在i之前出现过,则代表c字符已经计算过频数,不再处理,直接转(d)步;
(d)i++,若处理完,转(b)步;
可以动脑想想,或者集体讨论一下还可以采用什么算法完成这一道题,可能的算法很多。
实验目的:学会操作字符数组
实验内容:完成对字符数组的操作
/* 程序头部注释开始
* 程序的版权和版本声明部分
* Copyright (c) 2011, 烟台大学计算机学院学生
* All rights reserved.
* 文件名称: char_array.cpp
* 作 者: 贺利坚
* 完成日期: 2011 年 12 月 4 日
* 版本 号: v1.0
* 对任务及求解方法的描述部分
* 输入描述:要处理的字符串在程序中通过赋初值完成
* 问题描述:求出字符数组中字符的个数及计算句子中各字符出现的频数
* 程序输出:字符数组中字符的个数和句子中各字符出现的频数
* 程序头部的注释结束
*/
#include<iostream>
using namespace std;
int length(char[]);
void output_frequency(char[]);
int main(void)
{
char str[]="he threwthree free throws";
cout<<"要处理的字符串为:"<<str<<endl;
cout<<"字符串长度为:"<<length(str)<<endl;
cout<<"字符串中各字符出现的频数为:";
output_frequency(str);
cout<<endl;
return 0;
}
int length(char s[])
{
int i=0;
while(s[i]!='\0') i++;
return i;
}
// 下面的函数output_frequency用于求出各字符出现的频数
// 所用的算法是(算法1)
// (a) i=0
// (b)取字符串中的第i个符号c
// (c)如果在c未在i之前出现过,在i位置上是第一次出现,频数f=1,
// 从i+1位置到字符串结束,c每出现一次,f++, 最后输出c-f(即字符-频数)对;
// 如果在c在i之前出现过,则代表c字符已经计算过频数,不再处理,直接转(d)步;
// (d)i++,若处理未完成,转(b)步;
void output_frequency(char s[])
{
int len=length(s);
int i,j,f;
char c;
for(i=0;i<len;i++)
{
c=s[i]; //现在开始统计c字符,即s[i]出现的频数
j=0;
while(c!=s[j]&&j<i)j++; //如果c字符在s[i]之前出现过,循环将由于c!=s[j]而退出,这时,j<i为真(
请通过走查验证)
if(j==i) //否则,这个字符已经处理过了,转到for循环开始,i++后继续处理
{
f=1; //c在s[i]位置上第一次出现了,f置初值1
for(j=i+1;j<len;j++)
if(c==s[j]) f++; //直到字符串结束,每出现一次,累加1
cout<<c<<"-"<<f<<"; "; //输出c出现的频数f
}
}
}
运行结果:(贴图)
经验积累: 算法有些麻烦,但思路也算清晰,以后要习惯于将大脑中所想的过程自然地转换为代码
//下面是这一算法的另外一种表达,很简洁,对for循环的使用很老到
void output_frequency(char str[])
{
int i , j ,num;
for(i=0;i <= length(str);i++)//length(str)在for之前求出来保存到一个变量中就更好了
{
num = 0;
for(j=0;j<length(str);j++)
{
if(str[i]==str[j])
{
if(i>j)
break; //在i之前发现了等待统计的字符,break了之
else
num++; //这时已经过了大限,++即可
}
}
if(num!=0) //这儿我做些改动,只考虑不等于0即可
cout <<str[i]<<"-"<<num<<"; ";
}
}
下面再给出两个不同思想的算法供参考。有些同学们也想到了,不过有不少存在些小问题
算法2:
// 求出各字符出现的频数的算法2
// 算法思想:将字符数组排序,相同的字符将是连续的,形如aaaabbcccccc....计数方法比算法1简单很多
// 对于数组元素count[i],其下标为整型,而字符c当整型使用时,其值恰好为c的ASCII码。
void output_frequency(char s[])
{
int len=length(s);
char s1[80]; //无法预知字符数组长度,设一足够“长”的
int i,j,f;
char c;
for(i=0;i<len;i++)
s1[i]=s[i]; //将形参字符数组复制到临时数组变量中
s1[i]='\0'; //一般复制完后应该加上字符串结束的符号,但本题中意义不大
//下面对s1进行排序。讨论:也可以对s直接排序,但是会改变实际参数数组的值
//这在一般情况是要避免的: 该函数仅是求各字符出现的频率,不可有更多的“副作用”
//还是用冒泡法,其他方法也可以
for(j=0;j<len-2;j++)
for(i=0;i<len-j-1;i++)
if (s1[i]>s1[i+1])
{
c=s1[i];
s1[i]=s1[i+1];
s1[i+1]=c;
}
//计数形如aaaabbcccccc....的字符数组中的符号
c=s1[0];
f=1; //s1[0]字符出现了1次
for(i=1;i<len+1;i++) //i可以取值为len,有越界嫌疑,但此处不会,目标是在循环中完成最后一个符号频数的输出
{
if(c==s1[i])
f++; //重复出现相同字符,累加
else
{
cout<<c<<"-"<<f<<"; "; //输出前面的字符出现的频数
c=s1[i]; //开始计数现在读到的符号
f=1;
}
}
return;
}
总结:(1)这个算法使计数过程简单了,但却需要一个较高代价的工作:排序。如果直接对s 排序,还有产生副作用的危险,不太提倡;(2)请在本解中研究一下对相同字符相邻的字符数组计数的算法。
算法3
// 求出各字符出现的频数的算法3
// 算法思想:设置一个数组count[],当读到字符c时,将c的ASCII码作下标的数组元素累加1即可。
// 对于数组元素count[i],其下标为整型,而字符c当整型使用时,其值恰好为c的ASCII码。
void output_frequency(char s[])
{
int len=length(s);
int count[256]={0}; //int数组初始化时,未指定的元素值为0
int i,j,f;
char c;
for(i=0;i<len;i++)
{
c=s[i];
count[c]++; //c为字符,但此处用作下标,数据类型自动转换为int,取值为其ASCII码
}
for(i=0;i<256;i++)
{
if (count[i]>0)
cout<<char(i)<<'-'<<count[i]<<';'; //ASCII码为i的符号出现了count[i]次
}
}
总结:这是用空间换时间,通过count这个较大的数组,使计数简单地累加就行了。
算法4:
// 求出各字符出现的频数的算法4
// 这个算法由李洪悬提出,我们命名为李法
// 算法思想:当扫描中遇到正在统计的那个符号时,将其做一个标记,从而在以后扫描中不考虑这个符号
// 用'\0'作为这个标记。我们正在操作字符数组,而不是字符串,其循环由长度控制,故无碍
// 函数中要将s赋值比例本地的一个临时数组,目的是不致于改变实参数组的值
void output_frequency(char s[])
{
int len=length(s);
char s1[80]; //无法预知字符数组长度,设一足够“长”的
int i,j,f;
char c;
for(i=0;i<len;i++)
s1[i]=s[i]; //将形参字符数组复制到临时数组变量中
s1[i]='\0'; //一般复制完后应该加上字符串结束的符号,但本题中意义不大
for(i=0;i<len;i++)
{
if (s1[i]=='\0')continue; //如果是'\0',该字符在前面的统计中已经被处理过了,continue即可
c=s1[i];
f=0;
for(j=i;j<len;j++)
if(s1[j]==c)
{
f++;
s1[j]='\0'; //计数,当前字符赋值为'\0',以后将不再统计
}
cout<<c<<'-'<<f<<';';
}
}
下面是对“he threw three free throws”统计时,正在统计字符“e”(i=1)时单步执行中的一个截屏。可以看到其中的一些字符数组元素的值(下标为0,1,4,6,10)已经为空字符。