单链表-顺序存储

本文详细介绍了单链表的顺序存储结构,包括如何进行获得元素、插入和删除操作,以及其优缺点。顺序存储结构允许随机访问,存取时间性能为O(1)。但插入和删除操作可能导致大量元素移动,且当线性表长度变化大时,存储空间管理困难。

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

单链表-顺序存储

本文参考自《大话数据结构》,有兴趣的读者可以自行阅读。

顺序存储结构

#define MAXSIZE 20	//存储空间初始分配量
typedef int ElemType;	//ElemType类型根据实际情况而定,这里假设为int
typedef struct
{
    ElemType data[MAXSIZE];	//数组存储数据元素,最大值为MAXSIZE
    int length;	//线性表当前长度
}SqList;

对于第i个数据元素ai的存储位置可以由a1推算得出:

*LOC(ai)=LOC(a1)+(i-1)c;

通过这个公式可以随时算出线性表中任意位置的地址,不管是第一个还是最后一个,都是相同的时间。那么我们对每个线性表位置的存入或者取出数据,对于计算机来说都是相等的时间,也就是一个常数,因此用我们算法中学到的时间复杂度的概念来说,它的存取时间性能为O(1)。我们通常把具有这一特点的存储结构成为随机存取结构

获得元素操作

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
/* 初始条件:顺序线性表L已存在,1<=ListLength(L) */
/* 操作结果:用e返回L中第i个数据元素的值 */
Status GetElem(SqList L,int i,ElemType *e)
{
    if(L.length == 0 || i<1 ||L.length)
        return ERROR;
    *e = L.data[i-1];
    return OK;
}

插入操作

思路:

  • 如果插入位置不合理,抛出异常;
  • 如果线性表长度大于等于数组长度,则抛出异常或动态增加容量;
  • 从最后一个元素开始向前遍历到第i个位置,分别将它们都向后移动一个位置;
  • 将要插入元素填入位置i处;
  • 表长加1;
/* 初始条件:顺序线性表L已存在,1<=i<=ListLength(L) */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
Status ListInsert(SqList *L, int i, ElemType e)
{
    int k;
    if(L->length == MAXSIZE)	//顺序线性表已经满
        return ERROR;
    if(i<1 || i>L->length+1)	//当i不在范围内时
        return ERROR;
    if(i<=L->length)	//若插入数据位置不在表尾
    {
        for(k = L->length-1; k>= i-1; k--)	//将要插入位置后数据元素向后移动一位
            L->data[k+1] = L->data[k];
    }
    L->data[i-1] = e;	//将新元素插入
    L->length++;
    return OK;
}

删除操作

思路:

  • 如果删除位置不合理,抛出异常;
  • 取出删除元素;
  • 从删除元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一个位置;
  • 表长减1;
/* 初始条件:顺序线性表L已存在,1<=i<=ListLength(L) */
/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
Status ListDelete(SqList *L, int i, ElemType *e)
{
    int k;
    if(L->length == 0)	//线性表位空
        return ERROR;
    if(i<1 || i>L->length)	//删除位置不正确
        return ERROR;
    *e = L->data[i-1];
    if(i<L->length)	//如果删除不是最后位置
    {
        for(k=i;k<L->length;k++)	//将删除位置后继元素前移
            L->data[k-1] = L->data[k];
    }
    L->length--;
    return OK;
}

优点

  • 无须为表示表中元素之间的逻辑关系而增加额外的存储空间;
  • 可以快速地存取表中任一位置的元素;

缺点

  • 插入和删除操作需要移动大量元素;
  • 当线性表长度变化较大时,难以确定存储存储空间的容量;
  • 造成存储空间的"碎片";

代码实现

#include <stdio.h>

#define MAXSIZE 20	//存储空间初始分配量
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int ElemType;	//ElemType类型根据实际情况而定,这里假设为int

//结构体---单链表顺序存储
typedef struct
{
	ElemType data[MAXSIZE];	//数组存储数据元素,最大值为MAXSIZE
	int length;	//线性表当前长度 
}SqList;
	
//指针方式获取元素 
//Status getElem(SqList* list, int i, ElemType* e){
//	if(i <= 0){
//		printf("索引值小于0,不存在该数据\n");
//		return ERROR;
//	}else if(i > list->length){
//		printf("索引值超出边界\n");
//		return ERROR;
//	}else{
//		*e = list->data[i-1];
//		printf("*e:%d\n",*e);
//		return TRUE;
//	}
//}

//获取元素 
Status getElem(SqList list, int i, ElemType* e){
	if(i <= 0){
		printf("索引值小于1,不存在该数据\n");
		return ERROR;
	}else if(i > list.length){
		printf("索引值超出边界\n");
		return ERROR;
	}else{
		*e = list.data[i-1];
		return TRUE;
	}
}

//插入
Status insertElem(SqList* list, int i, ElemType e){
	int j;
	if(i<=0){
		printf("插入的位置小于1,非法插入!\n");
		return ERROR;
	}else if(i>list->length+1){
		printf("插入的位置大于链表长度,非法插入\n");
		return ERROR;
	}else{
//		list->length++;
		for(j=list->length-1;j>=i-1;j--){
			list->data[j+1] = list->data[j];
		}
		list->data[i-1] = e;
		list->length++;
		printf("当前list长度:%d\n",list->length);
		return TRUE;
	}
}

