线性表的顺序存储结构------顺序表(c语言附完整代码)


前言

线性表是最基本、最简单、也是最常用的一种数据结构。根据存储方式的不同可分为顺序表和链表,那么今天我就带大家学习一下线性表的顺序存储结构------顺序表,通过静态分配和动态分配两种不同的实现方式加深大家对顺序表的理解。(文章最后附有顺序表基本运算完整代码)

基础知识

对于一些刚接触到数据结构的同学,可能会对后面基本运算中的一些代码不太理解,故先给大家讲解一下这部分内容,以方便大家更容易理解后面的代码。

typedef的用法

为了编码方便,c语言允许为一个数据类型起一个别名。例如:

typedef int ElemType;//给int型数据起了一个别名ElemType

后面就可以直接用该别名定义变量,例如:

ElemType e;

等价于

int e;

同样的还可以给结构体类型定义别名,例如:

typedef struct 
{	
    ElemType data[MaxSize];		//存放顺序表元素
   	int length;					//存放顺序表的长度
} SqList;

上述代码为该种结构体类型起了一个别名SqList,后面可以直接用该别名定义变量,例如:

SqList *L;

上述代码定义了一个指向原结构体类型的指针L。

引用&

引用是c++对c语言的重要扩充。引用就是原变量的别名,引用变量本身没有自己的实际存储空间,对引用变量的操作,就是在操作原变量。在定义函数时使用引用的形参,就可以使实际参数发生改变。例如在后面代码中:

void InitList(SqList *&L)
{
	L=(SqList *)malloc(sizeof(SqList));	//分配存放线性表的空间
	L->length=0;//顺序表长度置0
}

初始化顺序表时,我们需要为顺序表申请一片空间,并且将length的值设置为0,为了使实参同时发生改变,这里使用了引用。
又如:

int ListLength(SqList *L)
{
	return(L->length);//返回length的值
}

在求顺序表的长度时,我们只需要获取length的值,不需要改变实参,所以这里没有使用引用。

malloc函数

malloc动态内存分配函数,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址

malloc函数的原型

extern void *malloc(unsigned int num_bytes);

malloc函数头文件

#include<malloc.h>

#include<stdlib.h>

malloc函数的返回的是无类型指针,所以在使用时一定要强制转换为所需要的类型。

L=(SqList *)malloc(sizeof(SqList));	//将指针返回值类型转换为SqList 型

在使用malloc开辟空间时,使用完成一定要释放空间,如果不释放会造内存泄漏。

free(L);//释放顺序表储存空间

free函数

free函数通常与malloc函数在一起使用,用于释放malloc函数分配的内存空间。
free 函数原型

void free(void *ptr);

free函数头文件

#include<malloc.h>

#include<stdlib.h>

使用方法

free(L);//释放指针L所指储存空间

一、定义

顺序表-----用顺序存储的方式实现线性表顺序存储。
把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。
声明顺序表(静态分配)

typedef struct 
{	
    ElemType data[MaxSize];		//存放顺序表元素
   	int length;					//存放顺序表的长度
} SqList;

定义一个结构体,包含两个成员变量,分别用来存放数据元素和顺序表长度,并为该结构体起了一个别名SqList,可以直接用SqList定义一个结构体变量。
声明顺序表(动态分配)

typedef struct
{
	ElemType *data;    //指示动态分配数组的指针
	int MaxSize;  //顺序表最大容量
	int length;   //顺序表当前容量
}SeqList;

定义一个结构体,包含三个成员变量,分别用来存放数据元素,顺序表最大容量,顺序表长度,并给这个结构体起了一个别名SeqList 。

二、特点

1.随机访问,即在O(1)时间内找到第i个元素
2.存储密度高,每个节点只存储数据元素
3.拓展容量不方便(即便采用动态分配的方式实现,拓展长度的时间复杂度 也比较高)
4.插入和删除不方便,需要移动大量元素
5.元素的逻辑顺序与物理顺序相同

三、基本运算

静态分配

初始化顺序表

