朝花夕拾 · C语言 | 数组篇

一.数组基础

1.数组定义

数组一般形式:类型说明符 数组名 [常量或常量表达式];
类型说明符:基本数据类型、构造数据类型(不能是void类型)。说明数组中保存的数据的数据类型。

数组名:遵循标识符的命名规则。

常量或常量表达式:数组中可以保存的元素的个数。
int a[10];                       

sizeof (a);                      //40字节

char str[32];                  //32字节

float array[5];                //20字节

注意:

1.常量表达式中可以包括常量和符号常量,不能包含变量。也就是说,C语言不允许对数组的大小作动态定义,即数组的大小不依赖于程序运行过程中变量的值。例如,下面这样定义数组是不行的

    int n;
    scanf("%d",&n);          //在程序中临时输人数组的大小
    int a[n];     

2.数组访问

数组必须先定义,然后使用。C语言规定只能逐个引用数组元素而不能一次引用整个数组。
下标可以是整型常量或整型表达式。例如:
a[0]=a[5]+a[7]-a[2* 3]
注意:定义数组时用到的“数组名[常量表达式]”和引用数组元素时用到的“[下标]”的区别,例如:
 


    a[0]=a[5]+a[7]-a[2* 3]
    int a[10];                //定义数组长度为10
    t=a[6];                   //引用a数组中序号为6的元素。此时6不代表数组长度
    

数组定义时只能用常量,但访问时可以使用变量访问(应以整型为结果访问)

3.数组初始化

对数组元素的初始化可以用以下方法实现。

(1)在定义数组时对数组元素赋予初值。例如:

    int a[10]={0,1,2,3,4,5,6,7,8,9};

将数组元素的初值依次放在一对花括号内。

(2)可以只给一部分元素赋值。例如:

    int a[10]={0,1,2,3,4};

定义a数组有10个元素,但花括号内只提供5个初值,这表示只给前面5个元素赋初值,后5个元素值为0

(3)如果想使一个数组中全部元素值为0,可以写成:

    int a[10]= {0,0,0,0,0,0,0,0,0,0};或int a[10]={0};

    不能写成int a[10]={0*10};

(4)在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组长

    int a[5]={1,2,3,4,5};可以写成int a[]={1,2,3,4,5};

在第二种写法中,花括号中有5个数,系统就会据此自动定义a数组的长度为5。但若数组长度与提供初值的个数不相同,则数组长度不能省略。例如,想定义数组长度为10,就不能省略数组长度的定义,而必须写成:
int a[10]={1,2,3,4,5};
只初始化前5个元素,后5个元素为0。

(5)数组定义时,若int a[],没指定数组大小,则需要初始化;数组定义若不进行初始化,则赋随机值。

4.数组存储

数组的存储形式
单一性:数组中的元素,每个元素的数据类型都是一致。
有序性:数组中的元素按照a[0]-a[n]的顺序进行存储。
连续性:数组在内存中选取连续内存空间存储。

数组越界:
1.初始化数组时,初始化的元素个数超出了数组本身的元素个数。
2.访问数组元素时,[]中的下标超出了数组元素的访问范围。

注意当使用sizeof时数组越界编译器并不会报错,sizeof 是编译时运算符,它计算的是表达式或类型的字节大小,而不会对表达式进行求值(即不会真正执行该表达式)。因此,即使数组长度为 10,使用 sizeof(a[10]) 也不会报错。

5.数组排序(选择、冒泡、插入排序)

1)选择排序

基本思想是:每一趟(如第i趟)在后面n-i+1(i=1,2,...,n-1)个待排序元素中选取关键字最小的元素,作为有序子序列的第i个元素,直到第n-1趟做完,待排序元素只剩下1个,就不用再选。

简单选择排序不断重复地选择待排序元素中最小的一个元素,当前处于第几轮就将该元素与
第几个位置上的元素交换位置,直至所有元素完成排序。

        例如,第一轮将最小的元素与第一个位置上(arr[0])的元素交换位置,第二轮将第二小的元素与第二个位置上的元素交换位置,不断重复上述过程,直到所有元素都已有序。具体步骤如下:
(1)从待排序序列中,找到关键字最小的元素;
(2)将该元素与待排序序列中的第一个元素交换位置(初始时,有序序列中没有元素);
(3)对于余下的n-1个元素,重复上述两个步骤。
对于给定的待排序关键字序列(25,20,99,16,11,14,22,25,42,17),使用简单选择排序的过程

流程如下图所示:

简单选择排序算法实现代码如下:


         for(int i=0;i<len-1;i++)
        {
            for(int j=i+1;j<len;j++)
            {    if( arr[i]>arr[j])
                    {
                        tmp=arr[i];
                        arr[i]=arr[j];
                        arr[j]=tmp;
                    }
            }
        }

也可采用如下方式:其中swap为封装交换两数的函数;

由于采用双重循环,故其时间复杂度为O(n*n);空间复杂度没有额外开销,为O(1);同时该算法不具备稳定性,因为其在内层循环中当遇到两数相同时并不会进行替换,而会继续向后比较,当遇到满足条件的值时会进行替换,从而越过中间相同的数,改变了两数的顺序

2)冒泡排序

基本思想:从后往前(或从前往后)两两比较相邻元素的值,若为逆序(A[i-1]>A[i]),则交换它们,直到序列比较完。我们称它为第一趟冒泡,结果是将最小的元素交换到待排序列的第一个位置(或将最大的元素交换到待排序列的最后一个位置),关键字最小的元素如气泡一般逐渐往上“漂浮”至“水面”(或关键字最大的元素如石头一般下沉至水底)。下一趟冒泡时,前一趟确定的最小元素不再参与比较,每趟冒泡的结果是把序列中的最小元素(或最大元素)放到了序列的最终位置,这样最多做n-1趟冒泡就能把所有元素排好序。

