单链表-顺序存储

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

单链表-顺序存储

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

顺序存储结构

#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. 无序链表的合并 当两组链表无需保持任何特定顺序时,可以直接通过修改指针的方式完成合并。具体方法如下: - 首先遍历第一个链表直到找到其最后一个节点。 - 将该节点的 `next` 指向第二个链表的第一个节点即可完成整个过程[^1]。 以下是基于 Python 的代码示例来展示这一逻辑: ```python class ListNode: def __init__(self, value=0, next=None): self.value = value self.next = next def merge_unsorted_lists(headA, headB): if not headA: return headB if not headB: return headA current_node_A = headA while current_node_A and current_node_A.next is not None: current_node_A = current_node_A.next # Connect the end of list A to the start of list B. current_node_A.next = headB return headA ``` #### 2. 有序链表的合并 如果目标是将两个已经按升序排列好的单链表合并成一个新的同样有序的链表,则需要采用一种更复杂的策略。通常会利用递归或者迭代的方法逐一比较两个输入列表中的元素大小,并构建新的链接关系。 下面是一个使用迭代法的例子,在此过程中创建了一个辅助哑元节点作为新链表头部起点以便简化边界条件处理: ```python def merge_sorted_lists(l1, l2): dummy = ListNode(-1) tail = dummy while l1 and l2: if l1.value < l2.value: tail.next = l1 l1 = l1.next else: tail.next = l2 l2 = l2.next tail = tail.next # Append any remaining nodes from either list. tail.next = l1 or l2 return dummy.next ``` 上述函数实现了对两条已排序链表的有效组合,最终返回的是一个全新的、仍然维持原有次序的新链表实例对象。 --- #### 总结说明 无论是哪种类型的单链表合并任务,核心都在于理解并灵活运用指向机制调整各个节点之间的关联状态。针对不同需求场景选用恰当的技术手段能够显著提升程序性能表现与可读程度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值