C/C++ 第六讲 二维数组、字符数组

6.1 二维数组的定义和初始化

定义

1
变量名 数组名[常量表达式1][常量表达式2];
  • 表达式1代表行,表达式2代表列;元素个数为行、列长度的乘积。
  • 行、列下标均由0始。
  • 二维数组按行存放(一行存满再下一行)

序号=当前行号*每行列数+当前列号

(二维数组作参数时可用)

存储

1
float a[2][3];

若系统分配的首地址为1000,则:

a[0][0]:1000; a[0][1]:1004; a[0][2]:1008

a[1][0]:100C; a[1][1]:1010; a[1][2]:1014

初始化

  1. 按存放顺序对所有元素赋初值。
1
2
int a[2][3]={1,2,3,4,5,6};
int a[][3]={1,2,3,4,5,6};
  • 全部元素初始化能省略且只能省略第一维长度。(三维、四维etc)
  1. 按行给元素赋初值,每行元素组织在一对花括号内。
1
int a[2][3]={{1,2,3},{4,5,6}}
  1. 按行给部分元素赋初值,未赋值元素自动为0.
1
int a[3][4]={{1,2,3},{0,1,3},{2,6,4}}
  1. 按行赋初值也可省略第一维长度:
1
int a[][3]={{1},{},{3}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;
int main()
{
int c[][3] = { {1},{},{2} },i,j;
for (i = 0; i < 3; i++)
{
for(j=0;j<3;j++)
cout << c[i][j] << ' ';
}
cout <<endl;
system("pause");
return 0;
}
//1 0 0 0 0 0 2 0 0

基本操作

  • 对于数值型数组,程序中只能使用其元素。

数组名 [行下标][列下标]

  • 输入、输出、批量处理时需使用双循环,外循环控制行变化,内循环控制列变化。例如:
1
2
3
for(i=0;i<2;i++)
for(j=0;j<3;j++)
cin>>a[i][j];

  • 求3*3方阵的最大元素集下标
1
2
3
4
5
6
7
8
9
10
max=a[0][0];
imax=jmax=0;
for(i=0;i<3s;i++)
for(j=0;j<3;j++)
if(a[i][j]>max)
{
max=a[i][j];
imax=i;
jmax=j;
}

6.2 二维数组应用–矩阵转置、杨辉三角

矩阵转置

注意:

  • 方阵转置就是以主对角线对称;
  • 一次对调a[i][j]a[j][i]两个元素,故只能以下三角/上三角元素做参照,去和上三角/下三角元素对调。
1
2
3
4
5
6
7
for(i=0;i<3;i++)
for(j=0;j<i;j++)
{
t=a[i][j];
a[i][j]=a[j][i];
a[j][i]=t;
}

杨辉三角

输出n(n<10)行的杨辉三角形

1
2
3
4
5
6
7
8
9
10
11
12
13
//第一列和主对角线
for (i=0;i<n;i++)
a[i][0]=a[i][i]=1;
//其余元素
for(i=2;i<n;i++)
for(j=1;j<i;j++)
a[i][j]=a[i-1]a[j-1]+a[i-1]a[j];
for(i=0;i<n;i++)
{
for(j=0;j<=i;j++)
cout<<a[i][j]<<' ';
cout<<endl; //一行完毕,换行
}

完整程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#define n 10
using namespace std;
int main()
{
int i, j, a[n][n];
//第一列和主对角线
for (i = 0; i < n; i++)
a[i][0] = a[i][i] = 1;
//其余元素
for (i = 2; i < n; i++)
for (j = 1; j < i; j++)
a[i][j] = a[i - 1][j - 1] + a[i - 1][j];
for (i = 0; i < n; i++)
{
for (j = 0; j <= i; j++)
cout << a[i][j] << ' ';
cout << endl;
}

system("pause");
return 0;
}

二维数组应用–最短路径

A, B, C, D四个城市相互之间有8条单向公路,编程求任意两个城市间的最短距离。

image-20231012102211663

  • 数据存储:4行4列二维数组e[4][4]

  • 数据表示:

    • 与自己的距离为0,不连通城市间距离为∞;
    • 行下标代表起点,列下标代表终点,元素值代表距离。
  • 数据处理:弗洛伊德算法求

  • 算法核心:对每一对I和j,检查是否存在顶点k,使得i–>k–>j的距离比原有路径短。如存在,则更新原有路径。

以A作中转点为例:

1
2
for(i=0;ie[i][0]+e[0][j])
e[i][j]=e[i][0]+e[0][j]

Floyd算法:

1
2
3
4
5
for(k=0;k<N;k++)
for(i=0;i<N;i++)
for(j=0;j<N;j++)
if(e[i][j]>e[i][k]+e[k][j])
e[i][j]=e[i][k]+e[K][J]

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#define N 4  //顶点数
#define M 8 //路径数
#include <iostream>
using namespace std;
#define MAX 1000000 //无穷远
int main()
{
int e[N][N], i, j, k, st, en, d;
for (i = 0; i < N; i++) //初始化路径距离
for (j = 0; i < N; j++)
if (i = j)
e[i][j] = 0;
else
e[i][j] = MAX;
for (i = 0; i <= M; i++)//输入起终点及路径距离
{
cin >> st >> en >> d; //依次输入起终点
e[st][en] = d;
}
//FLOYD算法
for (k = 0; k < N; k++)
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
if (e[i][j] > e[i][k] + e[k][j])
e[i][j] = e[i][k] + e[k][j];
//输出
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
cout << (char)('A' + i) << "->" << (char)('A' + i) << ":" << e[i][j];
cout << endl;
}
system("pause");
return 0;

}