流程图如下所示:

冒泡排序算法实现代码如下:

		 for(int i=0;i<len-1;i++)
		 {
			 for(int j=0;j<len-i-1;j++)
			 {
                if( arr[j]>arr[j+1])
                    {
                        tmp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=tmp;
					}
			 }
		 }

书中相比上述代码额外采用了flag标志位用来判断表初始是否有序,并且倒序实现,每次将最小值交换到序列前方,参考上述流程图。

其性能如下:

3)插入排序

基本思想:假设待排序序列存放在数组arr[0,...,n-1]中(temp代表当前待排序元素),算法步骤如n-1个元素无序。排序过程的插入操作主要分为两部分:
(1)将数组中的数据分成有序和无序两个部分。初始状态下,第1个元素(arr[0])有序,后
(2)循环n-1次,依次将后n-1个元素按关键字有序插入有序序列中的合适位置上。
1有序序列中的元素从后往前地与当前待排序的元素比较,寻找其插入位置p;
2将有序序列中位置p及其之后的元素统一后移1位,然后将当前待插入元素插入目标位置
p。需要注意的是,在算法的实现上,一般是在比较的同时移动数据元素。

流程图如下:

插入排序算法实现代码如下:

性能如下:

6.折半查找

折半查找也称二分查找,是一种在有序数组中查找某一特定数据元素的查找算法。

基本思想:
(1)从当前表的中间元素开始查找,如果中间元素刚好是要查找的数据元素,则查找成功(初
始时刻的当前表是整个数组)。
(2)如果要查找的元素大于(或小于)中间元素,则在数组大于(或小于)中间元素的那一半
子表中查找,在子表中从步骤(1)重新查找。
(3)如果在当前数组为空,则代表查找失败。
折半查找算法的每一次比较都使查找范围缩小一半,该算法对于待查找的列表有两个要求:
(1)查找表必须支持随机查找,为此必须采用顺序存储结构;
(2)查找表内元素必须按关键字大小有序排列。

流程图如下:

折半查找算法实现代码如下:

         int low=0,mid=0,high=len-1;
			while(low<=high)
			{
				mid=(low+high)/2;
				if(num>arr[mid])
					low=mid+1;
				else if(num<arr[mid])
					high=mid-1;
				else
				{
					printf("%d\n",arr[mid]);
					return 0;
				}
			}
			printf("not find\n");

此外,因为折半查找需要方便地定位查找区域,所以它要求线性表必须具有随机存取的特性。因此,该查找法仅适合于顺序存储结构,不适合于链式存储结构,且要求元素按关键字有序排列。

二.字符数组

1.字符型数组定义

char 数组名 [常量];

数组名:标识符命名规则

char str[32];--->sizeof(str)--->32字节

2.字符型数组元素访问

数组名[下标]:

str[0]; ---->char

注意:下标避免数组越界。

3.字符型数组初始化

局部初始化:

    char str[32] = {'h', 'e', 'l', 'l', 'o'};
    char str[32] =  {"hello"};                //字符串初始化
    char str[32] = "hello";

没有初始化的部分,默认存储0('\0'字符)

全部初始化:

    char str[5] ={'h','e','l,'l,'o'};

    char str[] = {'h','e','l','l','o'}; ----->5字节

    char str[] =  {"hello"};            ------>6字节
==
    char str[] = "hello";


当以字符串初始化时,[]常量被省略时,将来数组空间按照初始化后的字符个数去确定,额外增加‘0'字符的存储位置

4.字符型数组输入输出

输入:

1.scanf

    scanf("%s",str);
只能输入不带空格,tab与enter的字符,否则停止

2.gets()

输出:

1.以遍历的方式输出

    for (i=0;str[i]!='\0';++i)
    {
        printf("%c", str[i]);
    }

2.以字符串方式输出

    printf("%s\n",str);

3.puts输出

    puts(str);//自动换行

5.字符型数组相关函数

strlen(str):计算字符串的长度(字符型数组中有效字符个数,不包含'\0')

其一般形式为:
strlen(字符数组)。它是测试字符串长度的函数。函数的值为字符串中的实际长度(不包括'\0'在内)。例如:

char str[10]={"China"};

printf("%d",strlen(str));

输出结果不是10,也不是6,而是5。

strcpy(str1,str2):字符串拷贝

其一般形式为:strcpy(字符数组1,字符串2)
strcpy是stringcopy(字符串复制)的简写。它是“字符串复制函数”,作用是将字符串2复制到字符数组1中去.

strcat(str1,str2):字符串拼接

其一般形式为:strcat(字符数组1,字符数组2)
strcat是String Catenate(字符串连接)的缩写。其作用是连接两个字符数组中的字符串,把字符串2接到字符串1的后面,结果放在字符数组1中,函数调用后得到一个函数值即字符数组1的地址。

sizeof(str):求字符型数组在内存中所占的字节数(和空间开辟时大小有关)。

    	char str1[]={"hol"};
        printf("%ld\n",strlen(str1));   //3
		printf("%ld\n",sizeof(str1));   //4
    注意,strlen只能读取以\0为结尾的字符串,若定义char str1[]={'q','t'}此类,则不会输出2,而是

会继续向t后面的内存查找,直到找到以\0为结尾的字符位置;而若定义char str1[]={'q','t','\0'}则会

输出2。


三.二维数组的定义与引用

未完待更......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值