C语言——数组和排序
数组
数组的概念
数组是一组数据 ;
数组是一组相同类型的数据或变量的集合 ;
应用场景:
用于批量的处理多个数据 ;
语法:
类型说明符 数组名 [常量表达式]
类型说明符也就是数组中元素的数据类型,元素的数据类型可以是整型(int/short/long/long long)、字符型(char)、浮点型(float / double /long double );
数组名代表的是整个数组也表示的是数组首元素的地址,数组名的命名规则和普通变量的命名规则一致;
**[常量表达式]**其中的常量表达式代表的是数组的长度,数组长度可以省略,但是必须要有初始化编译器要根据初始化的值,来推算实际的长度。需要注意的是:c99标准之后,数组长度可以是变量但是不能初始化。
数组的初始化
数组的初始化可以分为三种:
1、全部初始化:int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
2、部分初始化:int a[10] = { 1, 2, 3, 4, 5 };
特点:没有被初始化的部分会被初始化为0;
3、不初始化:int a[10];
特点:不初始化数组中的值全部是随机值;
数组的特点
1、连续性, 即数组空间是一片连续内存空间 ;
2、有序性,即数组的元素挨个存放 ;
3、单一性,即数组元素类型是同一类型 ;
数组下标的含义:
a[0],下标表示的是相对于数组名也就是数组首元素偏移了几个元素。
排序
选择排序
选择排序的主要思想就是给数选择合适的位置;
实现代码如下:
#include <stdio.h>
int main(void)
{
int i = 0, j = 0, size = 0, temp = 0;
scanf("%d", &size);
int a[size];
for (i = 0; i < size; ++i)
{
scanf("%d", &a[i]);
}
for (i = 0; i < size-1; ++i)
{
for (j = i+1; j < size; ++j)
{
if(a[j] < a[i])
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
for(i = 0; i < size; ++i)
{
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
按照我的理解选择排序按照顺序选择一个数然后一次与数组后面的数进行比较假设要排升序,如果被选择的数在比较的过程中小于其他的数就交换这两个数,举个例子来说假设有一个数组a[5] = { 5, 2, 0, 9, 8 };第一次也就是i = 0时,拿5和2(a[j]其中j==1)比较,2 < 5,交换2和5的在数组中的位置,此时a[i]就为2,然后2再和0进行比较(也就是让j++),0 < 2,交换这两个元素,此时a[i] == 0,再拿0和9(j++)比较,9 > 0不交换,再拿8和0比较(j++),8 > 0不交换此时到达数组末尾数组中首元素就是最小值然后让i++也就是拿第二个元素和它后面的元素进行比较,以此类推直到倒数第二个元素和倒数第一个元素比较一次后结束循环,此时数组中的元素就是升序的了。
冒泡排序
冒泡排序的主要思想是:一次冒出一个数相邻两个元素进行两两比较,小的放前,大的放后。
实现冒泡排序的核心代码:
for (i = 0; i < n-1; ++i)
{
for (j = i + 1; j < n; ++j)
{
if (a[j] < a[i])
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
}
我的理解是冒泡排序就是从第一个数开始让第一个数和第二个数进行比较大的放在后面小的放在前面,然后让第二个元素和第三个元素进行比较同理大的在后小的在前,直到倒数第二个元素和最后一个元素的比较完成后数组最后一个元素就是最大的元素,然后进行第二趟冒泡再进行第二趟冒泡时就不需要和最后一个元素a[n - 1]比较了因为最后一个元素已经是最大了,第二趟只需要比较前面n - 1个数(其中n为数组元素的个数),第二趟比较也是从a[0]开始和a[1]比较大在后小在前,a[1]和a[2]比较以此类推直到a[n - 2]和a[n - 3]比较这一趟冒泡过程结束,到第三趟冒泡确定第三大得数,直到n - 1趟冒泡结束整个冒泡排序;经过冒泡排序最后数组就就变得有序了。
插入排序
插入排序的主要思想是在有序序列中,找到合适的位置插入 。
实现插入排序的核心代码:
for (i = 1; i < n; ++i)
{
int t = a[i];
j = i;
while(j > 0 && t < a[j-1])
{
a[j] = a[j-1];
--j;
}
a[j] = t;
}
假设数组为 int a[5] = { 5, 2, 10, 3, 9};
其实插入排序跟我们平时, 排队差不多,假设我们要把数组元素按升序排列,组第一个元素我们认为它是有序的,我们用一个临时变量保存第二个元素然后拿这个临时变量temp和第一个元素比较如果第二个元素比第一个元素小那么第一个元素往后移动一个位置因为第一个元素前没有数据了所以把temp赋值给第一个元素,以上的例子就是temp = 2;2 小于 5,5往后移动四个字节把2覆盖掉,此时数组元素就是 5, 5, 10, 3, 9然后把temp赋值给a[0]此时数组元素就是2,5,10,3,9,然后拿第三个元素和前面的元素比较10大于5和2不用动,再拿第四个元素和前面的元素比较,3小于10,10往后移动,把3赋值给未移动前10的位置,3再和5比较3小于5,5往后移动把3赋给5移动前的位置,3再和2比较3大于2结束比较,最后的9和前面元素的比较的过程也是一样的如果比前一个元素小,前一个元素就往后移动直到大于前一个元素或者到数组首元素为止。
二分查找
实现二分查找的前提是数组元素有序,输入要查找的元素,计算中间位置的坐标要计算中间位置的下标就必须知道要查找区域的首元素的下标和尾元素的下标(mid = begin + end),用中间位置下标对应的元素和要查找的元素比较如果要查找的元素大就继续往右找即更新要查找的区域begin = mid + 1;再更新中间位置的下标再继续继续比较中间位置下标对应的元素和要查找的元素的大小,如果要查找的元素小就往左找即更新要查找的区域,往左就是begin不变,end = mid - 1,再更新中间位置的下标再继续比较中间位置下标对应的元素和要查找的元素的大小,直到找到为止或者到达数组末尾为止。
#include <stdio.h>
int main(void)
{
int a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int begin = 0;
int key = 0;
int end = sizeof(a) / sizeof(a[0]);
scanf("%d", &key);
while(begin <= end)
{
int mid = (begin + end) / 2;
if(a[mid] > key)
{
end = mid - 1;
}
else if(a[mid] < key)
{
begin = mid + 1;
}
else
{
break;
}
}
if(begin <= end)
{
printf("找到了\n");
}
else
{
printf("没找到\n");
}
return 0;
}
一维字符型数组
一维字符型数组的语法定义和整型数组的语法定义差不多,只不过一维字符型数组的数据类型数char,在C语言中可以使用字符型数组来存放字符串,一维字符型数组的定义例子如下:
char s1[10] = "hello";
char s2[10] = { 'h', 'e', 'l', 'l', 'o', '\0'};//这个数组里存放的是一个字符串
char s3[10] = { 'h', 'e', 'l', 'l', 'o' };//这是一个字符数组
字符串的特点:
1、字符串在C语言中是当做字符数组来处理的;
2、字符串有一个专门的结束标志 ‘\0’;
字符串在内存中的存储:
字符串在内存中的存储方式其实是以字符数组的方式存储的,例如"hello"实际占用的内存空间是6个字节,包含了’\0’。
需要注意的是:在操作字符串的时候通常是以’\0’为结束标志的,而在操作数组时是通常是以数组长度为操作依据的。
与字符串相关的函数
puts
函数原型:
int puts(const char* s)
puts函数的功能数输出一个字符串;
参数:
s表示字符串它是一个指针类型如果你定义的是字符数组可以把数组名传进去,参数还可以是字符串常量字符串常量 比如说"helloworld!";
返回值:
如果输出成功返回一个非负数,如果输出失败就返回-1;puts在输出时自动换行;
gets
函数原型:
char *gets(char *s);
gets函数的是从键盘获取一个字符串;
参数:
s代表就是一块存储空间,需要的是一个一维字符型数组的数组名;
返回值:
如果成功获取就返回s, 如果失败就返回NULL。
strlen
函数原型:
size_t strlen(const char *s);
strlen的功能是计算字符串长度 ;
参数:
s表示参数是需要一个指针,这个参数可以是数组名或者是字符串常量;
返回值:
返回字符串长度;
strlen的功能简单模拟:
#include <stdio.h>
int main(void)
{
char s[20];
int i = 0;
gets(s);
while(s[i] != '\0')
{
++i;
}
printf("%d\n", i);
}
上述程序利用了字符串是以"\0"为结束标志的特点,输出的i就是字符串长度;
strcpy
函数原型:
char * strcpy(char *dest, const char *src);
strc函数的功能是复制字符串也就是将src中字符串,复制到dest中;
参数:
src表示原字符串,在传参数时可以传数组名或者字符串常量,dest表示目的字符串传参数是可以传数组名或者字符串常量;
strcpy函数的功能简单模拟:
#include <stdio.h>
int main(void)
{
char s1[10] = { 0 };
char s2[10] = { 'a', 'a', 'a', 'a', 'a' , 'a', 'a', 'a', 'a', 'a' };
int i= 0;
int size = sizeof(s1) / sizeof(s1[0]);
gets(s1);
while(s1[i] != '\0')
{
s2[i] = s1[i];
++i;
}
s2[i] = '\0';
printf("%s\n", s2);
return 0;
}
上述程序通过循环赋值的形式实现字符串的拷贝,也就是遍历字符数组s1当s1中的元素不为’\0’时就把这个元素赋值给s2对应位置,直至s1对应的元素为’\0’拷贝结束。
strcat
函数原型:
char *strcat(char *dest, const char *src);
strcat的功能是拼接字符串;
参数:
dest表示目的字符串, 传参数时可以使用的实参形式一维字符型数组名 ;
src表示源字符串,传参数时可以使用的实参形式一维字符型数组名或者是字符串常量;
strcat功能的简单模拟:
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[30] = { 'h', 'e', 'l', 'l', 'o', '\0' };
char s2[30] = { 'w', 'o', 'r', 'l', 'd', '\0'};
int i = 0;
int j = 0;
while(s1[i] != '\0')
{
++i;
}
while(s2[j] != '\0')
{
s1[i] = s2[j];
++j;
++i;
}
s1[i] = s2[j];
printf("%s\n", s1);
int count = 0;
i = 0;
while(s1[i] != '\0')
{
if(s1[i] == '\0')
{
count++;
}
++i;
++count;
}
printf("count = %d\n", count);
return 0;
}
功能模拟的思路是:
1、找到要拼接字符串的位置,也就是第一个字符串’\0’在的位置i;
2、从头遍历第二个字符串,在i位置开始拼接第二个字符串,采用逐个拷贝的方法;
3、遍历完成在拼接字符串后面加上’\0’让新字符串构成字符串;
4、打印拼接的字符串;
strcmp
strcmp的函数原型:
int strcmp(const char *s1, const char *s2);
strcmp的功能:比较字符串大小 ;
参数:
s1表示字符串 ;
s2表示字符串 ;
s1和s2可以是数组名或者是字符串常量 ;
返回值:最后停的位置上字符的差值来反映大小 ;
strcmp功能的简单模拟:
#include <stdio.h>
int main(void)
{
int i = 0;
char s1[100] = { 0 };
char s2[100] = { 0 };
gets(s1);
gets(s2);
while (s1[i] == s2[i] && s1[i] != '\0' && s2[i] != '\0')
{
++i;
}
if (s1[i] > s2[i])
{
printf("s1:%s > s2%s\n", s1, s2);
}
else if (s1[i] < s2[i])
{
printf("s1:%s < s2:%s\n", s1, s2);
}
else
{
printf("s1:%s == s2:%s\n", s1, s2);
}
return 0;
}
思路:
1、从s1和s2的首元素开始比较如果相等就比较下一个,如果不等就记录该位置;
二维数组
二维数组的语法:
类型说明符 数组名[常量表达式][常量表达式]
与一维数组不同的是二维数组比一维数组多了一个常量表达式;
类型说明符也就是数组中元素的数据类型,元素的数据类型可以是整型(int/short/long/long long)、字符型(char)、浮点型(float / double /long double );
数组名代表的是整个数组也表示的是数组首元素的地址,数组名的命名规则和普通变量的命名规则一致;
**第一个常量表达式:**表示行;
**第二个常量表达式:**表示列;
二维数组的初始化:
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};//全部初始化
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};//全部初始化
int a[3][4] = {1,2,3,4,5,6};//部分初始化,未被初始化的部分会被初始化为0;
int a[3][4] = {{1,2},{5,6},{9,10}};//全部初始化
二维数组的类型是:
int [4];
类型说明符 数组名[常量表达式]
int [4] a [3]
int[4] a[3]是一个一维数组而这个一维数组的元素是一维数组,但是C语言语法不允许这样写而是写成int a[3][4]的形式,但是可以知道的是二维数组本质上是一个一维数组,在C语言中不存在二维数组的概念二维数组使用一维数组来模拟的;
二维数组在内存中是按行存储的,其实可以这样想一维数组元素是挨个存放在一块连续的空间里的,而二维数组的行可以看成一维数组的一个元素所以在内存中二维数组是按行存储的而且每一行是紧挨在一起的,二维数组在内存中的存储方式如下图所示;
今天先到这明天再更新!