DS 0824(第二章 双链表等)

本文介绍了双链表的概念,强调了其相对于单链表的优缺点,包括带头节点双链表的初始化、插入和删除操作。接着讨论了循环链表,特别是循环双链表的特点和操作。最后,探讨了静态链表的定义、操作以及与顺序表的对比,指出其在特定场景下的适用性。

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

双链表

单链表与双链表对比

  1. 单链表只有一个指向后继结点的指针,所以如果给定结点p想要找到他的前趋结点很麻烦,无法逆向检索
  2. 双链表在单链表的基础上,增加了一个指针域,指向其前趋结点。可进可退,存储密度更低
typedef struct DNode{					//定义双链表结点数据类型 
	ElemType data;						//数据域 
	struct DNode *prior,*next;			//前趋和后继指针 
}DNode,*DLinkList;

带头节点双链表的初始化

typedef struct DNode{					//定义双链表结点数据类型 
	ElemType data;						//数据域 
	struct DNode *prior,*next;			//前趋和后继指针 
}DNode,*DLinkList;

bool InitDLinkList(DLinkList &L){
	L=(DNode *)malloc(sizeof(DNode));  //分配一个头节点
	if(L==NULL)
	return false;
	L->prior=NULL;						//头节点永远指向NULL 
	L->next=NULL;
	return true; 
} 

void testDLinkList(){
	//初始化双链表
	DLinkList L;
	InitDLinkList(L); 
	//后续代码 
}
}

//判断双链表是否为空
bool Empty(DLinkList L){
	if(L->next==NULL)
	return true;
	else
	return false;
} 

双链表的插入
1.在p结点之后插入s结点

bool InsertNextDNode(DNode *p,DNode *s){
	s->next=p->next;
	p->next->prior=s;		//p的后继结点的前向指针指向s 
	s->prior=p;
	p->next=s;
}

如果p是最后一个结点,则p-next-prior=s;这一句会出现问题

bool InsertNextDNode(DNode *p,DNode *s){
	if(p==NULL}||s==NULL)
	return false; 
	s->next=p->next;
	if(p->next!=NULL)		//如果p有后继结点 
	p->next->prior=s;		//p的后继结点的前向指针指向s 
	s->prior=p;				//跳过,就相当于,不需要调整p的后继结点的前向指针 
	p->next=s;
	return true; 
}

注意代码的顺序

**前插操作:**找到某结点的前趋,对他进行后插

双链表的删除

删除p的后继结点q

p->next=q->next;
q-next-prior=p;
free(q); 

如果q结点是最后一个结点

bool DeleteNextDNode(DNode *p){
	if(p==NULL)  return false;
	DNode *q=p->next;   		//找到p的后继结点q
	if(q==NULL)	 return false;  //p没有后继
	 p->next=q->next;
	 if(q->next!=NULL)          //q结点不是最后一个结点 
	 q->next->prior=p;
	 free(q);					//释放结点空间 
	 return true; 
}

双链表的销毁

void DestoryList(DLinklist &L){
	//循环释放各个数据结点
	while(L->next!=NULL)
	DeleteNextDNode(L);
	free(L);		//释放头节点 
	L=NULL; 		//头指针指向NULL 
} 

双链表的遍历

//后向遍历
while(p!=NULL){
	p=p->next;
	//对结点p的相应处理 
} 

//前向遍历
while(p!=NULL){
	p=p->prior;
	//对结点p做相应的处理 
} 

//前向遍历(跳过头结点)
while(p->prior!=NULL){
	p=p->prior;
	//对结点p做相应的处理 
} 

双链表不可随机存取,按位查找,按值查找的操作都只能使用遍历的方式实现,时间复杂度为O(n)

循环链表

在这里插入图片描述

  • 单链表:表尾结点next指针指向NULL
  • 循环单链表:表尾结点的next指针指向头结点
typedef struct LNode{
	ElemType data;			//定义单链表结点类型 
	struct LNode *next;		//每个结点存放一个数据元素 
}LNode,*LinkList; 			//指针指向下一个结点

