【C语言】语言篇——数组和字符串

简介: 【C语言】语言篇——数组和字符串

逆序输出

读入一些整数,逆序输出到一行中。已知整数不超过100个。

#include <stdio.h>
#include <string.h>
int maxn[105];//若数组较长则将其设为全局变量
int main (void)
{
    int i=0,x,k;
    memset(maxn,0,sizeof(maxn));//将数组清零
    while((scanf("%d",&x))==1) 
        maxn[i++]=x;//先将x赋值给maxn[i],之后i++
    for (k=i-1;k>=0;k--)
    printf("%d ",maxn[k]);
    return 0;   
}

scanf()函数返回值

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

如何告诉程序输入结束了呢?

是按Enter/空格/TAB键吗?我们会发现,按Enter/空格/TAB键,并没有反应,这是因为程序正在等待输入。

在Windows下,输入完毕后先按Enter键,再按Ctrl+Z键,最后再按Enter键。在Linux下,输入完毕后按Ctrl+D键即可结束输入。

大数组需要定义在函数体外的原因

大数组需要定义在函数体外的原因,全局变量在静态存储区内分配内存,而局部变量是在栈内分配内存空间的。C语言编写的程序会在运行期间创建一个堆栈段,用来保存函数的调用关系和局部变量。而在main函数内部定义大数组相当于在栈内需要一个很大的空间,会造成栈的溢出。

因此,当我们需要定义一个极大的数组时,最好在main 函数外部定义这个大数组。

开灯问题

有n盏灯,编号为1~n。第1个人把所有灯打开,第2个人按下所有编号为2的倍数的开关(这些灯将被关掉),第3个人按下所有编号为3的倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭)依此类推。一共有k个人,问最后有哪些灯开着?输入n和k,输出开着的灯的编号。k≤n≤1000。

样例输入:

7 3

样例输出:

1 5 6 7

分析:用a[1],a[2],a[3]……a[n]表示编号为1,2,3……n的灯是否开着。

#include <stdio.h>
#include <string.h>
int a[1000];
int main (void)
{
    int n,k,i,j;
    scanf("%d%d",&n,&k);
    memset(a,0,sizeof(a));//数组清零
    //0和1表示开灯和关灯两种状态
    for(i=1;i<=k;i++)//人
        for (j=1;j<=n;j++)//灯
            if (j%i==0)
            {
                if (a[j]==0)
                a[j]=1;
                else
                a[j]=0;
            }
    for(i=1;i<=n;i++)
    if (a[i]==1)
    printf("%d ",i);
    printf("\n");
    return 0;
}

0表示灯关的状态,1表示灯开的状态。从第一个人开始,判断是否是1~n编号的倍数,如果是的话,改变灯的状态,即原来是1,变为0;原来是0,变为1。

回文字

输入一个字符串,判断它是否为回文串以及镜像串输入字符串保证不含数字0。所谓回文串,就是反转以后和原串相同,如abba和madam。所有镜像串,就是左右镜像之后和原串相同,如2S和3AIAE。注意,并不是每个字符在镜像之后都能得到一个合法字符。在本题中,每个字符的镜像如图所示(空白项表示该字符镜像后不能得到一个合法字符)。


输入的每行包含一个字符串(保证只有上述字符。不含空白字符),判断它是否为回文

串和镜像串(共4种组合)。每组数据之后输出一个空行。

样例输入:

NOTAPALINDROME

ISAPALINILAPASI

2A3MEAS

ATOYOTA

样例输出:

NOTAPALINDROME – is not a palindrome.

ISAPALINILAPASI – is a regular palindrome.

2A3MEAS – is a mirrored string.

ATOYOTA – is a mirrored palindrome.

分析过程可以看这篇:【C语言】经典题目二

下面是代码实现:

