2.3 线性表的链式映象

2.1 线性表的类型定义.
2.2 线性表的顺序映象.
2.3 线性表的链式映象.

一、单链表

用一组地址任意的存储单元存放线性表中的数据元素,以
元素(数据元素的映象) + 指针(指示后继元素存储位置的) = 结点(表示数据元素)
以 “结点的序列” 表示线性表——称作链表
以线性表中第一个数据元素 a 1 a_{1} a1的存储地址作为线性表的地址,称作线性表的头指针。

头结点

数据域 指针域

二、结点和单链表的C语言描述

typedef struct LNode{
	ElemType data; //数据域
	struct LNode * next; //指针域
}LNode, * LinkList,

三、单链表操作的实现

GetElem(L, pos, &e)

在链表中的实现:
基本操作为:使指针p始终指向线性表中第j个数据元素

Status GetElem_L(LinkList L, int pos, ElemType &e){
	//L为带头结点的单链表的头指针。
	//当第pos个元素存在时,其值赋给e并返回OK,否则返回ERROR
	p = L->next; 
	j = 1; //初始化,p指向第一个结点,j为计数器
	while(p && j<pos){//顺指针向后查找,直到p指向第pos个元素或p为空
		p = p->next; 
		++j;
	}
	if (!p || j>pos){
		return ERROR; //第pos个元素不存在
	}
	e = p->data;//取第pos个元素
	return OK;
}//GetElem_L

此算法时间复杂度为:
O ( L i s t L e n g t h ( L ) ) O(ListLength(L)) O(ListLength(L))

ListInsert(&L, i, e)

在链表中的实现:
基本操作为:
找到线性表中第i-1个结点,修改其指向后继的指针有序对 < a i − 1 , a i > <a_{i-1}, a_{i}> <ai1,ai>改变为 < a i − 1 , e > <a_{i-1}, e> <ai1,e> < e , a i > <e, a_{i}> <e,ai>

Status ListInsert_L(LinkList &L, int pos, ElemType e){
	//在带头结点的单链线性表L中第pos个位置之前插入元素e
	p = L;
	j = 0;
	while(p && j<pos-1){ //寻找第pos-1个结点
		p = p->next;
		++j;
	}
	if(!p || j>pos-1){ //pos小于1或者大于 表长+1
		return ERROR;
	}
	s = (LinkList)malloc(sizeof(LNode)); //生成新结点
	s->data = e; //将新结点插入链表L中
	s->next = p->next;
	p->next = s;
	return OK;
}//ListInsert_L 

此算法时间复杂度为:
O ( L i s t L e n g t h ( L ) ) O(ListLength(L)) O(ListLength(L))

ListDelete(&L, i, &e)

在链表中的实现:
基本操作为:
找到线性表中第i-1个结点,修改其指向后继的指针有序对 < a i − 1 , a i > <a_{i-1},a_{i}> <ai1,ai> < a i , a i + 1 > <a_{i}, a_{i+1}> <ai,ai+1>改变为 < a i − 1 , a i + 1 > <a_{i-1}, a_{i+1}> <ai1,ai+1>

Status ListDelete_L(linkList &L, int pos, ElemType &e){
	//在带头结点的单链线性表L中,删除第pos个元素,并由e返回其值
	p = L;
	j = 0;
	while(p->next && j<i-1){ //寻找第pos个结点,并令p指向其前趋
		p = p->next;
		++j;
	}
	if(!(p->next) || j>i-1){ //删除位置不合理
		return ERROR;
	}
	q = p->next;
	p.next = q->next;//删除结点
	e = q->data;
	free(q);//释放结点
	return OK.
}//ListDelete_L

此算法时间复杂度为:
O ( L i s t L e n g t h ( L ) ) O(ListLength(L)) O(ListLength(L))

CreateList(LinkList &L, int n)