void InitList(SqList *&L)
{
	L=(SqList *)malloc(sizeof(SqList));	//分配存放线性表的空间
	L->length=0;//顺序表长度置0
}

使用malloc函数申请sizeof(SqList)大小的存储空间,并用指针L指向这片空间,将顺序表的长度初始化为0
本算法的时间复杂度为O(1)

销毁顺序表

void DestroyList(SqList *&L)
{
	free(L);//释放顺序表储存空间
}

使用free函数释放指针L所指存储空间
本算法的时间复杂度为O(1)

判断顺序表是否为空表

bool ListEmpty(SqList *L)
{
	return(L->length==0);//若表L的长度为0,返回1,否则返回0
}

该函数的返回值类型为布尔型,返回值为0或1,当L->length的值为0时,表示此时顺序表为空表,返回1;当 L->length的值不为0时,表示此时线性表非空,返回0
本算法的时间复杂度为O(1)

求顺序表的长度

int ListLength(SqList *L)
{
	return(L->length);//返回length的值
}

直接返回length的值
本算法的时间复杂度为O(1)

输出顺序表

void DispList(SqList *L)
{
	for (int i=0;i<L->length;i++)
		printf("%d ",L->data[i]);//循环打印L->data[i]的值
	printf("\n");
}

当 i小于线性表长度时,循环输出L->data[i]的值
本算法的时间复杂度为O(n)

求顺序表中某个位置元素的值

bool GetElem(SqList *L,int i,ElemType &e)
{
	if (i<1 || i>L->length)//判断查找位置是否正确
		return false;
	e=L->data[i-1];
	return true;
}

先判断所求位置是否合法,如果不合法则返回false,如果合法则将L-data[i]的值赋给e,并返回true。该运算可在O(1)时间复杂度内找到第i个数据元素,体现了顺序表的随机访问特性。
本算法的时间复杂度为O(1)

按元素的值查找

int LocateElem(SqList *L, ElemType e)
{
	int i=0;
	while (i<L->length && L->data[i]!=e) 
	   i++;//当i<L-length而且L->data[i]的值不等于e时继续查找
	if (i>=L->length)
		return 0;//i大于等于顺序表的长度时表示未找到,返回0
	else
		return i+1;//返回在顺序表中的位置
}

定义变量i并初始化为0,当 i 小于线性表的长度而且data[ i ] 的值不等于e时,继续查找,当循环结束时有两种情况,如果 i >=L->length,则说明未找到值为e的元素,返回0。如果i<length,则证明找到了值为e的元素,返回该值在顺序表中的位置 i+1。(因为i表示的是数组下标,而数据元素在顺序表中的位序比数组下标大1,故最后返回 i+1,而不是 i)
本算法的时间复杂度为O(n)

插入数据元素

bool ListInsert(SqList *&L,int i,ElemType e)
{
	int j;
	if (i<1 || i>L->length+1)//判断插入位置是否正确
		return false;
	i--;						//将顺序表位序转化为elem下标
	for (j=L->length;j>i;j--) 	//将data[i]及后面元素后移一个位置
		L->data[j]=L->data[j-1];
	L->data[i]=e;
	L->length++;				//顺序表长度增1
	return true;
}

首先判断插入位置是否合法,然后将插入位置顺序表位序转化为数组位序,
从最后一个元素开始将data[ i ]及后面元素往后移一个位置,最后将e的值赋给data[ i ],将顺序表的长度加 1。
本算法的时间复杂度为O(n)
动画演示:
在这里插入图片描述

删除数据元素

bool ListDelete(SqList *&L,int i,ElemType &e)
{
	int j;
	if (i<1 || i>L->length)
		return false;
	i--;						//将顺序表位序转化为elem下标
	e=L->data[i];
	for (j=i;j<L->length-1;j++)	//将data[i]之后的元素前移一个位置
		L->data[j]=L->data[j+1];
	L->length--;				//顺序表长度减1
	return true;
}