#include<stdio.h>
#include<string.h>
int main (void)
{
    char s[128]={0};
    int i,j,k,n;
    int flagH=1,flagJ=1;
    const char a[80]="AEHIJLMOSTUVWXYZ12358";
    const char b[80]="A3HILJMO2TUVWXY51SEZ8";
    gets(s);
    n=strlen(s);
    //判断是否是回文串
    for(i=0,k=n-1;i<k;i++,k--)
    {
        if (s[i]!=s[k])
        {
            flagH=0;
            break;
        }
    }
    //判断是否是镜像串
    for(i=0,k=n-1;i<k;i++,j--)
    {
        for (j=0;s[j]!=0;j++)
        {
            if (s[i]==a[j])
            {
                if (s[k]!=b[j])
                {
                    flagJ=0;
                }
            }
        }
        if (flagJ==0) break;
    }
    //根据标志量判断
    if(flagH==1 && flagJ==1)
    printf("%s -- is a mirrored palindrome.",s);
    else if (flagH==1 &&flagJ==0)
    printf(" %s-- is a regular palindrome.",s);
    else if (flagH==0 && flagJ==1)
    printf("%s -- is a mirrored string.",s);
    else 
    printf("%s-- is not a palindrome.",s);
    return 0;
}

习题

得分(score)

给出一个由O和X组成的串(长度为1~80),统计得分。每个O的得分为目前连续出现

的O的个数,X的得分为0。例如,OOXXOXXOOO的得分为1+2+0+0+1+0+0+1+2+3。

分析:对键盘输入的长串的每个字符进行判断。设置总和tot=0,若是X,则tot不累加。若是O,则需要判断前面O字符连续出现的个数。

#include<stdio.h>
int main (void)
{
    char a[80]={0};
    int tot=0,i,k;
    gets(a);
    for(i=0;a[i]!='\0';i++)
    {
        if (a[i]=='O')
        {
            tot+=1;
            for (k=i-1;k>=0;k--)
            {
                if (a[k]!='O') break;
                else tot+=1;
            }
        }
    }
    printf("%d\n",tot);
    return 0;
}

分子量(Molar Mass)

给出一种物质的分子式(不带括号),求分子量。本题中的分子式只包含4种原子,分

别为C, H, O, N,原子量分别为12.01, 1.008, 16.00, 14.01(单位:g/mol)。例如,C6H5OH的

分子量为94.108g/mol。

分析:对输入的分子式的每个元素进行循环,其中会有数字和’C’,‘H’,‘O’,'N’四个字母,而我们只对四个字母进行判断。若是的话,看其后的一位是否为数字,若是,需要此原子量×数字,若不是,只需加它本身就可以了。

这里需要解决的是,如果输入的字符数组a中的元素等于’C’或’H’或’O’或’N’,如何利用其对应的原子量?

这里用结构体解决比较方便。可以将’C‘与其原子量12.01进行“捆绑”,将’H‘与其原子量1.008进行“捆绑”,将’O‘与其原子量16.00进行“捆绑”,将’N‘与其原子量14.01进行捆绑。

#include<stdio.h>
struct Molar
{
    char atom;
    double mass;
};
int main (void)
{
    char a[30]={0};
    int i,k;
    double tot=0;
    gets(a);
    struct Molar b[4]={'C',12.01,'H',1.008,'O',16.00,'N',14.01};
    for (i=0;a[i]!='\0';i++)//遍历输入的a字符数组
    {
        for (k=0;k<=3;k++)
        if(a[i]==b[k].atom)
        {
            if (a[i+1]>='1' && a[i+1]<='9')//判断字符其后一位是否为数字
            tot+=b[k].mass * (a[i+1]-'0');//注意分子式中的数字都是以字符型存入字符数组的
            else
            tot+=b[k].mass;
        }
    }
    printf("%lf \n",tot);
    return 0;
}

数数字(Digit Counting)

把前n(n≤10000)个整数顺次写在一起:123456789101112…数一数0~9各出现多少次(输出10个整数,分别是0,1,…,9出现的次数)。

分析:利用sprintf()函数将1~10000输出到字符数组中。存入完毕后,对字符数组进行遍历即可。

关于memset()函数

memset()函数初始化是以一个字节为单位的,也就是说,对字符数组才能初始化为任意值。

memset()函数可以很方便将字符数组全部数组全部初始化为某一个值,例如:

c char a[20]; memset(a,'\0',sizeof(a))

将字符数组亲空。

#include<stdio.h>
#include<string.h>
char a[100000];
int main (void)
{
    int k=0,i;
    int b[10]={0};//用于记录1~9出现的次数
    char x;//记从键盘读入的数字字符
    memset(a,'\0',sizeof(a));//将字符数组清零
    scanf("%s",a);
    //对数组a进行遍历
    for(k=0;a[k]!='\0';k++)
    b[a[k]-'0']++;
    //输出数组b
    for(k=0;k<=9;k++)
    printf("%d ",b[k]);
    return 0;
}

周期串 (Periodic Strings)