输入有问题,暂不知为何

6.3 字符数组

例:如何将任意长度的英文句子中的所有小写字母转换成大写字母?

实际操作将整个句子以字符串形式处理

字符串概念

  • 双引号引起的一串字符,如"ab123"
  • 若有定义char s[20],通过特殊的初始化、输入、处理、输出可用来处理可变的字符串。

特点:

系统自动在有效字符末尾加'\0'(字符串结束符)

初始化

  1. 用字符串为字符数组初始化
1
2
char s[10]={"I am fine"};
char s[10]="I am fine";
  1. 字符串数组初始化
1
char a[3][8]={"COBOL","FORTRAN","PASCAL"};

输入输出

char s[100]

1
2
3
4
5
6
7
8
9
//字符串的整体输入
cin>>s;//不能提取空格符
gets(s);

//字符串的整体输入
cout<<s;
puts(s);

//gets(),puts()在<stdio.h>

例:

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
char s[100];
cin>>s; //直接使用数组名
cout<<s<<endl;
system("pause");
return 0;
}

cin()不能提取空格符.

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
char s[100];
gets(s); //可以
puts(s);
system("pause");
return 0;
}

附:

在VS2015中,stdio.h头文件中已经不存在gets()函数定义,而被更安全的get_s()函数和fgets()函数所替代。故直接用gets()会出现未定义的错误。

  • cin以三大分隔符分隔
  • gets()以回车键为分隔

字符串处理原则

  • 整体输入、整体输出
  • 不用数组长度控制循环,而是通过当前字符**是否达到'\0'**判断循环结束与否
  • 构造字符串时需保证结尾有'\0'

例:如何将任意长度的英文句子中的所有小写字母转换成大写字母?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <stdio.h>
#define N 100
using namespace std;
int main()
{
char s[N];
int i;
gets(s);
for(i=0;s[i]!='\0';i++)
if(s[i]>='a'&&s[i]<='z')
s[i]=s[i]-'a'+'A';
puts(s);
system("pause");
return 0;
}

6.4 字符数组的应用:字符串处理

例1 **重要