首先判断删除元素的位置是否合法,然后将删除元素的顺序表位序转化为数组位序,将data[ i ]的值赋给e,从data[ i ]后面的元素开始将data[ i ]之后的元素前移一个位置,最后将顺序表的长度减 1。
本算法的时间复杂度为O(n)
动画演示:
在这里插入图片描述

动态分配

初始化顺序表

void InitList(SeqList &L)
{
	L.data=(ElemType *)malloc(InitSize*sizeof(ElemType));//申请一片连续的空间
	L.length=0;                               //当前长度为0
	L.MaxSize=InitSize;                //顺序表最大容量为MaxSize
}

申请一片连续的存储空间,并用L.data指向这片空间,将顺序表的最大容量设置为InitSize ,将顺序表的长度置0。
本算法的时间复杂度为O(1)

拓展顺序表的容量

void IncreaseSize(SeqList &L,int len)
{
	ElemType *p=L.data;
	L.data=(ElemType *)malloc((L.MaxSize+len)*sizeof(ElemType));//重新申请一块连续的空间
	for(int i=0;i<L.length;i++)
	   L.data[i]=p[i];//将原存储空间的值复制到新的空间
	L.MaxSize=L.MaxSize+len;//修改顺序表最大容量
	free(p);//释放原存储空间
}

首先定义一个指针p 指向L.data所指存储空间,重新申请一片(L.MaxSize+len)*sizeof(ElemType)大小的存储空间,并用L .data指向它,
将原存储空间的数据元素复制到新的存储区域,然后修改顺序表的最大容量,最后释放原存储空间(即p所指存储空间)。
本算法的时间复杂度为O(n)

销毁顺序表

void DestroyList(SeqList &L)
{
	free(L.data);//释放指针所指存储空间
	L.data=NULL;
	L.length=0;//顺序表当前长度置0
	L.MaxSize=0;//顺序表最大容量置0
}

释放L.data所指存储空间,然后重置 L.data为NULL,在调用free函数之后,虽然归还了内存,但是L.data中仍然指向原来的地址,而这个地址在归还内存之后程序便无权进行访问,所以此时L.data就成了一个野指针,重置L.data为NULL就是为了防止发生野指针访问的情况,最后将顺序表的长度和最大容量置0。

本算法的时间复杂度为O(1)

判断顺序表是否为空表

bool ListEmpty(SeqList L)
{
	return(L.length==0);//线性表长度为0即为空表,返回1,否则返回0
}

本算法的时间复杂度为O(1)

求顺序表的长度

int ListLength(SeqList L)
{
	return(L.length);
}

本算法的时间复杂度为O(1)

输出顺序表

void DispList(SeqList L)
{
	for (int i=0;i<L.length;i++)
		printf("%d ",L.data[i]);
	printf("\n");
}

本算法的时间复杂度为O(n)

求顺序表中某个位置元素的值

bool GetElem(SeqList L,int i,ElemType &e)
{
	if (i<1 || i>L.length)//判断位置是否正确
		return false;
	e=L.data[i-1];
	return true;
}

本算法的时间复杂度为O(1)

按元素的值查找

int LocateElem(SeqList L, ElemType e)
{
	int i=0;
	while (i<L.length && L.data[i]!=e) i++;
	if (i>=L.length)
		return 0;
	else
		return i+1;
}

本算法的时间复杂度为O(n)

插入数据元素

bool ListInsert(SeqList &L,int i,ElemType e)
{
	int j;
	if (i<1 || i>L.length+1)//判断插入位置是否合法
		return false;
	if(L.length>=L.MaxSize)
	    IncreaseSize(L,Len);
	i--;						//将顺序表位序转化为elem下标
	for (j=L.length;j>i;j--) 	//将data[i]及后面元素后移一个位置
		L.data[j]=L.data[j-1];
	L.data[i]=e;
	L.length++;				//顺序表长度增1
	return true;
}

与静态分配顺序表不同,动态分配顺序表在进行插入操作时需要先判断当前顺序表的长度是否达到最大容量,如果达到最大容量,可以调用IncreaseSize函数增加顺序表的最大容量。
本算法的时间复杂度为O(n)

