数据结构——链表(双向链表)

大家好,我是小峰,今天给大家讲解的是双向链表,我们先来看看链表的结构。

链表分类

这里带头是哨兵位的头节点

3. 循环与非循环

我们排列一些就可以看出链表的结构种类

这些链表的操作大都差不多

我们今天讲解的是双向带头循环链表


带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都
是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带
来很多优势,实现反而简单了,后面我们代码实现了就知道了。
首先是链表基础结构

创建返回链表的头结点

cpp 复制代码
//创建并返回链表表头

listnode* listcj() {
	listnode* ps = (listnode*)malloc(sizeof(CMMlet) + 2*sizeof(listnode*));
	ps->next = ps;
	ps->prev = ps;
	return ps;
}

这是链表的头节点

那么我们接下来实现一个尾插

尾插数据

cpp 复制代码
void listwc(listnode* pead) {
	listnode* cur=listcj();
	listnode* ps = pead->next;
	//连接
	ps->next = cur;
	cur->prev = ps;
	cur->next = pead;
	pead->prev = cur;

}

我们在写个打印函数

链表打印

cpp 复制代码
//链表打印

void listdy(listnode* pead) {
	listnode* cur = pead->next;
	printf("哨兵位");
	while (cur != pead) {
		printf("<=>%d", cur->SZ);
		cur = cur->next;
	}
	printf("\n");
}

那接下来我们就可以测试一下我们写的尾插数据

老规矩我们分装一个函数来测试

头插数据

cpp 复制代码
//头插数据
void listtc(listnode* pead, CMMlet n) {
	listnode* ps = listcj();
	listnode* cur = pead->next;
	pead->next = ps;
	ps->prev = pead;
	cur->prev = ps;
	ps->next = cur;
	ps->SZ = n;
}

测试结果

头删数据

cpp 复制代码
//头删数据
void listts(listnode* pead) {
	listnode* pt = pead->next;
	listnode* ps = pead->next->next;
	free(pt);
	ps->prev = pead;
	pead->next = ps;
}

这个代码是有点小问题的大家可以想想如果参数只有头节点呢?

最后我们是不是会出现空指针问题?

这时我们就要用到断言了,但我们发现断言的条件也不好写,这时我们的bool类型就要大展身手了

cpp 复制代码
//bool判断
bool listpd(listnode* pead) {
	if (pead->next == pead && pead->prev == pead) {
		return false;
	}
	else {
		return true;
	}
}

修改后的代码如下

cpp 复制代码
//头删数据
void listts(listnode* pead) {
	assert(pead);
	assert(listpd(pead));
	listnode* pt = pead->next;
	listnode* ps = pead->next->next;
	free(pt);
	ps->prev = pead;
	pead->next = ps;
}

我们测试一下

当我们再删除时

它就会报错,并提示我们在哪一行。

尾删数据

cpp 复制代码
//尾删数据
void listws(listnode* pead) {
	assert(pead);
	assert(listpd(pead));
	listnode* ps = pead->prev;
	listnode* pt = ps->prev;
	free(ps);
	pt->next = pead;
	pead->prev = pt;
}

查找数据

与单链表类似我们来试试

cpp 复制代码
//查找数据
 listnode* listcz(listnode* pead, CMMlet n) {
	 assert(pead);
	listnode* ps = pead->next;
	while (ps != pead) {
		if (ps->SZ == n) {
			return ps;
		}
		else {
			ps = ps->next;
		}
	}
	return NULL;
}

我们测试一下

在pos的前面插入数据

cpp 复制代码
 //在pos前面插入数据
 void listposcr(listnode* pos, CMMlet n) {
	 listnode* ps = pos->prev;
	 listnode*pt=listcj();
	 pt->next = pos;
	 pos->prev = pt;
	 pt->prev = ps;
	 ps->next = pt;
	 pt->SZ = n;
 }

测试一下

删除pos位置的数据

cpp 复制代码
//删除pos位置的数据
 void listpossc(listnode* pos) {
	 assert(pos);
	 assert(listpd(pos));
	 listnode* prev = pos->prev;
	 listnode* next = pos->next;
	 free(pos);
	 prev->next = next;
	 next->prev = prev;
 }

测试一下

销毁链表

cpp 复制代码
//销毁链表
 void listxh(listnode* pead) {
	 listnode* cur = pead->next;
	 listnode* ps = cur->next;
	 while (cur != pead) {
		 free(cur);
		 cur = ps;
		 ps = ps->next;
	 }
	 pead->next = pead;
	 pead->prev = pead;
 }

测试一下

这就是双向带头循环链表的所有操作了

下面就是本节的所有代码了

