C#实践教程(第2版)
上QQ阅读APP看书,第一时间看更新

4.2 一维数组

一维数组是数组中最为简单和常用的,由单个数据为成员构成的数组。如季节数组可以包含4个成员:春、夏、秋、冬。数组中的每一个元素都可以作为一个变量被访问。

4.2.1 一维数组简介

一维数组的声明同变量声明类似,声明中需要包含成员的数据类型和数组名,不同的是数组声明需要在数据类型后紧跟着一个中括号[]。如声明一个名为num的数组,使用代码如下:

int[] num;

上述代码中,int关键字指定该数组中的成员为int型的数据,int后面的中括号除了用于与其他变量或常量区分,还用于定义数组的长度,即数组中的元素数量。

int关键字与中括号之间没有空格,而中括号与数组名称之间有一个空格。数组需要初始化才能使用,这一点与变量的使用一样。

数组的长度需要在初始化时指定,可以将长度写在中括号中,也可为数组成员赋值,系统将根据数组的赋值为数组分配空间、确定数组的长度。如定义一个有着3个整型元素的数组num,格式如下:

int[] num=new int[3];

数组的赋值需要将数组成员放在{}内,每个元素之间用逗号隔开。如将数组变量num直接赋值,格式如下:

int[] num={1,2,3};

上述代码中,对数组的赋值,可将数组成员放在大括号“{}”中,每个成员之间使用逗号隔开。数组成员赋值时,其内部的成员必须符合数组的数据类型。数组元素也可以是变量,如分别定义整型变量a、b、c,并赋给数组num,格式如下:

int a, b, c;
a = 0; b = 0; c = 0;
int[] num = { a, b, c };

除了数组在声明时的直接赋值,数组在声明后不能直接赋值,而需要实例化后才能赋值。使用关键字new,如练习4-1所示。

【练习4-1】

声明整型数组num,实例化数组的长度为3,并赋值为{55,9,7},使用代码如下:

int[] num;
num=new int[3]{55,9,7};

不能直接像变量赋值一样使用代码num={55,9,7};进行赋值。即使是使用new将对象实例化,也不能使用这样的语句。但是可以对实例化的数组成员单独赋值。即数组赋值只有三种形式:

□ 在声明时直接赋值。

□ 程序进行时使用new赋值。

□ 在数组被实例化后,对数组成员单独赋值。

由于一个数组有多个成员的排列,因此为确定到具体的一个成员,数组中有索引的概念。索引相当于图书的目录,为指定的内容标注一个方向。数组中的索引相当于其成员的编号,数组中的第一个成员索引为0,第二个成员索引为1,第n个成员的索引为n-1。使用索引来访问数组成员,如访问练习4-1中的第2个成员9,输出该成员的值,使用如下语句:

Console.Write(num[1]);

上述代码中,使用num[1]访问num数组的第2个成员9,其构成为:数组名、中括号、中括号内部的索引。这3个构成成分之间不需要有空格,上述代码的执行结果为9。

数组实际是类的一个对象,是基类Array的派生,可以使用Array的成员,如Length属性。关于类、对象、基类和派生等内容将在第5章和第6章介绍,本节只需学会如何使用Length属性获取数组长度。如获取数组num的长度并赋值给longnun,格式如下:

int longnum = num.Length;

4.2.2 数组遍历

数组的遍历即依照索引顺序,依次访问所有数组成员。数组的遍历可以使用循环语句,包括for循环、while循环等,以及专用于数组和数据集合的foreach in语句。

使用循环语句遍历数组,如为数组赋值或使用数组中的数据为其他变量赋值,如练习4-2所示。

【练习4-2】

声明整型数组num,实例化数组的长度为10,并将其成员依次赋值为1到10,使用代码如下:

int[] num;
num = new int[10];
for (int i = 0; i < num.Length;i++ )
{
    num[i] = i + 1;
}
for (int i = 0; i < num.Length; i++)
{
    Console.Write("{0} ",num[i]);
}

执行结果为:

1 2 3 4 5 6 7 8 9 10

练习4-2使用第1个for循环将数组赋值,又使用第2个for循环输出数组的值。使用循环语句遍历数组方便、容易理解,C#提供了数组专用的遍历语句foreach in语句,在本书第3章简单提过,使用方法如练习4-3所示。

【练习4-3】

将数组num1赋值为{5,2,6,8,4,1,3,9,7},使用foreach in语句将数组成员输出,代码如下:

int[] num1 = { 5, 2, 6, 8, 4, 1, 3, 9, 7 };
foreach(int i in num1)
{
    Console.Write("{0} ",i);
}

执行结果为:

5 2 6 8 4 1 3 9 7

foreach in语句结构简单,在数组中的作用就是将数组成员顺序遍历。

4.2.3 数组排序

数组最常见的应用就是对数组成员的排序,这也是生活中对数据的重要处理。如将全班学生的成绩赋值给数组,并按成绩从大到小排列。

