《算法入门·C语言》

1.程序设计入门

我们的目标是解决问题,而不是为了写程序而写程序,同时应保持简单 (Keep It Simple and Stupid,KISS),而不是自己创造条件去展示编程技巧。

算法竞赛是在比谁能更好地解决问题,而不是在比谁写的程序看上去更高级。

2.循环结构程序设计

只编写少量语句,就让计算机做大量的工作

2.1 for循环

①n是英文单词newline(换行)的首字母。

换句话 说,在输出的最后加一个\n会在输出结束后换行。问题出现了:如果 真的要输出斜线“\”和字符n,怎么办?方法是“printf("\n");”,编译器会把双斜线“\”理解 成单个字符“\”。

②建议尽量缩短变量的定义范围

例如,在for循环的初始化部分定义循环变量。

③continue是指跳回for循环的开始,执行调整语 句并判断循环条件(即“直接进行下一次循环”),而break是指直接跳出循环

2.2 while循环

clock函数

功 能: 返回处理器调用某个进程或函数所花费的时间。
用 法: clock_t clock(void);
说 明:clock_t其实就是long,即长整形。该函数返回值是硬件滴答数,要换算成秒或者毫秒,需要除以CLOCKS_PER_SEC
返回值:为1毫秒,就是0.001秒。
注 意:程序所占用处理器的“执行时间”。也就是说,当处于内核态或调度到其他程序时,这个时间就会暂停。比如说调用了sleep(),usleep()等会引发调度函数或者内核抢占了cpu都会暂停。
在这里插入图片描述
在这里插入图片描述
②循环的代价
算术运算溢出和程序效率低下

③count++的作用是计数器。

由于最终输出的是变换的次数,需要一个变量来完成计数。当需要统计某种事物的个数时,可以用一个变量来充当计数器

④在观察无法找出错误时,可以用“输出中间结果”的方法查错。

在给n做变换的语句后加一条输出语句printf("%d\n",n),将很快找到问题的所在:第 一次输出为-1332004332,它不大于1,所以循环终止。如果认真完成了前面的所有探索实 验,读者将立刻明白这其中的缘由:乘法溢出了。

int为32位整数

范围是是-2147483648~2147483647,即-231~231-1

C99中新增的long long,

其范围是-263~263-1,,唯一的区别就是要把输入时 的%d改成%lld,long long在Linux下的输入输出格式符为%lld,但Windows平台中有时 为%I64d。为保险起见,可以用后面介绍的C++流,或者编写自定义输入输出函数

⑥因为只要末6 位,所以输出时对106取模。

2.3 输入输出框架

①数据统计

while(scanf("%d",&x)==1)
{
.....
}

结束方法
Windows:Enter -> Ctrl+Z
Linux:Ctrl+D
·变量在未赋值之前是不确定的 int a,a可以为任意值
·输入输出重定向:在main函数的入口处加
·在使用之前赋初值。由于min保存的是最小值,其初值应该是 一个很大的数;反过来,max的初值应该是一个很小的数。一种方法是定义一个很大的常 数,如INF=1000000000,然后让max=-INF,而min=INF,
在这里插入图片描述

freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);

注意 、看要求,赛前读文件,不加绝对或相对路径

②数据统计(重定向版&文件版 )

scanf函数返回的是成功输入的变量个数,当输入结束时,scanf函数无法再次读取x,将返回0。

#define LOCAL
#include<stdio.h>
#define INF 1000000000
int main()
{
#ifdef LOCAL 
    freopen("data.in", "r",stdin);
    freopen("data.out", "w", stdout);
#endif
    int x, n = 0, min = INF, max = -INF, s = 0;
    while(scanf("%d", &x) == 1)
    {
       .....     /*
     printf("x = %d, min =%d, max = %d\n", x,min,max);*/
 }
    printf("%d %d %.3f\n",min,max,(double)s/n);
    return 0;
}

1.用/**/将现在用不到但以后会用到的语句保存
2.删除#define LOCAL即可取消重定向
3.如果比赛要求用文件输入输出但不能用重定向方式
在这里插入图片描述

重定向

方法写起来简单、自然,但是不能同时读 写文件和标准输入输出;

fopen的写法

稍显繁琐,但是灵活性比较大(例如,可以反复打开 并读写文件)。顺便说一句,如果想把fopen版的程序改成读写标准输入输出,只需赋值“fin =stdin;fout=stdout;”即可,不要调用fopen和fclose

③包含多组输入输出数据,n=0时结束程序

int s=0,kase=0;
while(scanf("%d",&n)==1&&n)
{
    int s = 0;
    for(int i=;i<n;i++){
        scanf("%d",&x);
        ....
    }
    if(kase)printf("\n");
    printf("...");
    ++kase;
}

·判断scanf的返回值:鲁棒性(robustness)是在数据有错误的情况下也可以输出正确结果
·算完一组数后要重置

别人的算法理解得再好,遇到问题时还是需要自己分析和设计。