void CreateList_L(LinkList &L, int n){
	//逆位序输入n个元素的值,建立带表头结点的单链线性表L。
	L = (LinkList)malloc(sizeof(LNode));
	L->next = NULL; //先建立一个带头结点的单链表
	for(i=n; i>0; --i){
		p = (LinkList)malloc(sizeof(LNode)); //生成新结点
		scanf(&p->data);//输人元素值
		p->next = L->next;//插入到表头
		L->next = p;
	}
}//CreateList_L

四、一个带头结点的线性链表类型

链表结构思考:
用上述定义的单链表实现线性表的操作时,存在的问题:
1、单链表的表长是一个隐含的值;
2、在单链表的最后一个元素最后插入元素时,需遍历整个链表;
3、在链表中,元素的“位序”概念淡化、结点的“位置”概念强化。

改进链表的设置:
1、增加“表长”、“表尾指针”和“当前位置的指针”三个数据域;
2、将基本操作由“位序”改为“指针”。

typedef struct LNode{ //结点类型
	ElemType data;
	struct LNode *next;
}*Link, *Position;

Status MakeNode(Link &p, ElemType e);
//分配由p指向的值为e的结点,并返回OK
//若分配失败,则返回ERROR

void FreeNode(Link &p);
//释放p所指结点

typedef struct{ //链表类型
	Link head, tail; //指向头结点和最后一个结点
	int len; //指示键表长度
	Link current; //指向当前访问的结点的指针,初始位置指向头结点
}LinkList;

链表的基本操作:

[结构初始化和销毁结构]

Status InitList(LinkList &L);
//构造一个空的线性链表L;头指针,尾指针和当前指针均指向头结点,表长为零

Status DestroyList(LinkList &L);
//销毁线性链表L, L不再存在

[引用型操作]

Status ListEmpty(LinkList L); //判表空

int ListLength(LinkList L); //求表长

Status Prior(LinkList L); //改变当前指针指向其前驱

Status Next(LinkList L); //改变当前指针指向其后继

ElemType GetCurElem(LinkList L); //返回当前指针所指数据元素

Status LocatePos(LinkList L, int i); //改变当前指针指向第i个结点

Status LocateElem(LinkList L, ElemType e, Status (*compare)(ElemType, ElemType));
//若存在与e满足函数compare()判定关系的元素,则移动当前指针,指向第1个满足条件的元素,并返回OK否則返回ERROR

Status ListTraverse(LinkList L, Status (*visit)()); //依次对L的每个元素调用函教visit()

[加工型操作]

Status ClearList(LinkList &L); //重置为空表

Status SetCurElem (LinkList &L, ElemType e); //更新当前指针所指数据元素

Status Append(LinkList &L, Link s); //一串结点链接在最后一个结点之后

Status InsAfter(LinkList &L, ElemType e); //将元素e插入在当前指针之后

Status DelAfter(LinkList &L, ElemType &e); //删除当前指针之后的结点
Status InsAfter(LinkList &L, ElemType e){
	//若当前指针在链表中,则将数据元素e插入在线性链表L中
	//当前指针所指结点之后,并返回OK;否则返回ERROR.
	if(!L.current){
		return ERROR;
	}
	if(!MakeNode(s, e)){
		return ERROR;
	}
	s->next = L.current->next;
	L.current->next = s;
	return OK
}//InsAfter
Status DelAfter(LinkList &L, ElemType &e){
	//若当前指针及其后继在链表中,则删除线性链表L中当前指针所指结点之后的结点,
	//并返回OK,否则返回ERROR。
	if (!(L.current && L.current->next)){
		return ERROR;
	}
	q = L.current->next;
	L.current->next = q->next;
	e = q->data;
	FreeNode(q);
	return OK;
}//DelAfter

利用上述定义的线性链表可以完成线性表的其它操作:
例一:

Status ListInsert_L(LinkList L, int i, ElemType e){
	//在帶头結点的单链线表L的第i个元素之前插入元素e
	if (!LocatePos(L,i-l)){ //i值不合法
		return ERROR;
	}
	if(InsAfter(L, e)){ //插入在第i-1个结点之后
		return OK;
	}else{
		return ERROR;
	}
}//ListInsert_L

