初学编程,大家帮忙看下这道c语言题怎么做?万分感谢
编程实现:
假设一维数组A中原有的存储内容是小写字母,要求将其数组元素转换为数字形式。
例如:原数组A[20]中内容为{a,c,b,e,t,a,t,c,c,b……},转换后为{0,1,2,3,4,0,4,1,1,2……},
转换规则是:
a------0 c------1 b------2 e------3 t------4
要求:
(1)数组长度要大于等于10;
(2)原数组A中的内容要求屏幕输入,但最多使用10个不同的小写字母,转换后的数组中的元素只使用0到9十个数字;
(3)使用数字的最大值等于转换规则的数目-1,例如原数组为{x,y,x……},则转换后为{0,1,0……},如果输出为{1,3,1……},则视为输出结果错误;
(4)输出转换后的数组,同时给出转换规则。
第二题
求划分:
在上题的基础上可以得到一个整型数组,数组元素的取值从0开始,如
a={0,1,2,0,1,1……}
要求求解上述数组的划分,算法如下:
1.选取数组元素中的最大值max和最小值min
2.建立max-min+1个数组或字符串,将第i元素a[i]放到第a[i]-min个数组或字符串中,
3.输出建立的max-min+1中每个数组中包含的元素的下角标;
例如:a={0,1,2,0,1,1,2,3,1,0}
输出结果为:
{0,9}
{1,4,5,8}
{2,6}
{7}
术语尽量低级简单 求大神帮忙!!万分感谢 展开
先给你第一题的,百度知道的这个编辑器真的不适合粘贴代码
#include<stdio.h>
#include<string.h>
#define MAX_ARRAY_SIZE 1024
#define MAX_MAP_SIZE 10
/* 输入数组,连续输入,如:aedabcdaeas */
int inputArray(char *buff) {
int len = 0;
/* 使用fgets来防止缓冲区溢出 */
if (NULL == fgets(buff, MAX_ARRAY_SIZE, stdin)) {
return 0;
}
len = strlen(buff);
/* fgets 返回的数据可能是换行符结尾的,也可能不是,对换行符结尾的进行处理 */
if (buff[len - 1] == '\n') {
buff[len - 1] = '\0';
len -= 1;
}
return len;
}
int processArray(int len, char *chars, char *map) {
/* 保存反向映射便于查找 */
int tmap[128];
int maplen = 0;
int i = 0;
char *p = chars;
memset(tmap, -1, sizeof(int) * 128);
for (i = 0; i < len; i++) {
if (*p > 'z' || *p < 'a') {
return -*p;
}
if (tmap[*p] == -1) {
if (maplen >= MAX_MAP_SIZE) {
return -1;
}
tmap[*p] = maplen;
map[maplen] = *p;
maplen += 1;
}
*p = '0' + tmap[*p];
p++;
}
return maplen;
}
int main() {
/* 用于输入的字符数组 */
char buff[MAX_ARRAY_SIZE];
/* 用于保存转换规则的数组 */
char map[MAX_MAP_SIZE];
/* 保存字符数组长度 */
int len = 0;
int maplen = 0;
int i = 0;
len = inputArray(buff);
if (len <= 0) {
puts("Cancelled");
} else if (len < 10) {
puts("Not enough 10 chars");
} else {
maplen = processArray(len, buff, map);
if (maplen >= 0) {
puts("转换结果:");
for (i = 0; i < len; i++) {
printf("%c ", buff[i]);
}
puts("");
puts("映射规则:");
for (i = 0; i < maplen; i++) {
printf("%c -> %d\n", map[i], i);
}
puts("");
} else if (maplen == -1) {
puts("Different Chars count is OverLimit of 10");
} else if (maplen <= -2) {
printf("Unexpected char %c\n", -maplen);
}
}
return 0;
}
执行结果:
第一题审题关键:题目中的转换规则就是字母在数组中第一次出现的顺序(顺序是从0开始自增)。
(注意数字是自增数(重复字母不自增,不同字母才自增),不是第一次出现的下标,因为题目有条件:使用数字的最大值等于转换规则的数目-1)
因此代码需要实现以下功能:
一、数组输入,需要有验证(验证输入的是否是小写,可嵌套循环判断)。
二、遍历数组,生成规则列表,可用数组char B[10]和int C[10]来记录规则,B数组是字母, C是该字母对应的顺序(顺序用变量自增)。比如数组char A[10]={a,b,a,d,e,f,d,g,j,k};循环每个元素,和数组B中对比,存在就跳过,不存在就将该字母记录到B中,并将顺序变量自增后记录到C数组对应的下标元素。
三、循环AB两个数组,通过对比,把数组A中元素替换成对应C中数字(或用新数组a来保存替换结果,原因见下)。
(注意:A是字符数组,c是整型数组,把数字直接存放到A,打印输出的时候按整型打印,实际就是把数字当成ASCII码保存了,打印数字就打印ASCII码。或者一开始就把A定义成字符串数组,最后把数字用ltoa()转换成字符串再存放到A中,打印用%s格式打印)。
如要接第二题整型a数组,这里可直接把对比结果保存在int a[10]数组中(和原数组A分开)。这样a可直接用于第二题。
第二题审题:根据上题的a内容定义n个数组(n=a中最大值-a中最小值+1),之后按照题目要求填充这几个数组,最后打印即可
技巧 1:不要使用“GOTO”语句
二十几年前,当计算机编程尚处于起步阶段时,程序流程是由“GOTO”语句来控制。该类语句允许程序员对当前代码行断行,而直接进入另一个不同的代码段。列表1为简单的示例。
列表1 使用GOTO语句
编程语言终究开始引入了函数的概念,即允许程序对代码进行断行。如果已经完成,不再使用goto语句来表示代码的断行。函数调用后,函数将回到下一条指令。列表2为示例。这一做法改善了程序结构,提高了可读性。自此,这被视为编写程序的正确方法。只要看到或想到goto语句,就会让软件工程师退缩,产生本能的厌恶。其中一个主要的原因是,一个遍布goto语句的程序会让让人很难抓住重心,不便于对程序的理解和维护。
列表2 用函数控制流程
技巧 2:使用FOR(;;)或While(1)
如果goto语句已经过时,那么对程序创建无限循环应该如何去做呢,这是一些硬件工程师可能会疑惑的问题。毕竟,之前都是通过创建一个goto语句然后再返回到main语句。解决这一问题就要利用C语言中已经存在的循环语句for和while(列表3和4)。
列表3 使用一个无限的For循环
列表4 使用一个无限的While循环
列表中的循环条件相对比较简单。for循环无非是以无条件情况使用条件语句。而另一方面,while循环是语句为真即予执行,这等同对任何条件的非零值。
技巧 3:使用合适的条件语句
除代码的可读性之外,程序的执行时间还主要依赖于做决定时所选择的条件结构类型。许多硬件工程师都熟悉简单的if语句的使用。然而,有时工程师可能没有意识到,如果第一个条件不正确,还可以使用else或else if语句。这可以节省处理器时间,而不必评估另一个条件语句。在列表5所示的前半部分代码中,如果Var值为1,则代码仍会查看Var是否为0。而在用了else语句的后半部分代码中,只评估第一个语句,之后就继续走下面的代码,这样就节省了时钟周期,使代码更加清晰。
列表5 用If/Else替代只用If
If/else if/else语句可能并不永远适用。如果需要检查若干个可能的条件,switch语句可能更合适。这样,处理器可以评估语句,然后从一个答案列表中选择下一步动作,而不用连续地评估一堆条件。列表6显示的例子与列表5示例的类型相同。
列表6 使用Switch语句
以上示例的寓意是,让条件语句的选择更开放,以选择出最适合的语句。这种做法使程序结构更简单,便于理解程序流程,缩短处理器的额外时钟周期。
技巧 4:避免使用汇编语言
微处理器的自然语言为汇编语言指令。为低级别机器语言编程可能会为处理器提供更高效的代码。然而,人类并不是天生就会这种语言,并且经验表明,编写汇编语言会造成误解。误解会导致维护不当,更甚者,可能会使系统到处是bug。一般建议避免使用汇编语言。实际上,现在大多数编译器都能编译出非常高效的代码。采用C语言或C++语言等高级语言的开发,能获得更有序的结构,便于理解和维护,使代码的整体效果更好。列表7给出了一个示例,比较了使一个32位变量递增所使用的汇编代码和C语言代码。
列表7 用汇编和C语言完成一个变量的递增
汇编C代码
当然,现在仍有一些场合适于使用汇编语言,但这种场合仍比较少。首个推荐的场合是开发引导装载程序。这种情况下,可能需要优化对启动过程中某个决策(启动应用或引导加载器)的速度。此时,分支判定用汇编代码就可能有意义了。另一种场合是开发一种在DSP上运行有严格时序要求的控制循环。为了从设备中的得到每个时钟周期,用汇编语言做控制循环的编码是有意义的。如果目前任务适合用汇编,应确保将其妥善存档便于有据可查,这样,未来的开发者(或未来的版本)会明白该代码的用途。
技巧 5:充分利用模块化
笔者最常见的经历是着手由硬件工程师开启的一个新项目往往是杂乱无章的代码组织。通常我们会发现,代码由一个单一的主模块组成,其中有2.5万多行代码。在这些应用中,一切都是全局性的,函数寥寥无几,goto语句贯穿整个代码结构。15年前这算正常,但如今已不再适用了!C语言编程使工程师能够将代码分成独立的功能模块,这简化了代码导航,同时还能够使工程师使用封装等面向对象技术。代码可以被组织成逻辑模块,这很有意义。虽然可能要先花点时间(几分钟),但从长远来看,这将能省掉很多漫长之夜,和很多调试之苦!
技巧 6:写千层饼式代码而非面条式代码
Beningo是一个意大利名字,和许多意大利人一样,我对意大利面食也是毫无保留地热爱。当拿意大利面食与软件相比时,我就会想到两种面食,即意大利面条和千层饼。意大利面条比较混乱,面条相互交织,纵横交错,结果完全没有任何类型的结构。编写非结构化代码就非常像意大利面条:咬一口,完全不知道吃的是哪部分。
另一种就是意大利千层饼!这种面食是分层的,是有结构的。分层开发的代码不仅更容易理解,还可以移走一层并添加一个新层,基本上能够实现重复使用和维护的简易性。图1为用千层饼式代码模型的一个简单软件模块示例。
图1 千层饼软件模型
驱动程序配置
应用程序配置
应用程序
驱动程序库
硬件
技巧 7:使用描述式变量名称
编写易于理解和维护的较大软件有许多障碍,其中之一就是变量的命名习惯。为了尽力缩短变量名,开发者通常会自创一些较短的、令人费解的助记符,往往只有他们自己才能明白的符号。现代语言使一个变量名可以包含数百个字符。为了让事情清晰明确,“直截了当”地方法要好于其它方式。因此,变量名一目了然不仅有利于开发人员,也有利于未来的维护团队。列表8给出一个示例。
列表8 变量的命名
技巧 8:少用#pragma语句
C语言中有一种特殊的#pragma语句。这些语句通常处理非标准的句法和特性,应尽可能避免使用这种语句,因为它们是非标准的,不能从一个处理器移植到另一个处理器。有些编译器可能要求用这类语句完成某项任务,例如定义一个中断服务程序。在这种情况下,可能除了使用#pragma语句以外别无它法。如果可能,将所有的#pragma语句放在一个模块或几个模块里。这有助于确保在代码移植时,只需要更新几处代码,而非整个代码库;此外,这也将有助于防止移植代码的首次编译所带来的困扰。
技巧 9:错误往往并不是看上去那样简单
在调试一个C程序时,有一个让人当心的陷阱就是编译器错误。由于编译器的复杂性,当检测到一个错误时,通常错误位于程序中的其它地方,而非编译器所指示的位置。这主要与编译器生成程序的步骤有关。错误类型通常是一致的,工程师可以发现的一些错误中,90%都是根源:
·当心漏掉#include文件。这可能会使程序开发人员看到完美的代码行,但由于未包含必要的头文件,编译器便会将其标志为一个错误,表示有些东西未定义。
·当心漏掉分号。编写C代码时最常见的错误是忘记在句末加分号。
·当心漏掉括号。漏写括号是代码编写过程中又一常犯的错误,或是粗心漏掉,或是由于键入错误而产生一个错误字符。
·当心漏掉逗号。在复杂的定义中很容易忘记逗号!
一般情况下,当弹出一个奇怪的编译错误对话框时,要查看该行前已被编译的内容。很有可能就是错误所在!它可能是出现在一行上面,或中间部分,或在完全不同的文件里。
不要放弃!只要具备一定的经验,解决这些疑难问题就会成为一种第二天性。
技巧 10:优秀的程序员编写的代码行数不一定少
人们常有这种误解,即认为较一般的程序员而言,一个优秀的程序员往往写较少的代码行就能解决问题。不要卷入这一错误的想法!一个优秀的程序员通常具备思维缜密、结构清晰的编码基础。变量命名和封装都恰如其分,系统中几乎不用全局变量。函数应保持简短有效。如果代码看起来很混乱,需要多写几行才能使其看上去更清晰,那就不妨多写几行!可以上网查看获得C代码编写最混乱殊荣奖项的代码用作前车之鉴。优秀程序员写的代码简洁、易于理解和维护,代码行数并非最少!