cpp 复制代码
# include<stdio.h>
# include<stdlib.h>
# include<stdbool.h>
# include<assert.h>


typedef int CMMlet;
typedef struct listnode  listnode;

struct listnode {
	CMMlet SZ;
	//上一个节点
	listnode* prev;
	//下一个节点
	listnode* next;
};

//创建并返回链表表头

listnode* listcj() {
	listnode* ps = (listnode*)malloc(sizeof(CMMlet) + 2*sizeof(listnode*));
	ps->next = ps;
	ps->prev = ps;
	return ps;
}

//尾插数据

void listwc(listnode* pead,CMMlet n) {
	listnode* cur=listcj();
	listnode* ps = pead->prev;
	//连接
	ps->next = cur;
	cur->prev = ps;
	cur->next = pead;
	pead->prev = cur;
	cur->SZ = n;

}

//链表打印

void listdy(listnode* pead) {
	listnode* cur = pead->next;
	printf("哨兵位");
	while (cur != pead) {
		printf("<=>%d", cur->SZ);
		cur = cur->next;
	}
	printf("\n");
}


//头插数据
void listtc(listnode* pead, CMMlet n) {
	listnode* ps = listcj();
	listnode* cur = pead->next;
	pead->next = ps;
	ps->prev = pead;
	cur->prev = ps;
	ps->next = cur;
	ps->SZ = n;
}

//bool判断
bool listpd(listnode* pead) {
	if (pead->next == pead && pead->prev == pead) {
		return false;
	}
	else {
		return true;
	}
}

//头删数据
void listts(listnode* pead) {
	assert(pead);
	assert(listpd(pead));
	listnode* pt = pead->next;
	listnode* ps = pead->next->next;
	free(pt);
	ps->prev = pead;
	pead->next = ps;
}


//尾删数据
void listws(listnode* pead) {
	assert(pead);
	assert(listpd(pead));
	listnode* ps = pead->prev;
	listnode* pt = ps->prev;
	free(ps);
	pt->next = pead;
	pead->prev = pt;
}
//查找数据
 listnode* listcz(listnode* pead, CMMlet n) {
	 assert(pead);
	listnode* ps = pead->next;
	while (ps != pead) {
		if (ps->SZ == n) {
			return ps;
		}
		else {
			ps = ps->next;
		}
	}
	return NULL;
}

 //在pos前面插入数据
 void listposcr(listnode* pos, CMMlet n) {
	 listnode* ps = pos->prev;
	 listnode*pt=listcj();
	 pt->next = pos;
	 pos->prev = pt;
	 pt->prev = ps;
	 ps->next = pt;
	 pt->SZ = n;
 }
 //删除pos位置的数据
 void listpossc(listnode* pos) {
	 assert(pos);
	 assert(listpd(pos));
	 listnode* prev = pos->prev;
	 listnode* next = pos->next;
	 free(pos);
	 prev->next = next;
	 next->prev = prev;
 }

 //销毁链表
 void listxh(listnode* pead) {
	 listnode* cur = pead->next;
	 listnode* ps = cur->next;
	 while (cur != pead) {
		 free(cur);
		 cur = ps;
		 ps = ps->next;
	 }
	 pead->next = pead;
	 pead->prev = pead;
 }



 void listcheshi() {
	 listnode* pead = listcj();
	 listtc(pead, 1);
	 listtc(pead, 2);
	 listtc(pead, 3);
	 listtc(pead, 4);
	 listtc(pead, 5);
	 listdy(pead);
	 listxh(pead);
	 listdy(pead);

 }

int main() {
	//测试函数
	listcheshi();

	return 0;
}

以上就是全部内容了,如果有错误或者不足的地方欢迎大家给予建议。

相关推荐
stm 学习ing31 分钟前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
茶猫_5 小时前
力扣面试题 - 25 二进制数转字符串
c语言·算法·leetcode·职场和发展
ö Constancy6 小时前
Linux 使用gdb调试core文件
linux·c语言·vim
lb36363636366 小时前
介绍一下strncmp(c基础)
c语言·知识点
wellnw6 小时前
[linux] linux c实现共享内存读写操作
linux·c语言
Hera_Yc.H6 小时前
数据结构之一:复杂度
数据结构
肥猪猪爸7 小时前
使用卡尔曼滤波器估计pybullet中的机器人位置
数据结构·人工智能·python·算法·机器人·卡尔曼滤波·pybullet
linux_carlos7 小时前
环形缓冲区
数据结构
readmancynn7 小时前
二分基本实现
数据结构·算法
Bucai_不才7 小时前
【数据结构】树——链式存储二叉树的基础
数据结构·二叉树