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

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

链表分类

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

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;
}

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

相关推荐
学java的小菜鸟啊14 分钟前
第五章 网络编程 TCP/UDP/Socket
java·开发语言·网络·数据结构·网络协议·tcp/ip·udp
LaoWaiHang36 分钟前
C语言从头学61——学习头文件signal.h
c语言
菜鸟求带飞_1 小时前
算法打卡:第十一章 图论part01
java·数据结构·算法
是小Y啦1 小时前
leetcode 106.从中序与后续遍历序列构造二叉树
数据结构·算法·leetcode
一名路过的小码农1 小时前
C/C++动态库函数导出 windows
c语言·开发语言·c++
m0_631270401 小时前
标准c语言(一)
c语言·开发语言·算法
万河归海4281 小时前
C语言——二分法搜索数组中特定元素并返回下标
c语言·开发语言·数据结构·经验分享·笔记·算法·visualstudio
小周的C语言学习笔记1 小时前
鹏哥C语言36-37---循环/分支语句练习(折半查找算法)
c语言·算法·visual studio
凌肖战2 小时前
力扣上刷题之C语言实现(数组)
c语言·算法·leetcode
秋夫人3 小时前
B+树(B+TREE)索引
数据结构·算法