如果一个字符串可以由某个长度为k的字符串重复多次得到,则称该串以k为周期。例如,abcabcabcabc以3为周期(注意,它也以6和12为周期)。

输入一个长度不超过80的字符串,输出其最小周期。

分析:对于一个字符串,其周期一定是字符串长度的因子,因此只对因子判断即可。

如何判断是否是周期呢?例如3为一个周期,则一定满足a[0]=a[0+3],a[1]=a[1+3],a[2]=a[2+3];

但是要做判断的话,满足的如上条件是仅仅不够的。例如会出现abcabcdddabcabcddd这种情况。所以我们需要对整个字符串进行判断。

#include <stdio.h>
#include <string.h>
int main (void)
{
    int i,k,len,n;
    int flag;  //设置标志量
    char s[80];
    gets(s);
    len=strlen(s);  
    for(i=1;i<=len;i++)
    {
        if (len%i==0)  //判断是否是因子
        //若是,判断能否为周期
        {
            flag=1;
            for (k=0;k<i;k++)
            {
                for (n=1;n<=len/i-1;n++)
                {
                    if (s[k]!=s[k+n*i]) flag=0;                   
                }
            }
        }
        if (flag==1)
        {
            printf("%d\n",i);
            break;
        }
    }
    return 0;   
}

DNA序列(DNA Consensus String)

输入m个长度均为n的DNA序列,求一个DNA序列,到所有序列的总Hamming距离尽量

小。两个等长字符串的Hamming距离等于字符不同的位置个数,例如,ACGTGCGAHamming距离为2(左数第1, 4个字符不同)。

输入整数m和n(4≤m≤50, 4≤n≤1000),以及m个长度为n的DNA序列(只包含字母A,C,G,T),输出到m个序列的Hamming距离和最小的DNA序列和对应的距离。如有多

解,要求为字典序最小的解。例如,对于下面5个DNA序列,最优解为TAAGATAC。

TATGATAC

TAAGCTAC

AAAGATCC

TGAGATAC

TAAGATGT

后来发现的问题:都错题了!!!求一个DAN序列,这是重点!

我的问题是从输入的DAN序列中找DAN序列,使得到所有的序列的总Hanming距离最小。

如果把题目改成这样的基础版,下面是分析过程。

分析:

首先是要处理m个DAN序列的储存,我们可以利用二维数组进行存储(因为二维数组可以看作是很多个一维数组组成。)

其次是如何如何找出最优解。其实就是,第一个序列和剩下的所有的序列进行比较,然后出现不同的字符,……(这个和我们熟悉的选择排序是不一样的,因为选择排序是和它后面的每一个数字进行比较,而这个是和所有的进行比较。)

那么如何储存每个序列总Hamming距离呢?不妨可以放在一个数组中用来存放。

那么如何输出呢?我们需要边存储边记录,设置变量min,若在每一次的比较中,有总HAMMING距离小于min,那么将此赋值给min,并将此时的DAN序列拷贝到字符数组s中。最终输出s即可。

这里需要注意的是,题目要求如有多解,要求为字典序最小的解。所以我们可以试着从最后一个DAN序列开始循环。

#include<stdio.h>
#include<string.h>
int main(void)
{
    char a[50][1000];  //用来存储DAN序列,保证长度足够长
    char s[1000]; //用来存储最优DAN序列
    int b[50]={0}; //记录每个DAN序列的Hamming距离值
    int i=0,k,j;
    int m,n;//m行n列
    scanf("%d%d",&m,&n);
    int min=(m-1)*n; //min一定小于等于(m-1)*n 完成初始化
    for (i=0;i<m;i++)
    scanf("%s",&a[i]);//输入m个DAN序列
    for(k=m-1;k>=0;k--) //从最后一行开始
    {
        for(i=0;i<m;i++)
        {
            for(j=0;j<n;j++)
            {
                if (a[k][j]!=a[i][j])
                b[k]++;
            }
        }
        if(b[k]<=min)
        {
            min=b[k];
            strcpy(s,a[k]);
        }  
    }
    //输出
    //输出m个序列的Hamming值
    for (j=0;j<m;j++)
    printf("%d ",b[j]);
    printf("\n");
    //输出最小的DAN序列及对应的值
    printf("%s\n%d",s,min);
    return 0;   
}

运行结构如下:

思考

题目1(必要的存储量):