再次强调:编程不是看书看会的,也不是听课听会的,而是练会的。本章后面的上机编 程习题中包含了很多正文中没有提到的内容,对能力的提高很有好处。如有可能,请在上机 实践时运用输出中间结果、设计伪代码、计时测试等方法。

自己就缺少实践能力,要改正

3.数组和字符串

3 .1 数组

①a[n++]=x 等于 a[n]=x;n++;

在这里插入图片描述

②比较大的数组应声明在main函数外

C语言的数组并不是“一等公民”,而是“受歧视”的。例如,数组不能够进行赋值操作: 在程序3-1中,如果声明的是“int a[maxn],b[maxn]”,是不能赋值b=a的。如果要从数组a复 制k个元素到数组b,可以这样做:memcpy(b,a,sizeof(int)*k)。当然,如果数组a和b 都是浮点型的,复制时要写成“memcpy(b,a,sizeof(double)*k)”。另外需要注意的是, 使用memcpy函数要包含头文件string.h。如果需要把数组a全部复制到数组b中,可以写得简单 一些:memcpy(b,a,sizeof(a))。

③蛇形填数

在这里插入图片描述

④乘法溢出:在n特别大时如果直接n*n就会溢出,所以只能连除两次

int n,x,y,tot=0;
scanf("%d",&n);
memset(a,0,sizeof(a));
tot=a[x=0][y=n-1]=1;
while(tot<n*n)
{
   if(x+1<n && !a[x+1][y])
        a[++x][y]=++tot;
   ...
}

C语言的简洁优势:在一个语句里合并完成一些事

3.2 字符串

