(一)数组杂识
数组操作的复杂性:
数组操作的时间和空间复杂性如下表所示:
时间复杂性
算法 | 平均情况 | 最坏情况 |
插入(增) | O(1) | O(1) |
删除(删) | O(n) | O(n) |
查找(查) | O(n) | O(n) |
遍历+赋值(改) | O(n) | O(n) |
空间复杂度:
最坏情况下的空间复杂度为O(n)。
数组
数组的所有数据元素都存储在主存储器中的连续位置。 数组名称表示主存储器中的基地址或第一个元素的地址。 数组的每个元素都由适当的下标表示。
可以用三种方式定义数组的索引。
- 0(从零开始查找):数组的第一个元素是
arr[0]
。 - 1(基于一个查找):数组的第一个元素是
arr [1]
。 - n(基于n的查找):基于数组的第一个元素,可以定位任何随机下标值。
- 注意:如果数组的大小是
n
,则最后一个元素使用n-1
来访问。 - 某一数组A[i] 元素的字节地址 = 基地址 + size * (i距离首地址的下标差值)
(二)线性表的顺序表示
线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素。
顺序表中存放数据的特点和数组这种数据类型完全吻合,所以顺序表的实现使用的是数组。
使用顺序存储结构存储的数据,第一个元素所在的地址就是这块存储空间的首地址。通过首地址,可以轻松访问到存储的所有的数据,只要首地址不丢,数据永远都能找着(班级里,按照学号去叫人,肯定能念到每一个同学,想找谁,就念谁)。
在建立顺序表时,需要预先申请内存空间,还需要实时记录顺序表的长度和顺序表本身申请的内存大小,便于后期对顺序表中的数据元素进行调取。
自定义顺序表的结构:
//----线性表的动态分配顺序存储结构--------
#define LIST_INIT_SIZE 100//线性表存储空间的初始分配量
typedef struct {
int * head;//声明了一个名为head的长度不确定的数组,也叫“动态数组”:存储空间基址
int length;//当前顺序表的长度
int size;// 顺序表分配的存储容量
}SqList;
初始化顺序表:
//初始化
SqList InitSqList()
{
SqList q ;
q.head = (int*)malloc(LIST_INIT_SIZE * sizeof(int));//构造一个空的顺序表,动态申请存储空间
assert(q.head);//如果q.head为空,返回空
q.lenth = 0;//空表的长度初始化为0
q.size = LIST_INIT_SIZE;//空表的初始存储空间为Size
return q;//返回头结点,也可以不返回
}
完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//----线性表的动态分配顺序存储结构--------
#define LIST_INIT_SIZE 10//线性表存储空间的初始分配量
typedef int Data;//抽象数据类型
typedef struct
{
int* head;//声明了一个名为head的长度不确定的数组,也叫“动态数组”:存储空间基址
int lenth;//当前顺序表的长度
int size;// 顺序表分配的存储容量
}SqList;
//初始化
SqList InitSqList()
{
SqList q ;
q.head = (int*)malloc(LIST_INIT_SIZE * sizeof(int));//构造一个空的顺序表,动态申请存储空间
assert(q.head);//如果q.head为空,返回空
q.lenth = 0;//空表的长度初始化为0
q.size = LIST_INIT_SIZE;//空表的初始存储空间为Size
return q;//返回头结点,也可以不返回
}
//增:这里采用直接尾插顺序表,作为表的最后一个元素;但还有按指定位置插入;在表头插入;
void InsertSqList(SqList* q,Data data)
{
if (q->lenth > q->size) return;
q->head[q->lenth] = data;
q->lenth++;
}
//删除:这里是按指定位置删除; num:指定下标
void DeleteSqList(SqList* q, int num)
{
assert(q->head);
if (q->lenth <= num)
{
printf("长度传入失败,请检查后输入\n");
return;
}
for (int i = num; i < q->lenth; i++)
{
q->head[i] = q->head[i + 1];
}
q->lenth--;
}
//遍历
void Print(SqList q)
{
if (q.head == NULL)
{
printf("p为空 \n");
return;
}
for (int i = 0; i < q.lenth; i++)
{
printf("%d ",q.head[i]);
}printf("\n");
}
//查:num:指所查元素下标
Data researchSqList(SqList q, int num)
{
assert(q.head);
if (q.lenth <= num)
{
printf("长度传入失败,请检查后输入\n");
return;
}
return q.head[num];
}
//改:num:指所改元素下标,a:修改数据
void modifySqList(SqList* q,Data a,int num)
{
assert(q->head);
if (q->lenth <= num)
{
printf("长度传入失败,请检查后输入\n");
return;
}
q->head[num] = a;
}
int main()
{
SqList p=InitSqList();
printf("增加10个元素:\n");
for (int i = 0; i < 10; i++)
{
InsertSqList(&p, 3+i);
}
Print(p);
printf(" 修改坐标为3的元素:\n");
modifySqList(&p, 77, 3);
Print(p);
printf("删除坐标为2的元素:\n");
DeleteSqList(&p, 2);
Print(p);
printf("查找坐标为4的元素:\n");
printf("%d",researchSqList(p, 4));
return 0;
}
总结:
顺序表的实现,借用了数组这一数据类型,优点是在对数据进行遍历时,数据在连续的物理空间中存放,查找,修改,遍历的速度比较快。但是由于数组本身的限制,在向顺序表中新增(若按指定位置插入)或者删除数据元素时,如果被操作位置后续有很多数据元素,后续所有的数据元素都需要前移,最后虽然实现了功能,但是程序总体效率不高。