例:将字符串s的内容复制到字符串t中

  • 数组名是地址常量,不能被复制
  • 逐元素复制
1
2
3
4
5
6
7
8
9
10
11
int i;
char s[N],t[N];
gets(s);
i=0;
while(s[i]!='\0')
{
t[i]=s[i];
i++;
}
t[i]='\0'; //在t末尾加\0,成为数组
puts(t);

注意:

字符数组需要逐个元素处理

例2

例:逐个比较两个字符串对应位置的字符大小,输出“两字符完全相等”的提示或第一个不相等字符的ASCII码差。

关键:

  • 找出持续比较的条件:
    • 对应位置的字符相等且串还未结束。
  • 退出循环后的再判断:如何退出的?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <stdio.h>
#define N 100
using namespace std;
int main()
{
char s[N],t[N];
int i;
gets(s);
gets(t);
i=0;
while(s[i]!='\0'&&t[i]!='\0')
if(s[i]==t[i])
i++;
else
break;
if(s[i]=='\0'&&t[i]=='\0')
cout<<"两字符串相等"<<endl;
else
cout<<"两串相差"<<s[i]-t[i];

system("pause");
return 0;
}

常用字符串处理函数

strlen(str)

求字符串str的长度,不包含’\0’在内。

strlwr

将str中的大写字母转换为小写字母。

strupr(str)

将str中的小写字母转换为大写字母。

strcpy(str1,str2)

将str2所指的字符串复制到str1中。

str1只能是字符数组名字或字符指针代表的字符串;

str2可以是字符数组名字、字符指针变量、字符串常量。

strcat(str1,str2)–>strcat_s()

将字符串str2的内容连接到str1内容的后面。

strcmp(str1,str2)

比较字符串str1和str2的大小。

从左至右逐字符比较ASCII码的大小,直到出现不相同字符或遇到'\0'为止。

str1 小于 str2 返回-1

str1 等于 str2 返回0

str1 大于 str2 返回1

例:strcmp("ABCD", "BD"):

B大于A,∴返回-1

说明

  • 上述函数原型在中
  • 除strcpy和strcat两个函数的第一个参数不能取字符串常量外,其他参数都可以为字符数组、字符指针变量或字符串常量。

例:字符串处理函数示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <sring.h>
int main()
{
char s[80];
while(1)
{
gets(s);
if (strcmp(s,"pass")) //s和"pass"不相等的时候输出非0值,执行
puts("Invalid password.\n");
else
break;
}
puts("pass\n");
system("pause");
return 0;
}

数组应用-古诗接龙程序分析

程序功能及要求

  • 可通过菜单选择五言诗和七言诗并进行连句测试
  • 机器给出诗的上句,要求用户给出下句。正确提示“答对了,你真棒!”,答错提示“很遗憾,继续努力!”
  • 若五言诗或七言诗库中的诗句全都测试完毕,则做出相应提示。

数据结构

二位字符数组(N:已定义过的符号常量)或字符指针数组。

char s5[N][30] //存放N个五言诗的上下句(实21)

char s7[N][30] //存放N个七言诗的上下句(实29)

获取数据

  • 可在字符数组初始化时将诗的上下句存放在一个字符数组中

  • 改进:读文件的方式将诗句读入字符数组

程序流程

  • 菜单选择:五言、七言、退出;
  • 随机产生一个五言诗或七言诗库中现有诗句个数内的不重复随机数;
  • 以该随机数为下标取出相应字符串的前半段显示,后半段复制到一个代表答案的字符串中;(上下句在一个字符串里)
  • 从键盘输入用户的接龙字符串并比较,根据结果做相应提示
  • 重复直到退出

代码组织

目前:所有代码组织在一个主函数中;

改进:模块化管理,将代码组织在多个自定义函数中

程序功能完善

  • 下接上
  • 加入分数系统
  • 记录打错诗句,今后重点训练