①scanf(“%s”,s);读入一个不含空格、tab、回车的字符串,存入字符数组s中,无&
②scanf(“%s”,s[i]);读取char s[n】[m]中第i个字符串
③竖式问题

找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中, 所有数字都属于一个特定的数字集合。
在这里插入图片描述
1.运行结果**
2357
<1>
… 775
. . . 33
————
. 2325
2325.
————
25575
the number of solutions=1

2.strchr函数

char * strchr(char* _Str,int _Ch)
功 能:查找字符串s中首次出现字符c的位置
返 回:返回首次出现c的位置的指针,返回的地址是字符串在内存中随机分配的地址再加上你所搜索的字符在字符串位置,如果s中不存在c则返回NULL。##

3.sprintf函数

功能:把格式化的数据写入指定的字符串缓存区中(输出到字符串)
原型:int sprintf( char *buffer, const char *format, [ argument] … );
参数:buffer:char型指针,指向字符串缓存区
format:想要将参数转换成的格式
[ argument] …:任何类型的数据
返回值:返回转换完成后字符串的长度,不包括‘\0’

4.末尾加“\0”

C语言中的字符串是以“\0”结尾的字符数组,可以用strlen(s)返回字符串s中
结束标记之前的字符个数。字符串中的各个字符是s[0], s[1],…,s[strlen(s)-1]。
由于字符串的本质是数组,它也不是“一等公民”,只能用strcpy(a, b), strcmp(a, b), strcat(a, b)来执行“赋值”、“比较”和“连接”操作,而不能用“=”、“==”、 “<=”、“+”等运算符。上述函数都在string.h中声明。

char  buffer[200], s[] = "computer", c = 'l';   
int   i = 35, j;   
float fp = 1.7320534f;     
  j  = sprintf( buffer,    "   String:    %s\n", s );
  j += sprintf( buffer + j, "   Character: %c\n", c );  
  j += sprintf( buffer + j, "   Integer:   %d\n", i ); 
  j += sprintf( buffer + j, "   Real:      %f\n", fp );    
  printf( "Output:\n%s\ncharacter count = %d\n", buffer, j );

输出结果:
Output:
String: computer
Character: l
Integer: 35
Real: 1.732053
character count = 79

3.3 相关题目

①Tex中的引号

//简洁表达

int c,q=1;
    while((c=getchar())!=EOF)
    {
        if(c=='"')
        {
            printf("%s",q?"``":"..");
            q=!q;
        }
        else printf("%c",c);
    }

输出

"it is good!" i said"yse"haha
``it is good!.. i said``yse..haha

1.fgetc(fin)函数
功 能:打开文件fin,读取一个字符,返回一个int值
语 法:int fgetc(FILE *stream)
内容说明:果到了文件的结尾或遇到读错误,将返回EOF。由于EOF是一个有效的整型值,当你操作二进制文件时,必须用feof函数进行文件结束检测。同样也必须使用ferror函数进行出错检查。getchar=fgerc(stdin)
2.fgets(buf,maxn,fin)
功 能:读取完整的一行。如果出现\n读取工作结束,回车符成为buf字符串中最后一个有效字符,再往后就是\0了。
buf的声明为char buf[maxn]。这个函数读取不超过maxn-1个字符,然后在末尾添上结束符“\0”,
返 回:当一个字符都没有遇到,返回NULL
对 比:gets函数没有指明读取的最大字符数,不管是否储存的下,gets将一直往字符串中读取内容 ,存在缓冲区溢出漏洞,所以不要使用

②键盘往右移

char s[]="1234567890-=QWERTYUIOP[]\\ASDFGHJKL;'ZXCVBNM,./";
    int i,c;
    while((c=getchar())!=EOF)
    {
        for(i=1;s[i]&&s[i]!=c;i++);
            if(s[i]) putchar(s[i-1]);
        else putchar(c);
    }

输出

GOHJY JSJS MOHJY
FIGHT HAHA NIGHT

1. for循环直接加;表示找到对应的i
2. 善用常量数组往往能化简代码,比如说当你想写很多switch和case时

③回文词

在这里插入图片描述
在这里插入图片描述

不说人家写的就是好啊,有许多值得学习的地方,好好学习吧!
1.ctype.h
用 途:测试字符是否属于特定的字符类别,如字母字符、控制字符等等
isalpha函数:判断字符ch是否为英文字母,若为英文字母,返回非0(小写字母为2,大写字母为1)。若不是字母,返回0。
**toupper\tolower用来转换大小写
2.rev的空格其实是表示那些不符合条件的字母
3.msg最后用双变量的判断方法大大减少了代码的复杂度,不见不知道
4.len+1那个,无论len是奇是偶都嫩得出正确的结果,很厉害啊

④生成元

题 目:如果x加上x的各个数字之和得到y,就说x是y的生成元。给出n(1≤n≤100000),求最小生成元。无解输出0。
例如,n=216,121,2005时的解分别为198,0,197
分 析:逆向思维,先由x得到y,写成一个数组,再由输入的y得到相应的x(打表)
X: 1 2 …10 11 12 …20 21 22
Y: 2 4 …11 13 15 …22 24 26
提交一次就AC,嘿嘿嘿,开心o( ̄▽ ̄)ブ

⑤环状序列

const int maxn=110;
int less(char *s,int p,int q)
{int i;
    int len=strlen(s);
    for(i=0;i<len;i++){
        if(s[(p+i)%len]!=s[(q+i)%len])
            return s[(p+i)%len]<s[(q+i)%len];
    }
    return 0;
}
int main(){
    int T,i,ans,len;
    char s[maxn];
    scanf("%d",&T);
    while(T--){
        ans=0;
        scanf("%s",s);
        len=strlen(s);
        for(i=0;i<len;i++){
            if(less(s,i,ans)){
                ans=i;
            }
        }
        for(i=0;i<len;i++)
            putchar(s[(ans+i)%len]);
        putchar('\n');
    }
    return 0;
}

输出

1
TGBRWAARTFCASW
AARTFCASWTGBRW

1.(p+i)%len为典型的环形表达:0,1,2,3;1,2,3,0;2,3,0,1等,i从0到1,p决定了开头第一个数字
2.return a>b 如果成立则为1,反之为0

⑥进位制与整数表示

1.用ASC编码表示字
prinf("%d %o %x",a);十进制,八进制,十六进制
2.进制转换与移位运算符
×2 也表示为 <<1
计算机里的数是用补码形式储存的:下列的输出都是由补码表示的
b=-1;
printf("%d %x\n",b,b); -1的原码为0x80000001,但是输出为ffffffff
b=0x8fffffff;
printf("%d %x\n",b,b); 0x8fffffff对应的十进制为-268435455(第一位为符号位),输出为-1879048193,对应-0x70000001
b=0x80000001;
printf("%d %x\n",b,b); 0x80000001对应的十进制为-1,输出为-2147483647,对应-0x7FFFFFFF
b=0x80000002;
printf("%d %x\n",b,b); 0x80000002对应的十进制为-2,输出为-2147483646,对应-0x7FFFFFFE
printf("%u",-1); 输出为…即-n的内部表示为232-n
在这里插入图片描述
在这里插入图片描述

⑦猜数字的游戏提示

实现一个经典"猜数字"游戏。给定答案序列和用户猜的序列,统计有多少数字位置正确 (A),有多少数字在两个序列都出现过但位置不对(B)。
输入包含多组数据。每组输入第一行为序列长度n,第二行是答案序列,接下来是若干 猜测序列。猜测序列全0时该组数据结束。n=0时输入结束。
在这里插入图片描述
**1.runtime error的原因:**数组开的太小了
2.for可以用来无限循环
3.思路要学习

总结

**1.字符串结尾:**本章中只讲了gets,但 其实strcpy也有类似问题——如果源字符串并不是以“\0”结尾的,复制工作将可能覆盖到缓冲 区之外的内存。这也提醒我们:如果按照自己的方式处理字符串,千万要保证它以“\0”结 尾。

发布了1 篇原创文章 · 获赞 0 · 访问量 103

猜你喜欢

转载自blog.csdn.net/weixin_45797295/article/details/104097935