例二:

void Mergelist_L(LinkList &La, LinkList &Lb, LinkList &Lc ,int (*compare)(Elemlype, ElemType)){
	if(!InitList(Lc)){
		return ERROR; //存储空间分配失败
	}
	LocatePos(La, 0); //当前指针指向头结点(设置当前指针位置)
	LocatePos(Lb, 0);

	if(DelAfter(La, e)){
		a = e;
	}else{
		a = MAXC; //MAXC为常量最大值
	}
	if(DelAfter(Lb, e)){
		b  = e;
	}else{
		b = MAXC; //a和b为两表中当前比较元素
	}
	while(!(a==MAXC && b==MAXC)){ //La或Lb非空
		if((*compare)(a, b) <= 0){
			InsAfter(Lc, a);
			if(DelAfter(La, e){
				a = e;
			}else{
				a = MAXC;
			}
		}else{ //a>b
			InsAfter(Lc, b);
			if(DelAfter(Lb, e){
				b = e;
			}else{
				b = MAXC;
			}
		}
	}
	DestroyList(La);
	DestroyList(Lb); //销毁链表La和Lb
	return OK:
}

五、其它形式的链表

1,双向链表
-线性表的双向链表存储结构-

typedef struct DuLNode{
	ElemType data, //数据域
	struct DuLNode *prior; //指向前驱的指针域
	struct DuLNode *next; //指向后继的指针域
}DuLNode, * DuLinkList;

p->next->prior = p = p->prior->next

2、循环链表
最后一个结点的指针域的指针又指回第一个结点的链表

2.4一元多项式的表示

一元多项式
p n ( x ) = p 0 + p 1 x + p 2 x 2 + . . . + p n x n p_{n}(x)=p_{0}+p_{1}x+p_{2}x^{2}+...+p_{n}x^{n} pn(x)=p0+p1x+p2x2+...+pnxn
在计算机中,可以用一个线性表来表示:
P = ( p 0 , p 1 , . . . , p n ) P=(p_{0}, p_{1},...,p_{n}) P=(p0,p1,...,pn)

S ( x ) = 1 + 3 x 10000 − 2 x 20000 S(x)=1+3x^{10000}-2x^{20000} S(x)=1+3x100002x20000

一般情况下的一元多项式可写成
P n ( x ) = p 1 x e 1 + p 2 x e 2 + . . . + p m x e m P_{n}(x) = p_{1}x^{e_{1}}+p_{2}x^{e_{2}} +...+ p_{m}x^{e_{m}} Pn(x)=p1xe1+p2xe2+...+pmxem
其中: p i p_{i} pi是指数为 e i e_{i} ei的项的非零系数,
0 ⩽ e 1 ⩽ e 2 ⩽ . . . ⩽ e m = n 0\leqslant e_{1}\leqslant e_{2}\leqslant ... \leqslant e_{m}=n 0e1e2...em=n

( ( p 1 , e 1 ) , ( p 2 , e 2 ) , . . . , ( p m , e m ) ) \Big( (p_{1},e_{1}),(p_{2},e_{2}),...,(p_{m},e_{m}) \Big) ((p1,e1),(p2,e2),...,(pm,em))

P 999 ( x ) = 7 x 3 − 2 x 12 − 8 x 999 P_{999}(x)=7x^{3} - 2x^{12} - 8x^{999} P999(x)=7x32x128x999
用这样的线性表 ( ( 7 , 3 ) , ( − 2 , 12 ) , ( − 8 , 999 ) ) \Big( (7,3),(-2,12),(-8,999) \Big) ((7,3),(2,12),(8,999)) 表示此多项式。

抽象数据类型一元多项式的定义如下:
ADT Polynomial {
数据对象:
D = { a i ∣ a i ∈ T e r m S e t ; i = 1 , 2 , . . , m ; m > 0 T e r m S e t 中的每个元素包含一个表示系数的实数和表示指数的整数 } \begin{aligned}D = \{&a_{i} | a_{i} \in TermSet; i=1,2,..,m; m>0 \\&TermSet中的每个元素包含一个表示系数的实数和表示指数的整数\}\end{aligned} D={aiaiTermSet;i=1,2,..,m;m>0TermSet中的每个元素包含一个表示系数的实数和表示指数的整数}

数据关系:
R 1 = { < a i − 1 , a i > ∣ a i − 1 , a i ∈ D , 且 a i − 1 中的指数值 < a i 中的指数值 , i = 2... , n } R1 = \{<a_{i-1},a_{i}> | a_{i-1},a_{i} \in D, 且a_{i-1}中的指数值<a_{i}中的指数值,i=2...,n \} R1={<ai1,ai>ai1,aiD,ai1中的指数值<ai中的指数值,i=2...,n}

基本操作:
creatPolyn(&P, m)
操作结果:输入m项的系数和指数,建立一元多项式P。
DestroyPolyn(&P)
初始条件:一元多项式P已存在。
操作结果:销毁一元多项式P。
PrintPolyn§
初始条件:一元多项式P已存在。
操作结果:打印输出一元多项式P。
PolynLength§
初始条件:一元多项式P已存在。
操作结果:返回一元多项式P中的项数。
AddPolyn(&Pa, &Pb)
初始条件:一元多项式Pa和Pb已存在。
操作结果:完成多项式相加运算,即:Pa = Pa + Pb,并销毁一元多项式Pb。
SubtractPolyn(&Pa, &Pb)
操作结果:
完成多项式相减运算,即:Pa = Pa - Pb,并销毁一元多项式Pb。
MultiplyPolyn(&Pa, &Pb)
初始条件:一元多项式Pa和Pb已存在。
操作结果:完成多项式相乘运算,即:Pa = Pa pb,并销毁一元多项式Pb。
} ADT Polynonial

例:
A 17 ( x ) = 7 + 3 x + 9 x 8 + 5 x 17 A_{17}(x)=7+3x+9x^{8}+5x^{17} A17(x)=7+3x+9x8+5x17

B 8 ( x ) = 8 x + 22 x 7 − 9 x 8 B_{8}(x)=8x+22x^{7}-9x^{8} B8(x)=8x+22x79x8

本章小结:
1、了解线性表的逻辑结构特性是数据元素之间存在着线性关系,在计算机中表示这种关系的两类不同的存储结构是顺序存储结构链式存储结构。用前者表示的线性表简称为顺序表,用后者表示的线性表简称为链表。
2、熟练掌握这两类存储结构的描述方法,以及线性表的各种基本操作的实现。
3、能够从时间和空间复杂度的角度综合比较线性表两种存储结构的不同特点及其适用场合。





以下内容待整理


//线性表的链式表示和实现
//线性表的单链表存储结构
# include <stdio.h>
# include <malloc.h>  //包含了malloc函数
# include <stdlib.h>  //包含了exit函数

typedef int ElemType;
typedef int Status;
#define OK 1
#define ERROR 0
	
typedef struct LNode{
	ElemType data;
	struct LNode * next;
} LNode, * LinkList;

Status AppendElem_L(LinkList &L,ElemType val){
	LNode * node = (LNode *)malloc(sizeof(LNode));
	node->data = val;
	

	return OK;
}

Status GetElem_L(LinkList L, int i, ElemType &e){
	// L为带 头结点 的单链表的头指针
	// 当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
	LNode * p = L->next; // 初始化,p指向第一个节点
	int j = 1; // j为计数器
	while(p && j<i){ // 顺指针向后查找,直到p指向第i个元素或p为空
		p = p->next;
		++j;
	}
	if(!p || j>i){ // 第i个元素不存在
		return ERROR;
	}
	e = p->data; // 取第i个元素
	return OK;
}

Status ListInsert_L(LinkList &L, int i, ElemType e){
	// 带头结点的单链表L中第i个位置之前插入元素e
	LinkList p = L;
	int j = 0;
	while(p && j<i-1){// 寻找第i-1个节点
		p = p->next;
		++j;
	}
	if(!p || j>i-1){// i小于1 或者 大于表长加1
		return ERROR;
	}
	LNode * s = (LNode *)malloc(sizeof(LNode));// 生产新结点
	s->data = e;
	s->next = p->next;// 插入L中
	p->next = s;
	return OK;
}

Status ListDelete_L(LinkList &L, int i, ElemType &e){
	//在带头节点的单链表L中,删除第i个元素,并由e返回其值
	LinkList p = L;
	int j = 0;
	while(p->next && j<i-1){//寻找第i个结点,并令p指向其前驱
		p = p->next;
		++j;
	}
	if(!(p->next) || j>i-1){//删除位置不合理
		return ERROR;
	}
	LNode * q = p->next;//从链表中删除指定结点并释放结点存储空间
	p->next = q->next;
	e = q->data;
	free(q);
	return OK;
}

int List_Length(LinkList L) {
	LinkList p = L->next;
	int len = 0;

	while (p) {
		p = p->next;
		len++;
	}
	return len;
}

void CreateList_L(LinkList &L, int n) {
	//逆位序输入n个元素的值,建立带表头结点的单链线性表L
	L = (LinkList)malloc(sizeof(LNode));
	L->next = NULL;//先建立一个带头结点的单链表
	for(int i = n; i>0; --i){
		LNode * p = (LNode *)malloc(sizeof(LNode));//生成新结点
		scanf("%d",&p->data);//输入元素值
		p->next = L->next;//插入到表头
		L->next = p;
	}
}

void TraverseList_L(LinkList pHead) {
	LinkList p = pHead->next;
	while (NULL != p) {
		printf("%d  ", p->data);
		p = p->next;
	}
	printf("\n");
	return;
}

void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc) {
	//已知单链线性表La和Lb的元素按值非递减排列
	//归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列
	LNode * pa = La->next;
	LNode * pb = Lb->next;
	LNode * pc;
	Lc = pc = La;//用La的头结点作为Lc的头结点
	while(pa && pb){
		if(pa->data <= pb->data){
			pc->next = pa;
			pc = pa;
			pa = pa->next;
		} else {
			pc->next = pb;
			pc = pb;
			pb = pb->next;
		}
	}
	pc->next = pa ? pa : pb;//插入剩余段
	free(Lb);//释放Lb的头结点
}

void main() {
	LinkList p;
	CreateList_L(p,5);
	TraverseList_L(p);


	//分配了一个不存放有效数据的头结点
	LinkList pHead = (LinkList)malloc(sizeof(LNode));
	pHead->next = NULL;
	int len = List_Length(pHead);
	printf("初始化表长 len = %d\n",len);

	ElemType e1 = 99;
	int i1 = 1;
	ListInsert_L(pHead, i1, e1);
	len = List_Length(pHead);
	printf("添加元素后 len = %d\n",len);

	ElemType e2 = 88;
	int i2 = 2;
	ListInsert_L(pHead, i2, e2);
	len = List_Length(pHead);
	printf("添加元素后 len = %d\n",len);

	ElemType e3;
	if(ListDelete_L(pHead, 1, e3)){
		printf("删除的元素是 e3 = %d\n",e3);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线性表链式存储结构是使用链式线性表中的元素存储起来。链表可以分为单链表、循环链表和双链表。在单链表中,每个结点都包含指向下一个结点的指针。循环链表是在单链表的基础上,将最后一个结点的指针指向头结点,形成一个循环。而双链表则是在单链表的基础上,每个结点都包含指向前一个结点和后一个结点的指针。链表的存储单元可以是连续的,也可以是非连续的,甚至是零散分布在内存的任何位置上。因此,链表中结点的逻辑顺序和物理顺序不一定相同。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [数据结构——线性表链式存储](https://blog.csdn.net/weixin_46272350/article/details/119612209)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [线性表链式存储结构..](https://download.csdn.net/download/N201871643/86035198)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值