2.3 线性表的链式表示

本文深入探讨了线性表的链式表示方法,详细解释了如何通过链表实现线性表,包括节点结构、插入和删除操作,以及其在内存管理上的优势。

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

.单链表的定义
1.线性表的链式存储又称为单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素。
为了建立数据元素之间的线性关系对每个链表结点,除存放元素自身的信息外,还需要存放一个指向
其后继的指针。

2.单链表中结点类型的描述如下:
typedef struct LNode //定义单链表结点类型
{
	ElemType data; //数据域
	struct LNode *next;  //指针域
}LNode,*LinkList;

3.利用单链表可以解决顺序表需要大量连续存储单元的缺点,但单链表附加指针域,也存在浪费存
储空间的缺点。由于单链表的元素离散地分布在存储空间中,所以单链表是非随机存取的存储结构,
即不能直接找到表中某个特定的结点。查找某个特定的结点时,需要从表头开始遍历,依次查找。

4.引入头结点后,可以带来以下两个优点:
(1)由于第一个数据结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作和
在表的其他位置上的操作一致,无须进行特殊处理
(2)无论链表是否为空,其头指针都指向头结点的非空指针,因此空表和非空表的处理也就得到了统一

二、单链表上基本操作的实现
1.采用头插法建立单链表
该方法从一个空表开始,生成新结点,并将读取到的数据存放到新结点的数据域中,然后将新结点插
入到当前链表的表头,即头结点之后
LinkList List_HeadInsert(LinkList &L)//逆向建立单链表
{
	LNode *s;int x;
	L=(LinkList)malloc(sizeof(LNode));//创建头结点
	L->next=NULL;//初始为空链表
	scanf("%d,&x);//输入结点的值
	while(x!=9999)//输入9999表示结束
	{
		s=(LNode*)malloc(sizeof(LNode));//创建新结点
		s->data=x;
		s->next=L->next;
		L->next=s;//将新结点插入表中,L为头指针
		scanf("%d",&x);
	}
	return L;
}
采用头插法建立单链表时,读入数据的顺序与生成的链表中的元素的顺序是相反的。每个结点插入
的时间为O(1),设单链表长为n,则总时间复杂度为O(n)

2.采用尾插法建立单链表
该方法将新结点插入到当前链表的表尾,为此必须增加一个尾指针r,使其始终指向当前链表的尾结点
LinkList List_TailInsert(LinkList &L)//正向建立单链表
{
	int x;//设元素类型为整型
	L=(LinkList)malloc(sizeof(LNode));
	LNode *s,*r=L;//r为表尾指针
	scanf("%d",&x);//输入结点的值
	while(x!=9999)	//输入9999表示结束
	{
		s=(LNode *)malloc(sizeof(LNode));
		s->data=x;
		r->next=s;
		r=s;//r指向新的表尾结点
		scanf("%d",&x);
	}
	r->next=NULL;//尾结点指针空
	return L;
}
时间复杂度和头插法的相同

3.按序号查找结点值
在单链表中从第一个结点出发,顺指针next域逐个往下搜索,直到找到第i个结点为止,否则返回最
后一个结点指针域NULL
LNode *GetElem(LinkList L,int i)
{
	int j=1;//计数,初始为1
	LNode *p=L->next;//头结点赋给指针p
	if(i==0)
		return L;//若i等于0,则返回头结点
	if(i<1)
		return NULL;//若i无效,则返回头结点
	while(p&&j<i)
	{
		p=p->next;
		j++;
	}
	return p;
}
时间复杂度为O(n)

4.按值查找表结点
LNode *LocateElem(LinkList L,ElemType e)
{
	LNode *p=L->next;
	while(p!=NULL&&p->data!=e)
		p=p->next;
	return p;
}
时间复杂度为O(n)

5.插入结点操作
插入结点操作将值为x的结点插入到单链表的第i个位置上。先检查插入位置的合法性,然后找到待
插入位置的前驱结点,即第i-1个结点,再在其后插入新结点
s->next=p->next;
p->next=s;

6.删除结点操作
找到被删结点的前驱结点
q=p->next;//令q指向被删结点
p->next=q-next;
free(q);//释放结点的存储空间

7.求表长操作
从第一个结点开始顺序依次访问表中的每个结点,直到访问到空结点为止。

三、双链表
1.单链表结点中只有一个指向其后继的指针,使得单链表只能从头结点依次顺序地向后遍历。要访
问某个结点的前驱结点,只能从头开始遍历,访问后继结点的时间复杂度为O(1),访问前驱结点的时
间复杂度为O(n)。为了克服单链表的上述缺点引入了双链表。

2.双链表中结点类型的描述如下
typedef struct DNode//定义双链表结点类型
{
	ElemType data;//数据域
	struct DNode *prior,*next;//前驱和后继指针
}DNode,*DLinklist;

3.双链表的插入操作
s->next=p->next;//将结点*s插入到*p之后
p->next->prior=s;
s->prior=p;
p->next=s;

4.双链表的删除操作
p->next=q->next;
q->next->prior=p;
free(q);

四、循环链表
1.循环单链表的判空条件不是头结点的指针是否为空,而它是否等于头指针。

2.循环双链表为空表时,其头结点的prior和next域都等于L,

五、静态链表
1.静态链表借助数组来描述线性表的链式存储结构,结点也有数据域data和指针域next,这里的指
针是结点的相对地址(数组下标),又称游标。和顺序表一样,静态链表也要预先分配一块连续的内
存空间。

2.静态链表结构类型的描述如下:
#define MaxSize 50 //静态链表的最大长度
typedef struct
{
	ElemType data;//存储数据元素
	int next;//下一个元素的数组下标
}SLinkList[MaxSize];

3.静态链表以next==-1作为其结束的标志。静态链表的插入、删除操作与动态链表的相似,只需要
移动指针,不需要移动元素。

六、顺序表和链表的比较
1.存取方式:顺序表可以顺序存取,也随机存取,链表只能从表头顺序存取元素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值