删除数据元素

bool ListDelete(SeqList &L,int i,ElemType &e)
{
	int j;
	if (i<1 || i>L.length)
		return false;
	i--;						//将顺序表位序转化为elem下标
	e=L.data[i];
	for (j=i;j<L.length-1;j++)	//将data[i]之后的元素前移一个位置
		L.data[j]=L.data[j+1];
	L.length--;				//顺序表长度减1
	return true;
}

本算法的时间复杂度为O(n)

四、小结

在这里插入图片描述

完整代码

静态分配

#include <stdio.h>
#include <malloc.h>
#define MaxSize 50
typedef int ElemType;   //将数据元素类型设为int  
typedef struct 
{	ElemType data[MaxSize];		//存放顺序表元素
   	int length;					//存放顺序表的长度
} SqList;						//顺序表的类型
void CreateList(SqList *&L,ElemType a[],int n)
//建立顺序表
{
	L=(SqList *)malloc(sizeof(SqList));
	for (int i=0;i<n;i++)
		L->data[i]=a[i];
	L->length=n;
}
void InitList(SqList *&L)
{
	L=(SqList *)malloc(sizeof(SqList));	//分配存放线性表的空间
	L->length=0;
}
void DestroyList(SqList *&L)
{
	free(L);
}
bool ListEmpty(SqList *L)
{
	return(L->length==0);
}
int ListLength(SqList *L)
{
	return(L->length);
}
void DispList(SqList *L)
{
	for (int i=0;i<L->length;i++)
		printf("%d ",L->data[i]);
	printf("\n");
}
bool GetElem(SqList *L,int i,ElemType &e)
{
	if (i<1 || i>L->length)
		return false;
	e=L->data[i-1];
	return true;
}
int LocateElem(SqList *L, ElemType e)
{
	int i=0;
	while (i<L->length && L->data[i]!=e) i++;
	if (i>=L->length)
		return 0;
	else
		return i+1;
}
bool ListInsert(SqList *&L,int i,ElemType e)
{
	int j;
	if (i<1 || i>L->length+1)
		return false;
	i--;						//将顺序表位序转化为elem下标
	for (j=L->length;j>i;j--) 	//将data[i]及后面元素后移一个位置
		L->data[j]=L->data[j-1];
	L->data[i]=e;
	L->length++;				//顺序表长度增1
	return true;
}
bool ListDelete(SqList *&L,int i,ElemType &e)
{
	int j;
	if (i<1 || i>L->length)
		return false;
	i--;						//将顺序表位序转化为elem下标
	e=L->data[i];
	for (j=i;j<L->length-1;j++)	//将data[i]之后的元素前移一个位置
		L->data[j]=L->data[j+1];
	L->length--;				//顺序表长度减1
	return true;
}
int main()
{
	SqList *L;
	ElemType e;
	InitList(L); 
	ElemType a[]={1,2,3,4,5};
	for(int i=0;i<5;i++)
	   L->data[i]=a[i];   //给顺序表赋值1,2,3,4,5 
	L->length=5;          //将顺序表的长度设置为5 
	printf("当前顺序表元素:");
	DispList(L);          //输出当前顺序表元素 
	printf("当前表是否为空:%d\n",ListEmpty(L));   //判断顺序表是否为空 
	ListInsert(L,5,6);           //在 顺序表第5个元素前插入元素6 
	printf("插入后顺序表元素: ");
	DispList(L);                //输出当前顺序表元素 
	printf("删除后顺序表元素: ");
	ListDelete(L,1,e);           //删除顺序表中的第一个元素,并将值赋给e 
	DispList(L);
	printf("当前顺序表的长度为:%d\n",ListLength(L));
	GetElem(L,3,e);              //求顺序表中第三个元素的值 
	printf("第3个元素的值为:%d\n",e);
	printf("值为4的元素的位置为:%d\n",LocateElem(L,4)); //求值为4的元素在顺序表中的位置 
	DestroyList(L);
	return 0;
}

