C语言初阶之数组讲解(1)(三大排序其二)
目录
数组
在学习数组之前,大家是否还记得之前讲过的变量和常量?变量是一个储存常量的盒子,在实际情况中我们可能需要很多个盒子,并且这些盒子之间没有联系。那我们要怎么一次性建立许多有关系的盒子呢?这个时候我们需要使用数组
数组的定义
数组是一组相同类型元素的集合,简单来说就是一个大盒子,这个大盒子里又装了许多的小盒子。
我们研究代码的时候总是不断的追求减少代码量,递归和循环就是如此
数组同样可以做到
int a=0;
int b=0;
int c=0;
int d=0;
这个时候我们可以使用数组来完成
int arr[4]={0};
这样看者是不是大大的减少了代码量呢
同时,数组这个大盒子里的小盒子在内存方面是连续存在的,所有在进行很多实际问题时,我们完全可以利用这种性质
int arr[10]; //定义了一个数组名是arr,可以存放十个整形的数组
char ch[20]; //定义了一个数组名是ch,可以存放二十个字符的数组
float farr[5];//定义了一个数组名是farr,可以存放五个单精度浮点数的数组
在声明一个数组的时候,[]里代表的是数组长度,[]旁的是数组名,最左边的是数组中的元素类型
为什么强调声明数组的时候呢,因为声明完数组之后,[]是一种运算符,[]里的数叫做下标。下面讲解一下数组的几个注意事项
1.既然数组在内存方面是连续存在的,那么数组中的每一个元素都是由联系的,找到每一个元素的工具就是下标,数组的每个元素的下标是从0开始的,千万要牢记,并且数组的使用需要通过下标
//下面利用简单的for循环来引用数组的每一个成员
#include<stdio.h>
int main() {
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
for (i = 0; i < 10; i++)
printf("%d ", arr[i]);
printf("n");
return 0;
}
//打印结果是1 2 3 4 5 6 7 8 9 10
2.声明数组是,[]必须放常量,当然c99标准引入了变长数组的概念,使得数组长度可以是一个变量,但是目前只有gcc编译器支持变长数组。
3.初始化数组时,我们应该把我们需要存放的元素放到{}里,当我们进行不完全初始化时,剩下的元素默认初始化为0。我们举个例子说明一下
int arr[10];
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int arr[10]={0};
第一种声明时我们没有进行初始化
第二种声明我们把每个元素都进行了初始化,每个数组元素都有对应的值,这种就叫完全初始化
第三种声明我们只赋值了一个0,但是数组的长度为10,并不是所有的成员都被初始化了
这种叫做不完全初始化,而未被初始化的元素统统都会被初始化为0或者
4.数组的长度可以省略,此时编译器会根据声明的内容来确定数组的长度
int arr[]={1,2,3,4,5};
//此时的数组arr的长度是5,因为只初始化了五个数组的成员
需要牢记的数组名的含义
数组名是一个常量指针,是一个不能改变里面内容的已经绑定了的盒子,这个盒子里存放的内容是首元素地址,大家一定要牢记这个概念,但至于什么是常量指针,什么是首元素地址在指针章节会详细讲解,这里不过多深究,另外,注意数组名含义的例外,只有这两个例外
1.sizeof(数组名) 此时的数组名代表整个数组,即单独放在sizeof中的数组名代表整个数组
2.&数组名 这是的数组名也代表整个数组,得到的结果也是整个数组的地址
数组的三种排序
介绍完了数组的含义,接下来上我们今天的主菜,数组的排序,数组这个大盒子里的元素是人为赋值的,所以经常会出现无序的情况,这个时候我们需要去数组进行排序,在学习数组的排序之前,大家有没有在尝试另外三个角的九九乘法表呢?建议大家一定要进行尝试,因为这种两层for循环的思想对接下三种排序的学习尤为重要
1.冒泡排序
我们要讲的三大排序分别是冒泡,选择和插入,那我们先从最简单的冒泡排序开始今天的学习
什么是冒泡排序
冒泡排序是一种计算机科学领域比较简单的算法,通过不断的比较相邻的两个元素,如果顺序错误就进行就交换来实现,两层for循环的冒泡排序每一趟排序都会讲最大或者最小的元素放到最右边,就想冒泡一样,所以称之为冒泡排序,接下来以整形的冒泡排序为例来介绍冒泡排序
#include<stdio.h>
int main() {
int arr[10] = { 5,4,6,8,99,77,55,10,100,800 };
int i, j; //两个循环变量
int box; //再将两个变量进行交换位置的时候,我们需要一个空盒子来作为媒介,而box变量就是
//这个空盒子
for (i = 0; i < 10; i++)
{
j = 0;
for (j = 0; j < 9; j++) { //每一趟冒泡排序都会把最大的值放到最右边
if (arr[j] > arr[j + 1]) //如果前面那项大于后面那项,说明顺序错误,进行交换
{
box = arr[j]; //交换的流程
arr[j] = arr[j + 1];
arr[j + 1] =box;
}
}
}
for (i = 0; i < 10; i++) {
printf("%dt", arr[i]); //将排序后的结果打印
}
printf("n");
return 0;
}
这个便是这个程序运行的结果,但是 这个冒泡排序其实有很多的多余操作,我们可以尝试通过观察每一次排序后的结果来进行优化。
#include<stdio.h>
int main() {
int arr[10] = { 5,4,6,8,99,77,55,10,100,800 };
int i, j;
int box;
for (i = 0; i < 10; i++)
{
j = 0;
for (j = 0; j < 9; j++) {
if (arr[j] > arr[j + 1])
{
box = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = box;
}
}
for (j = 0; j < 10; j++) { //每一趟冒泡排序都打印结果
printf("%dt", arr[j]);
}
printf("n");
}
return 0;
}
这段代码的运行结果是这样的
再将数组的数据进行修改,我们再看一下另一种结果
#include<stdio.h>
int main() {
int arr[10] = { 500,95 ,84,56,800,29,16,358,11,4 };
int i, j;
int box;
for (i = 0; i < 10; i++)
{
j = 0;
for (j = 0; j < 9; j++) {
if (arr[j] > arr[j + 1])
{
box = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = box;
}
}
for (j= 0; j < 10; j++) { //每一趟冒泡排序都打印结果
printf("%dt", arr[j]);
}
printf("n");
}
return 0;
}
我们可以很明显的发现几个问题
1. 最后一次排序多余,没有必要
2.第几趟排序的最后几个数都是已经是有序了,没有必要在比较
3.有时候可能排序早就完成了
根据以上的问题,我们将对冒泡排序进行再一次的改进
#include<stdio.h>
int main() {
int arr[10] = { 500,95 ,84,56,800,29,16,358,11,4 };
int i, j;
int box;
int flag = -1; //用flag作为数组是否有序的标志
for (i = 0; i < 9; i++) //减少一趟冒泡排序
{
j = 0;
for (j = 0; j < 9-i; j++) { //后i项已经完成了排序,可以不用在比较
if (arr[j] > arr[j + 1])
{
box = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = box;
flag = 0; //进行了调整,则赋值flag为0
}
}
if (flag)
break; //进行了一趟排序之后,如果没有进行交换,
//则flag为-1,数组已经有序,表达式为真,跳出循环
}
for (j= 0; j < 10; j++) {
printf("%dt", arr[j]);
}
printf("n");
return 0;
}
这串代码很好的解决了上面的问题,减少了没意义的操作,但是这个代码也并不是完美的,后面会告诉大家原因
2.选择法排序
掌握了冒泡排序之后,选择排序就显得比较简单了,选择排序也是不断地比较两个元素,第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。 以此类推, 但选择排序被称为是不稳定的排序方法
我们直接在原有的冒泡排序的基础上进行修改
#include<stdio.h>
int main() {
int arr[10] = { 500,95 ,84,56,800,29,16,358,11,4 };
int i, j;
int box;
int min; //不断的让arr[min]成为最小值
for (i = 0; i <9; i++) {
min = i;//前i项已经有序
for (j = i; j <10; j++)
{
if (arr[j] < arr[min]) //如果第j项小于arr[min],则第j项为最小项
min = j;
}
box = arr[i];
arr[i] = arr[min];
arr[min] = box;
printf("%dt", arr[i]); //每一趟找到最小值
}
return 0;
}
相信大家在学会了冒泡排序之后,对于选择排序并不难理解
3.插入排序(不讲解)
插入排序相对于前两种排序来说复杂很多,并且标题也是其二,所以今天并不会讲插入排序,但是
插入排序也需要使用两层循环来实现,学到这大家也能理解掌握两层循环的重要性了吧。
在学习插入排序之前,大家一定要尝试解决这样一个实际问题
如何往一个有序的数组插入一个数,并保证数组的有序性?下一期博客会详细讲解这部分内容
今天的博客分享到此结束,希望对你的学习有所帮助!