数组的排序将改变数组成员的原有索引,如将数组成员按照从小到大的顺序排序,则数组中数字最小的成员,在排序后,其索引被修改为0。常见的排序方式有以下几种。

冒泡排序 将数据按一定顺序一个一个传递到应有位置。

选择排序 选出需要的数据与指定位置数据交换。

插入排序 在顺序排列的数组中插入新数据。

1. 冒泡排序

冒泡排序是最稳定的,也是遍历次数最多的排序方式。例如,将n个元素的数组数据从小到大排序:

冒泡排序将按照序号将数组中的相邻数据进行比较,每一次比较后将较大的数据放在后面。所有数据执行一遍之后,最大的数据在最后面。接着再进行一遍,直到进行n次,确保数据顺序排列完成,如练习4-4所示。

【练习4-4】

将数组value赋值为{15,4,1,2,8,33,22,26,30,19},通过冒泡排序将数组成员按从小到大的顺序排序,代码如下:

int[] value = { 15, 4, 1, 2, 8, 33, 22, 26, 30, 19 };
int max=0;
for (int i = 9; i >= 0;i-- )
{
    for (int j = 0; j <i; j++)
    {
        if (value[j] > value[j + 1])
        {
            max = value[j];
            value[j]=value[j+1];
            value[j+1]=max;
        }
    }
}
foreach (int i in value)
{
    Console.Write("{0} ", i);
}

执行结果为:

1 2 4 8 15 19 22 26 30 33

练习4-4通过内部循环将最大值一点一点移动到数组最后的位置。冒泡排序每移动一个最大值,接下来可以减少一次比较移动。因此,内部循环每执行一遍,执行次数减少一次。

冒泡排序准确性高,但执行语句多,数组要进行的比较和移动次数多。冒泡排序不会破坏相同数值元素的先后顺序,被称作是稳定排序。

2. 选择排序

选择排序为数组每一个位置选择合适的数据,如将数组从小到大排序,选择排序给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,以此类推,直到第n-1个元素,第n个元素不用选择了。

将数组按从小到大排序,选择排序将第一个元素视为最小的,分别与其他元素比较;当其他元素小于第一个元素,则交换它们的位置,并继续跟剩下的元素比较。直到确定第一个元素是最小的,再从第二个元素比较。直到倒数第二个元素与最后一个元素比较,如练习4-5所示。

【练习4-5】

将数组score赋值为{75,69,89,72,99,86,93,88,84,77},通过选择排序法将数组成员按从小到大的顺序排序,代码如下:

int[] score = { 75, 69, 89, 72, 99, 86, 93, 88, 84, 77 };
int max;
for (int i = 9; i >= 0; i--)
{
    for (int j = 0; j < i; j++)
    {
        if (score[j] > score[i])
        {
            max = score[j];
            score[j] = score[i];
            score[i] = max;
        }
    }
}
foreach (int i in score)
{
    Console.Write("{0} ", i);
}

执行结果为:

69 72 75 77 84 86 88 89 93 99

练习4-5中将数组序号从小到大依次与最后一个元素比较,将较大值与最后一个元素交换数值,得到最后位置上的数值;接着将元素依次与倒数第二个元素比较,以此类推,直到与第2个数比较。

选择排序改变了数值相同元素的先后顺序,属于不稳定的排序。选择排序同样进行了较多的比较和移动。

4.2.4 插入数组元素

数组新元素的插入,将导致插入位置之后的元素依次改变原有序号。在指定位置插入新的元素,为保证原有元素的稳定,首先要将原有元素移位,再将新的元素插入指定位置。

插入时元素的移位与排序时的移位不同,插入使得数组改变了原有长度,存储数组的空间不足以让新元素的加入。

使用new关键字可以修改数组的长度,但这种修改相当于重新定义了数组,数组元素的值将会被定为默认值0,如练习4-6所示。

【练习4-6】

将数组int[] score = { 75, 69, 89, 72, 99, 86, 93, 88, 84, 77 }重新声明为11个值,并输出,代码如下:

int[] score = { 75, 69, 89, 72, 99, 86, 93, 88, 84, 77 };
score = new int[11];
foreach (int j in score)
{
    Console.Write("{0} ", j);
}

输出结果为:

0 0 0 0 0 0 0 0 0 0 0

可见数组的重新定义将失去原有元素值,只能通过新建数组来保存插入后的数组,如练习4-7所示。

【练习4-7】

将数组score的第(n+1)个位置插入数据73,使其成为新的数组score1,即score1[n]=73,实现代码如下:

int[] score = { 75, 69, 89, 72, 99, 86, 93, 88, 84, 77 };
int[] score1 = new int[11];
int n = 5;                                          //在第n+1位置插入
int addnum = 73;                                    //插入数值73
for (int i = 9; i >=n; i--)
{
    score1[i + 1] = score[i];
    if (i == n)
    {
        score1[n] = addnum;
        for (int j = n - 1; j >= 0; j--)
        {
            score1[j] = score[j];
        }
        break;
    }
}
foreach (int j in score1)
{
    Console.Write("{0} ", j);
}

