线性表的顺序表示和实现
一:线性表的相关概念
1:定义
由n(n>=0)个数据特性相同的元素构成的有限序列叫做线性。
2:特性
- 数据元素的位序从1开始
- 除第一个恶元素之外,结构中的每个数据元素均有只有一个前驱
- 除最后一个元素之外,结构中的每个数据元素均只有一个后继
二:顺序表介绍以及相关函数的介绍
1:定义
顺序表示用一组地址连续的存储单元依次存储线性表的数据元素,这种表示也叫做线性表的顺序存储结构或者顺序映像。
2:时间复杂度
- 最好时间复杂度:最好情况下的时间复杂度
- 最坏时间复杂度:最坏情况下的时间复杂度
- 平均时间复杂度:算法在所有可能的情况下,按照输入实例以等概率出现时,算法计算量的 加权平均值
3:c语言中用到的函数介绍
sizeof实际上是获取了数据类型在内存中所占用的存储空间
下面是两种使用情况
-
用于数据类型
sizeof使用形式:sizeof(type)
数据类型必须用括号括住。如sizeof(int)。
-
用于变量
sizeof使用形式:sizeof(var_name)或sizeof var_name
变量名可以不用括号括住。如sizeof (var_name),sizeof var_name等都是正确形式。带括号的用法更普遍,大多数程序员采用这种形式。
注意:sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。
malloc和free介绍
malloc()函数:其实就在内存中找一片指定大小的空间,然后将这个空间的首地址范围给一个指针变量,这里的指针变量可以是一个单独的指针,也可以是一个数组的首地址,这要看malloc()函数中参数size的具体内容。
free函数:释放内存空间
这两个函数都是在<stdlib.h>这个头文件中
malloc函数
-
功能:在内存的动态存储区中分配一块长度为"size"字节的连续区域。函数的返回值为该区域的首地址。
-
函数原型 :void malloc(int size);在c语言编写程序的具体使用过程中,void 类型可以强制转换为任何其它类型的指针。
-
说明:如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
-
当内存不再使用时,应使用free()函数将内存块释放。
free函数
- 调用形式: free(void*ptr);
- 功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区应是由malloc或calloc函数所分配的区域。
三:静态顺序表的实现
静态顺序表的缺点:大小是固定的(代码编写是就已经确定);
1:代码实现
#include "stdio.h"
#include "math.h"
#define MaxSize 10 //定义一个最大长度
//存储空间是静态的,顺序表的长度刚开始就确定了不能更改
typedef struct {
int data[MaxSize];//用数组存放数据元素
int length; //顺序表的当前长度
}SqList; //顺序表的类型定义
/**
* 初始化一个顺序表
*/
void InitList(SqList *L){
//防止数组出现初始值错误
for (int i = 0; i < MaxSize; ++i) {
L->data[i]=0;
}
//顺序表的初始长度为0
L->length=0;
}
/**
* 创建一个线性表 --n是数组a[]的大小
*/
int CreatList(SqList *L,int a[],int n){
if(n>MaxSize){
printf("空间不够,请重新分配空间\n");
return 0;
}
//将数组a的值一个一个的赋值给data数组
for (int i = 0; i <n; ++i) {
L->data[i]=a[i];
}
//需要改变数组data的长度length值
L->length=n;
return 1;
}
/**
* 修改顺序表中的元素 --index是位序,value是待修改的值并返回修改的值
*/
int ModifyList(SqList *L,int index,int value){
//首先对index进行判断
if(index<1||index>L->length){
return 0;
}
L->data[index-1]=value;
return L->data[index-1];
}
/**
* 打印顺序表中的元素
*/
void PrintList(SqList *L){
printf("顺序表的元素=[");
for (int i = 0; i < L->length; ++i) {
if(i< L->length-1){
printf("%d,",L->data[i]);
} else{
printf("%d",L->data[i]);
}
}
printf("]\n");
}
/**
* 在表L的第i个位置上插入指定的元素e --i指得是位序
*/
int ListInsert(SqList *L,int i,int e){
//首先进行位置判断
if(i<1||i>L->length){
return 0;
}
//判断是否超出定义数组的最大长度
if(L->length>MaxSize){
return 0;
}
//进行插入操作--将第i给位置以及后面的元素均往后移动一个单位
for (int j =L->length; j >=i; j--) {
L->data[j]=L->data[j-1];
}
//将待插入的元素给指定的位置
L->data[i-1]=e;
//数组的长度加1
L->length++;
return 1;
}
/**
* 顺序表的删除--删除第index个元素(位序)并返回待删除的元素
*/
int ListDelete(SqList *L,int index){
//对index进行判断
int e;//记录代删除的元素值
if(index<1||index>L->length){
return 0;
}
e=L->data[index-1];
for (int i = index; i <L->length ; ++i) {
L->data[i-1]=L->data[i];
}
//没删除一个元素后对数组的长度进行--操作
L->length--;
return e;
}
/**
* 顺序表的查找 按位(位序)查找
*/
int GetElem(SqList *L,int i){
return L->data[i-1];
}
/**
* 顺序表的查找 按值查找,并返回其位序
*/
int LocateElem(SqList *L,int value){
for (int i = 0; i < L->length; ++i) {
if(L->data[i]==value){
return i+1;
}
}
return 0;
}
/**
* 判空操作
*/
int ListEmpty(SqList *L){
if(L->length==0){
return 1;
} else{
return 0;
}
}
int main(){
SqList L; //声明一个顺序表
InitList(&L);
int n=6;
int a[n];
int e;//用来接收删除元素的值
for (int i = 0; i < n; ++i) {
a[i]=i;
}
if(CreatList(&L,a,n)==1){
printf("创建数组成功!\n创建的");
} else{
printf("创建数组失败!\n");
}
PrintList(&L);
if(ListInsert(&L,3,3)==1){
printf("插入一个元素成功!\n");
} else{
printf("插入一个元素失败!\n");
}
PrintList(&L);
printf("查找了元素为3的位序=%d\n", LocateElem(&L,3));
if( (e=ListDelete(&L,4))!=0){
printf("删除成功!\n");
printf("删除了第四个元素,元素的值=%d\n删除之后",e);
PrintList(&L);
} else{
printf("删除失败!\n");
}
if(ListEmpty(&L)==0){
printf("顺序表的长度=%d\n",L.length);
} else{
printf("顺序表是空的!");
}
int modeifValue=L.data[2];
if((e=ModifyList(&L,3,15))!=0){
printf("修改前的第三个元素=%d\n",modeifValue);
printf("修改成功!\n");
printf("修改后的第三个元素=%d\n",e);
}
return 0;
}
2:时间复杂度的分析
- 插入函数时间复杂度分析
int ListInsert(SqList *L,int i,int e){
//首先进行位置判断
if(i<1||i>L->length){
return 0;
}
//判断是否超出定义数组的最大长度
if(L->length>MaxSize){
return 0;
}
//进行插入操作--将第i给位置以及后面的元素均往后移动一个单位
for (int j =L->length; j >=i; j--) {
L->data[j]=L->data[j-1];
}
//将待插入的元素给指定的位置
L->data[i-1]=e;
//数组的长度加1
L->length++;
return 1;
}
-
最好时间复杂度:当插入元素的位置在静态数组的末尾,T(n)=O(1)
-
最坏时间复杂度:当插入的元素在静态数组的开头,T(n)=O(n)
-
平均时间复杂度: 假设新元素插入到任何一个位置的概率相同,即i 的值为1,2,3,…,length+1的概率都是p=1/n:
-
i=1,循环n次;
-
i=2,循环n-1次;i=3,i=4,…
-
i=n+1时,循环0次
平均循环环次数=1/n*[n+(n-1)+…+2+1+0]=n/2
T(n)=O(n)
- 删除操作时间复杂度分析
int ListDelete(SqList *L,int index){
//对index进行判断
int e;//记录代删除的元素值
if(index<1||index>L->length){
return 0;
}
e=L->data[index-1];
for (int i = index; i <L->length ; ++i) {
L->data[i-1]=L->data[i];
}
//没删除一个元素后对数组的长度进行--操作
L->length--;
return e;
}
-
最好时间复杂度:当删除元素的元素在静态数组的末尾,T(n)=O(1)
-
最坏时间复杂度:当删除元素的元素在静态数组的开头,T(n)=O(n)
-
平均时间复杂度: 假设删除任意元素的概率相同,即i 的值为1,2,3,…,length+1的概率都是p=1/n:
-
i=1,循环n-1次;
-
i=2,循环n-2次;i=3,i=4,…
-
i=n+1时,循环0次
平均循环环次数=1/n*[(n-1)+…+2+1+0]=n*(n-1)/2*1/n=(n-1)/2
T(n)=O(n)