//初始化一个循环链表
bool InitList(LinkList &L){
	L=(LNode*)malloc(sizeof(LNode)); //分配一个头结点 
	if(L==NULL) 
	return false;						//由于内存不够,分配失败
	L->next=L;						//头结点next指针指向头指针
	return true; 
} 

//判断循环单链表是否为空
bool Empty(LinkList L){
	if(L->next==L)
	return true;
	else
	return false;
} 

//判断扫描指针p是否是表尾结点

bool isTail(LinkList L,LNode *p){
	if(p->next==L)
	return true;
	else
	return false;
}
  • 从单链表的一个结点出发只能找到后续的各个结点
  • 从一个结点出发可以找到其他任何一个结点

循环双链表
在这里插入图片描述

  • 双链表:表头结点的prior指向NULL,表尾结点的next指向NULL
  • **循环双链表:**表头结点的prior指向表尾结点,表尾结点的next指向头结点
typedef struct DNode{
	ElemType data;
	struct DNode *prior,*next;
}DNode,*LinkList;

bool InitDlinkList(DLinkList &L){
	L=(DNode*)malloc(sizeof(DNode)); //分配一个头结点
	if(L==NULL)
	return false;
	L->prior=L;
	L->next=L;
	return true; 
}

//判断循环双链表是否为空
bool Empty(DLinkList L){
	if(L->next==L)
	return true;
	else
	return false;
} 

采用循环双链表时,删除,插入等操作,对于最后一个结点没有特殊
判空操作是遍历的while循环核心

静态链表

在这里插入图片描述
def:

  1. 在内存中分配一整片连续的内存空间
  2. 每个结点包括数据元素,以及下一个结点的数组下标(游标)
  3. 0号结点充当头结点
  4. 游标充当指针
  5. 游标的值设为-1及表示没有后继节点了

用代码定义一个静态链表

#define MaxSize 10	//静态链表的最大长度 
struct Node{		//静态链表结构类型的定义 
	Elemtype data;	//存储数据元素 
	int next;		//下一个元素的数组下标 
};

void testSLinkList(){
	struct Node a[MaxSize];  //数组a作为静态链表
	
	//后续代码 
} 

or

#define MaxSize 10	//静态链表的最大长度 
typedef struct Node{		//静态链表结构类型的定义 
	Elemtype data;	//存储数据元素 
	int next;		//下一个元素的数组下标 
}SLinkList[MaxSize];

void testSLinkList(){
	SLinkList b;  //数组a作为静态链表
	
	//后续代码 
} 

常用操作

  1. 查找:从头结点出发挨个往后遍历结点
  2. 插入位序为i的结点:1.找到一个空的结点,存入数据元素 2.从头结点出发找到位序为i-1的结点 3.修改新结点的next 4.修改i-1号结点的next

初始化时,应将空闲结点设置为某个特殊的值,方便于寻找判断空闲结点

3.删除:1.从头结点出发找到新的前趋结点 2.修改前趋结点的游标 3.被删除后的结点next设置为-2

**静态链表:**用数组的方式实现链表

  • 优点:增,删操作不需要大量移动元素
  • 缺点:不能随机存取,只能从头结点开始依次往后查
    容量固定不变

适用场景

  1. 不支持指针的低级语言
  2. 数据元素容量固定不变的场景(如操作系统的文件分配表FAT)

顺序表与链表的对比

  1. 逻辑结构:都属于线性表,是线性结构
  2. 存储结构:顺序表–》顺序存储 :支持随机存取,存储密度高,大片连续内存分配不方便,改变容量不方便
    链表----》链式存储:离散的小空间分配方便,改变容量方便,不可随机存取,存储密度低
    3.基本操作:初始化(创),销(毁),增删改查

表长难以估计,经常增加/删除元素 ----链表
表长可估计,查询(搜索)操作较多 ----顺序表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值