动态分配

#include<malloc.h>
#include<stdio.h>
#define InitSize 10
#define Len 5          //设置每次最大容量的增加量
typedef int ElemType; //将顺序表的数据类型设为int 
typedef struct
{
	ElemType *data;   //指示动态分配数组的指针 
	int MaxSize; //顺序表的最大值 
	int length;  //顺序表的当前长度 
}SeqList;
void InitList(SeqList &L)
{
	L.data=(ElemType *)malloc(InitSize*sizeof(ElemType));
	L.length=0;
	L.MaxSize=InitSize;
}
void IncreaseSize(SeqList &L,int len)
{
	ElemType *p=L.data;
	L.data=(ElemType *)malloc((L.MaxSize+len)*sizeof(ElemType));
	for(int i=0;i<L.length;i++)
	   L.data[i]=p[i];
	L.MaxSize=L.MaxSize+len;
	free(p);
}
void DestroyList(SeqList &L)
{
	free(L.data);
	L.data=NULL;
	L.length=0;
	L.MaxSize=0;
}
bool ListEmpty(SeqList L)
{
	return(L.length==0);
}
int ListLength(SeqList L)
{
	return(L.length);
}
void DispList(SeqList L)
{
	for (int i=0;i<L.length;i++)
		printf("%d ",L.data[i]);
	printf("\n");
}
bool GetElem(SeqList L,int i,ElemType &e)
{
	if (i<1 || i>L.length)
		return false;
	e=L.data[i-1];
	return true;
}
int LocateElem(SeqList L, ElemType e)
{
	int i=0;
	while (i<L.length && L.data[i]!=e) i++;
	if (i>=L.length)
		return 0;
	else
		return i+1;
}
bool ListInsert(SeqList &L,int i,ElemType e)
{
	int j;
	if (i<1 || i>L.length+1)
	    return false;
	if(L.length>=L.MaxSize)
	    IncreaseSize(L,Len);
	i--;						//将顺序表位序转化为elem下标
	for (j=L.length;j>i;j--) 	//将data[i]及后面元素后移一个位置
		L.data[j]=L.data[j-1];
	L.data[i]=e;
	L.length++;				//顺序表长度增1
	return true;
}
bool ListDelete(SeqList &L,int i,ElemType &e)
{
	int j;
	if (i<1 || i>L.length)
		return false;
	i--;						//将顺序表位序转化为elem下标
	e=L.data[i];
	for (j=i;j<L.length-1;j++)	//将data[i]之后的元素前移一个位置
		L.data[j]=L.data[j+1];
	L.length--;				//顺序表长度减1
	return true;
}

int main()
{
	SeqList L;
	ElemType e;
	InitList(L); 
	ElemType a[]={1,2,3,4,5};
	for(int i=0;i<5;i++)
	   L.data[i]=a[i];   //给顺序表赋值1,2,3,4,5 
	L.length=5;          //将顺序表的长度设置为5 
	printf("当前顺序表元素:");
	DispList(L);          //输出当前顺序表元素 
	printf("当前表是否为空:%d\n",ListEmpty(L));   //判断顺序表是否为空 
	ListInsert(L,5,6);           //在 顺序表第5个元素前插入元素6 
	printf("插入后顺序表元素: ");
	DispList(L);                //输出当前顺序表元素 
	printf("删除后顺序表元素: ");
	ListDelete(L,1,e);           //删除顺序表中的第一个元素,并将值赋给e 
	DispList(L);
	printf("当前顺序表的长度为:%d\n",ListLength(L));
	GetElem(L,3,e);              //求顺序表中第三个元素的值 
	printf("第3个元素的值为:%d\n",e);
	printf("值为4的元素的位置为:%d\n",LocateElem(L,4)); //求值为4的元素在顺序表中的位置 
	DestroyList(L);
	return 0;
}

参考资料:
谭浩强《c语言程序设计》
李春葆《数据结构教程》(第五版)
王道数据结构
csdn 博主Dream_飞翔

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hello_world&&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值