数组可以用来保存很多数据,但在一些情况下,并不需要把数据保存下来。下面哪些题目可以不借助数组,哪些必须借助数组?请编程实现。假设输入只能读一遍。


  1. 输入一些数,统计个数。
  2. 输入一些数,求最大值、最小值和平均数。
  3. 输入一些数,哪两个数最接近。
  4. 输入一些数,求第二大的值。
  5. 输入一些数,求它们的方差。

1,输入一些数,统计个数。

显然不需要将数据存入数组中,我们只需要设置一个变量作为累加器即可。

如下:

#include <stdio.h>  int main (void) {
    int count=0;
    double x;
    while (scanf("%lf",x)==1)
    count++;
    printf("%d\n",count);  } 

2,输入一些数,求最大值、最小值和平均数。

第二个可以利用数组实现,但也可以在循环中实现。

定义变量max,和min用来存放最大值和最小值。假设第一个输入的数字既是最大值也是最小值(即先完成初始化),然后后面每输入一个数,都和最大值和最小值比较,若比最大值大,则将此数赋值给max,对于min同理。

那么如何求平均值呢?可以定义和变量sum,每输入一个数,就将此数加到sum中,然后最后sum除以输入的数的总数即可。

#include<stdio.h> 
int main (void)  
{
    int count=1;
    double max,min,sum,x,ave;
    scanf("%lf",&x);
    min=max=x;
    sum=x;
    while(scanf("%lf",&x)==1)
    {
        count+=1;
        if (x>=max)
        {
            max=x;
        }
        if (x<=min)
        {
            min=x;
        }
        sum+=x;
    }
    ave=sum/count;
    printf("max=%.2lf,min=%.2lf,average=%.2lf\n",max,min,ave);
    return 0;   
} 

3,输入一些数,哪两个数最接近。(假设输入的都是整数)

我的想法是,将输入的数字从小到大排序,然后相邻的两个数之间相减,即可,将差值放到变量min中。利用循环,依次判断两个数的差值,若小于min,将将此差值 赋值给min。

对于数字的排序,可以利用选择排序法和起泡排序法。

#include<stdio.h>
#include<string.h>  int maxn[10000];//保证数组足够大  int main (void)   {
    int k=0,i,x;//x用来存放每次输入的值
    int count=0;//累加器
    int tmp;//设置中间变量
    int min;//存放差值的最小值
    memset(maxn,0,sizeof(maxn));//数组初始化为0;
    //输入一些数字
    while(scanf("%d",&x)==1)
    {
        maxn[k++]=x;
        count++;
    }
    //输入完毕,对输入的数字排序
    //下采用选择法进行排序
    for(i=0;i<count;i++)
    {
        for (k=i+1;k<count;k++)
        {
            if (maxn[k]<maxn[i]) 
            //若后面的小于它,则利用中间变量交换
            {
                tmp=maxn[i];
                maxn[i]=maxn[k];
                maxn[k]=tmp;
            }
        }
    }
    min=maxn[1]-maxn[0];//初始化
    for(i=1;i<count-1;i++)
    {
        if(maxn[i+1]-maxn[i]<min)
        min=maxn[i+1]-maxn[i];
    }
    printf("%d\n",min);
    return 0; 
    } 

运行结果:

4.输入一些数,求第二大的值。

对于这个题目,可以只借助两个变量来实现。一个变量存放当前输入的整数中的最大值,一个变量存放当前输入的整数中的第二大值。

#include<stdio.h>
int main(void)
{
    int maxF,maxS,x;
    scanf("d",&x);
    maxF=maxS=x;//初始化
    while(scanf("%d",&x)==1)
    {
        if(x>maxF)
        {
            maxS=maxF;  //易出错
            maxF=x;1
        }
        else if(x>maxS && x<maxF)
        maxS=x;
    }
    printf("%d\n",maxS);
    return 0;
}

5. 输入一些数,求它们的方差。

求方差,需要先求出来平均值。其次将各项与平均值作差并且平方相加,再除以总数。我们需要利用输入的每个数,所以需要利用数组,将数据保存。

