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

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

链表分类

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

3. 循环与非循环

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

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

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

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

创建返回链表的头结点

//创建并返回链表表头

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

这是链表的头节点

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

尾插数据

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

}

我们在写个打印函数

链表打印

//链表打印

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

测试结果

头删数据

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

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

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

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

//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的前面插入数据

 //在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位置的数据

//删除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;
 }

测试一下

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

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

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

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

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值