[数据结构] [C语言] 线性表的顺序表示和实现

本文介绍了线性结构的特点和线性表的概念,重点讲解了线性表的顺序表示,使用C语言实现线性表的动态存储,包括插入和删除操作,并对算法进行了分析,指出在顺序存储结构中插入或删除元素平均需要移动一半元素,时间复杂度为O(n)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线性结构

线性结构的特点是:在数据元素的非空有限集中,(1)存在唯一一个被称作“第一个”的数据元素;(2)存在唯一一个被称作“最后一个”的数据元素;(3)除第一个之外,集合中的每个元素均只有一个前驱;(4)除最后一个之外,集合中每个数据元素均只有一个后继。

线性表

线性表是最常用且最简单的一种数据结构。一个线性表是n个数据元素的有限序列。线性表中元素的个数n(n≥0)定义为线性表的长度,n=0时称为空表。

线性表是一个相当灵活的数据结构,它的长度可以根据需要增长或缩短,即对线性表的数据元素不仅可以进行访问,还可以进行插入和删除等。

线性表的顺序表示和实现

线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素。

global.h
#ifndef GLOBAL_H
#define GLOBAL_H
#include<stdio.h>
#define OK 1             // 通过
#define ERROR 0          // 错误
#define OVERFLOW -2      // 堆栈上溢
typedef int Status;      // 函数类型,其值为状态码
typedef int ElemType;    // 抽象数据类型
#endif // !GLOBAL_H
存储结构

由于线性表的长度可变,且所需最大存储空间随问题不同而不同,在C语言中可用动态分配的一维数组,如下描述。

#define LIST_INIT_SIZE 10  // 线性表存储空间初始分配量
#define LISTINCREMRNT 10   // 线性表存储空间的分配增量

// 线性表的动态分配顺序存储结构
typedef struct SqList
{
	ElemType* elem;  // 存储空间基址
	int length;      // 当前长度
	int listsize;    // 当前分配的存储容量
}SqList;

上述定义中,数组指针elem指示线性表的基地址,length指示线性表的当前长度。顺序表的初始化操作就是为顺序表分配一个预定义大小的数组空间,并将线性表的当前长度设为“0”。listsize指示顺序表当前分配的存储空间大小,一旦因插入元素而空间不足时,可进行再分配,即增加一个大小为存储LISTINCREMRNT个数据元素的空间。

Status InitList_Sq(SqList* L)
{
	// 构造一个空的线性表L
	L->elem = (ElemType*)malloc(LIST_INIT_SIZE * sizeof(ElemType));
	if (!(L->elem))
		exit(OVERFLOW);// 存储分配失败
	L->length = 0;// 空表长度为0
	L->listsize = LIST_INIT_SIZE;// 初始存储容量
	return OK;
}// InitList_Sq
插入和删除

一般情况下,在第i(1≤i≤n)个元素之前插入一个元素时,需将第n至第i(共n-i+1)个元素向后移动一个位置。

// 在顺序线性表L中第i个位置之前插入新的元素e
Status ListInsert_Sq(SqList* L, int i, ElemType e)
{
	// 在顺序线性表L中第i个位置之前插入新的元素e
	// i的合法值为1<=i<=ListLength_Sq(L)+1
	if (i<1 || i>L->length + 1)
		return ERROR;// i值不合法
	if (L->length >= L->listsize) {// 当前存储空间已满,增加分配
		ElemType* newbase = (ElemType*)
			realloc(L->elem, (L->listsize + LISTINCREMRNT) * sizeof(ElemType));
		if (!newbase)// 存储分配失败
			exit(OVERFLOW);
		L->elem = newbase;// 新基址
		L->listsize += LISTINCREMRNT;// 增加存储容量
	}
	ElemType* q = &(L->elem[i - 1]), * p = NULL;// q为插入位置
	for (p = &(L->elem[L->length - 1]); p >= q; --p)
		* (p + 1) = *p;// 插入位置及之后的元素后移
	*q = e;// 插入e
	++(L->length);// 表长增加1
	return OK;
}// ListInsert_Sq

一般情况下,删除第i(1≤i≤n)个元素时需将从第i+1至第n(共n-i)个元素依次向前移动一个位置。

// 在顺序线性表L中删除第i个元素,并用指针e返回
Status ListDelete_Sq(SqList* L, int i, ElemType* e)
{
	// 在顺序线性表L中删除第i个元素,并用指针e返回
	// i的合法值为1<=i<=ListLength_Sq(L)
	if (i<1 || i>L->length)
		return ERROR;// i值不合法
	ElemType* p = &(L->elem[i - 1]), * q = NULL;// p为被删除元素的位置
	*e = *p;// 被删除元素的值赋给*e
	q = L->elem + L->length - 1;// 表尾元素的位置
	for (++p; p <= q; ++p)
		* (p - 1) = *p;// 被删除元素之前的元素前移
	--(L->length);// 表长减一
	return OK;
}// ListDelete_Sq
测试

测试代码:

int main() {
	SqList* L = (SqList*)malloc(sizeof(SqList));
	InitList_Sq(L);
	int i;
	for (i = 0; i < 15; i++) {
		ListInsert_Sq(L, 1, i);
	}
	PrintList_Sq(L);
	ElemType* e = (ElemType*)malloc(sizeof(ElemType));
	ListDelete_Sq(L, 5, e);
	printf("%d is del\n", *e);
	PrintList_Sq(L);
	return 0;
}

结果如下:
在这里插入图片描述

算法分析

在顺序存储结构的线性表中某个位置插入或者删除一个数据元素时,其时间主要耗费在移动元素上(换句话说,移动元素的操作为预估算法时间复杂度的基本操作),而移动元素的个数取决于插入或删除的元素的位置。

假设pi是在第i个元素之前插入一个元素的概率,则在长度为n的线性表中插入一个元素时所需移动元素次数的期望(平均次数)为:

假设qi是删除第i个元素的概率,则在长度为n的线性表中删除一个元素时所需移动元素次数的期望(平均次数)为:

不失一般性,假定在线性表的任何位置上插入或删除元素都是等概率的,即

于是可分别进行化简:

由上面两式可见,在顺序存储结构的线性表中插入或者删除一个数据元素,平均移动表中一半元素。若表长为n,则算法ListInsert_Sq和ListDelete_Sq的时间复杂度为O(n)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值