#include <stdio.h>
#include <string.h>
int maxn[1000];//定义足够大的数组
int main (void)
{
    memset(maxn,0,sizeof(maxn));//数组初始化为0
    int x,sum=0,k=0,count=0,i;
    double ave,var=0;//平均值和方差
    //输入一些数
    while(scanf("%d",&x)==1)
    {
        maxn[k++]=x;
        sum+=x;
        count++;
    }
    //计算平均值
    ave=(double)sum/count;//注意强制转换sum
    //计算方差
    for (i=0;i<count;i++)
    {
        var+=(maxn[i]-ave)*(maxn[i]-ave);
    }
    var=var/count;
    printf("%.2lf",var);
    return 0;
}


相关文章
|
1月前
|
存储 C语言 C++
【C语言数组】
【C语言数组】
|
16天前
|
存储 编译器 C语言
【C语言基础考研向】09 一维数组
数组是一种有序集合,用于存储相同类型的数据,便于统一操作与管理。例如,将衣柜底层划分为10个格子存放鞋子,便于快速定位。在C语言中,数组定义格式为 `类型说明符数组名[常量表达式];`,如 `int a[10];` 表示定义了一个包含10个整数的数组。数组初始化时可以直接赋值,也可以部分赋值,且数组长度必须固定。数组在内存中连续存储,访问时需注意下标范围,避免越界导致数据异常。数组作为参数传递时,传递的是首地址,修改会影响原数组。
|
16天前
|
存储 C语言
【C语言基础考研向】10 字符数组初始化及传递和scanf 读取字符串
本文介绍了C语言中字符数组的初始化方法及其在函数间传递的注意事项。字符数组初始化有两种方式:逐个字符赋值或整体初始化字符串。实际工作中常用后者,如`char c[10]=&quot;hello&quot;`。示例代码展示了如何初始化及传递字符数组,并解释了为何未正确添加结束符`\0`会导致乱码。此外,还讨论了`scanf`函数读取字符串时忽略空格和回车的特点。
|
16天前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
|
19天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
19天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第六章 数组_一维数组_二维数组_字符数组详解
本章介绍了C语言中的数组概念及应用。数组是一种存储同一类型数据的线性结构,通过下标访问元素。一维数组定义需指定长度,如`int a[10]`,并遵循命名规则。数组元素初始化可使用 `{}`,多余初值补0,少则随机。二维数组扩展了维度,定义形式为`int a[3][4]`,按行优先顺序存储。字符数组用于存储字符串,初始化时需添加结束符`\0`。此外,介绍了字符串处理函数,如`strcat()`、`strcpy()`、`strcmp()` 和 `strlen()`,用于拼接、复制、比较和计算字符串长度。
|
24天前
|
C语言
C语言 字符串操作函数
本文档详细介绍了多个常用的字符串操作函数,包括 `strlen`、`strcpy`、`strncpy`、`strcat`、`strncat`、`strcmp`、`strncpy`、`sprintf`、`itoa`、`strchr`、`strspn`、`strcspn`、`strstr` 和 `strtok`。每个函数均提供了语法说明、参数解释、返回值描述及示例代码。此外,还给出了部分函数的自实现版本,帮助读者深入理解其工作原理。通过这些函数,可以轻松地进行字符串长度计算、复制、连接、比较等操作。
|
1月前
|
算法 C语言
C语言------数组
这篇文章是关于C语言数组的实训,包括一维数组、二维数组和字符数组的定义、赋值、输入、输出方法,并通过实例代码演示了数组的使用和一些基本算法,如冒泡排序。
C语言------数组
|
1月前
|
存储 编译器 程序员
七:《初学C语言》— 数组
【8月更文挑战第2天】本篇文章详细讲解了一维数组和二维数组的创建、使用和初始化及如何使用sizeof()计算数组中的元素个数。并附带了多个教学源码及代码练习
43 1
七:《初学C语言》— 数组
|
1月前
|
存储 编译器 数据处理
【编程秘籍】解锁C语言数组的奥秘:从零开始,深入浅出,带你领略数组的魅力与实战技巧!
【8月更文挑战第22天】数组是C语言中存储同类型元素的基本结构。本文从定义出发,详述数组声明、初始化与访问。示例展示如何声明如`int numbers[5];`的数组,并通过下标访问元素。初始化可在声明时进行,如`int numbers[] = {1,2,3,4,5};`,编译器自动计算大小。初始化时未指定的元素默认为0。通过循环可遍历数组,数组名视为指向首元素的指针,方便传递给函数。多维数组表示矩阵,如`int matrix[3][4];`。动态数组利用`malloc()`分配内存,需用`free()`释放以避免内存泄漏。掌握这些技巧是高效数据处理的基础。
55 2