运行结果如下:

75 69 89 72 99 73 86 93 88 84 77

与原数组相比,第6个位置,原来86所在的位置插入了73。插入排序法在比较的基础上进行插入。

插入排序法是在一个有序的数组基础上,依次插入一个元素。如将数组从小到大排序,将新元素与有序数组的最大值比较,若新元素大,插入到字段末尾;否则与倒数第二个元素比较,直到找到它的位置,此时需要将该位置及该位置之后的元素序号发生改变,需要重新调整。

插入排序没有改变相同元素的先后位置,属于稳定排序法,但插入排序的算法复杂度高。具体步骤如练习4-8所示。

【练习4-8】

将数组score赋值为{75,69,89,72,99,86,93,88,84,77},通过插入排序法将数组成员按从小到大的顺序排序,代码如下:

int[] score = { 75, 69, 89, 72, 99, 86, 93, 88, 84, 77 };
int[] score0 = new int[10];
score0[0] = score[0];
int num = 0;
for (int i = 1; i < 10; i++)
{
    num = score[i];
    if(num>score0[i-1])
    { score0[i] = num;}
    for (int j = 0; j < i; j++)
    {
        if (score0[j] > num)
        {
            for (int k = (i - 1); k >= j; k--)
            {
                score0[k + 1] = score0[k];
            }
            score0[j] = num;
            break;
        }
    }
}
foreach (int sco in score0)
{
    Console.Write("{0} ", sco);
}

练习4-8中先将原数组第一个元素赋给新数组,这样新数组可以视为只有一个元素的有序数组。将原数组的第二个元素与新数组中第一个元素比较后插入,新数组将有两个元素,直到原数组最后一个元素的插入。

在插入时首先判断插入元素是否比有序数组最后一个元素大,若插入元素最大,则直接放在有序数组最后,否则将依次跟有序数组元素相比较,找到合适的位置,将原有元素移位后,插入新元素。

4.2.5 删除数组元素

数组元素的删除相对容易,只需找到需要删除的元素的位置,并将该元素之后的元素移位即可。

数组元素的删除有两种:一种是根据元素的索引删除;另一种是在不知道索引的情况下,删除有着某个值的元素。

1. 根据索引删除元素

根据索引删除数组元素,其实质是:将该索引后面的成员依次前移,覆盖掉原有数据;最后将最后一个索引成员赋值为0(整型数组元素默认值为0)。由于数组的长度是不能变化的,因此成员的删除,只是将后面的成员移位。

【练习4-9】

有数组score {75,69,89,72,99,86,93,88,84,77},将数组中的第3个元素删除,使用代码如下:

int[] score = { 75, 69, 89, 72, 99, 86, 93, 88, 84, 77 };
int del=3;                                          //删除第del个元素
for (int i = del; i < 10;i++ )
{
    score[i - 1] = score[i];
    if (i == 9)
    { score[i] = 0; }
}
foreach (int sco in score)
{
    Console.Write("{0} ", sco);
}

执行结果为:

75 69 72 99 86 93 88 84 77 0

2. 删除指定元素值

删除指定元素值,需要先找出指定元素的位置再进行删除,或直接将原有数组改为不含删除元素值的新数组。

对于没有重复元素的数组,可以找出要删除的元素位置再删除,如练习4-10所示。

【练习4-10】

有数组score {75,69,89,72,99,86,93,88,84,77},将数组中元素值为88的元素删除,使用代码如下:

int[] score = { 75, 69, 89, 72, 99, 86, 93, 88, 84, 77 };
int delnum=88;                                    //要删除的元素值
int del=0;
for (int i = 0; i < 10; i++)
{
    if (score[i] == delnum)
    {
        del = i;
        break;
    }
}
for (int i =( del+1) ; i < 10; i++)
{
    score[i-1] = score[i];
    if (i == 9)
    { score[i] = 0; }
}
foreach (int sco in score)
{
    Console.Write("{0} ", sco);
}

执行结果为:

75 69 89 72 99 86 93 84 77 0

若有重复的元素值,即使找出了元素位置,也不容易删除。可以将原数组为新数组赋值,遇到要删除的元素取消赋值并跳出。但这样产生的结果是,需要被删除的元素位置的值,被0取代。如练习4-11所示。

【练习4-11】

有数组score {75,69,89,74,99,86,93,74,84,77},将数组中元素值为74的元素删除,使用代码如下:

int[] score = { 75, 69, 89, 74, 99, 86, 93, 74, 84, 77 };
int delnum = 74;
int[] score0 = new int[10];
for (int i = 0; i < 10; i++)
{
    if (score[i] == delnum)
    {
        continue;
    }
    score0[i] = score[i];
}
foreach (int sco in score0)
{
    Console.Write("{0} ", sco);
}

执行结果为:

75 69 89 0 99 86 93 0 84 77