//删除
Status deleteElem(SqList* list, int i){
	int j;
	if(i<1){
		printf("索引值小于1,非法删除!\n");
		return ERROR; 
	}else if(i>list->length){
		printf("索引值超过链表长度,非法删除!\n");
		return ERROR;
	}else{
		for(j=i-1;j<list->length;j++){
			list->data[j] = list->data[j+1];
		}
		list->length--;
		return TRUE;
	}
}

//主函数 
int main(){
	Status continueFlag = TRUE;	//是否继续循环菜单,默认循环 
	int i;	//索引
	int initNum;	//初始化数值数
	int insertNum, insertSize;	//插入的数字和插入的位置
	int deleteSize;	//删除的数的位置 
	int inputNum; 	//查找第几个 
	int functionNum;	//功能号 
	ElemType init = 1; 	//指针初始化 
	ElemType* e = &init;
	//需初始化,不然会出错
	//	ElemType* e;
	SqList sqList;
	//指针
	SqList* list = &sqList; 
	printf("输入要初始化的整型值的个数(不超过20个):\n");
	scanf("%d",&initNum);
	printf("输入%d个整型数字(不超过20个,空格分隔):\n",initNum);
	for(i=0;i<initNum;i++){
		scanf("%d", &sqList.data[i]);
	}
	sqList.length = initNum;
	for(i=0;i<sqList.length;i++)	//初始化检测
		printf("链表的第%d个数据是:%d\n",i+1,sqList.data[i]);
	printf("初始化完成!\n");
	while(continueFlag){
		printf("-----------菜单----------\n");
		printf("----------1.查找--------\n");
		printf("----------2.插入--------\n");
		printf("----------3.删除--------\n");
		printf("----------4.退出--------\n");
		printf("请输入功能号:\n");
		scanf("%d", &functionNum);
		switch(functionNum){
			case 1:
				printf("------------查找-----------\n");
				printf("输入数据在链表中的位置:\n");
				//输入一个数 
				scanf("%d", &inputNum);
				//查找 
				if(getElem(sqList, inputNum, e) == TRUE)
				printf("第%d个元素的值为:%d\n",inputNum,*e);
				break;
			case 2:
				//插入
				printf("------------插入-----------\n");
				printf("输入要插入的位置和数(空格分隔):\n");
				scanf("%d %d",&insertSize, &insertNum);
				if(insertElem(list, insertSize, insertNum) == TRUE){
					printf("插入成功!第%d个数是%d.\n",insertSize,sqList.data[insertSize-1]);
					for(i=0;i<sqList.length;i++)
						printf("链表的第%d个数据是:%d\n",i+1,sqList.data[i]);
				}
				break;
			case 3:
				//删除
				printf("------------删除-----------\n");
				printf("输入要删除的位置:\n");
				scanf("%d",&deleteSize);
				if(deleteElem(list, deleteSize) == TRUE){
					printf("删除成功!\n");
					for(i=0;i<sqList.length;i++){
						printf("链表的第%d个数据是:%d\n",i+1,sqList.data[i]);
					}
				} 
				break; 
			case 4:
				continueFlag = FALSE;
				printf("感谢使用!");
				break;
			default:
				printf("功能号不存在,请重新输入!\n");
				break;
		}
	}
	return 0;
}

运行结果

单链表-顺序结果实验结果1
单链表-顺序结果实验结果2

### 单链表顺序存储结构的关系分析 单链表不属于顺序存储结构[^1]。为了更清晰地理解这一点,可以从两者的定义及其特性出发进行探讨。 #### 定义区分 - **顺序存储结构**是指利用一段连续的内存单元依次存放数据元素的方式。这种存储方法能够使逻辑上相邻的数据项在物理位置上也紧邻,便于随机访问任何指定索引处的节点[^2]。 - 而**单链表**(Singly Linked List),则是一种典型的非顺序存储形式——它借助指针将各个散列分布于内存中的结点串联起来形成整体列表。每个结点不仅保存着自身的数据域还维护指向下一个结点地址的信息字段即next指针[^1]。 因此可以看出,尽管它们都用来表达线性序列关系,但在实际实现原理上有本质差异:前者依赖固定大小数组模拟排列紧密型布局;后者依靠动态申请零散区块构建松耦合式链接网络。 #### 特性对比 | 特征 | 顺序存储结构 | 单链表 | |----------------|----------------------------------|-----------------------------------| | 插入删除效率 | 平均时间复杂度为\( O(n)\)[^2]| 只要找到合适位置即可完成增删, 时间复杂度接近 \(O(1)\), 不过前提是已知确切插入点前驱位置. 如果未知还需遍历寻找,则最坏情况下仍可能是 \(O(n)\). | | 查找速度 | 支持直接按位序号定位 | 必须从头开始逐一比较直至匹配成功为止 | | 存储空间利用率 | 提前预留可能导致未充分利用或满载溢出风险存在[^2] | 每次仅新增必要部分不会造成额外负担但因附加管理信息而稍微增大总体消耗 | 综上所述,虽然单链表顺序存储都能很好地解决某些特定应用场景下的需求问题,但由于设计哲学的不同决定了二者之间不存在归属关联。 ```python class Node: def __init__(self,value=None,next_node=None): self.value=value self.next=next_node def create_sll(values_list): # 创建单向链表函数示例 head,tail=None,None for val in values_list: newnode=Node(val) if not head: head=newnode tail=head else: tail.next=newnode tail=newnode return head ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值