《双向链表》

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

``

链表有着不同的种类,分别有《带头or不带头》,《单向or双向》,《循环or不循环》222结合起来就有8种,而相对与双向链表则是《带头》《双向》《循环》的链表


``

一、双向链表是什么?

在这里插入图片描述
带头:指的是链表中有哨兵位节点,该哨兵位节点即头节点(而在单链表中,口头提到的头节点实际上指的是第一个有效节点,这不是正确的称呼)
在这里插入图片描述
双向:
在这里插入图片描述

带头双向
有头节点(哨兵位)可以从俩个方向遍历

3:循环或不循环
在这里插入图片描述

二、代码实现

1.双链表结构

代码如下(示例):

typedef struct S
{
    int data;//数据
   struct S* next;//指向后面的地址
   struct S* prve;//指向前面的地址
}S1;

2.代码功能实现

代码如下(示例):初始化

void chushihua(S1** ps);//初始化

void chushihua(S1** ps)
{
	//给双向链表创建一个哨兵位
	*ps = Buynode(-1);//即使有值我们也不会修改
}

尾插


void SLpushback(S1* ps,cwz x);//尾插
S1* Buynode(cwz x)
{
	//申请节点
	S1* node = (S1*)malloc(sizeof(S1));
	if (node == NULL)
	{
		perror("malloc");
		exit(1);
	}
	node->data = x;
	node->prve = node->next = node;
	return node;
}
void SLpushback(S1* ps, cwz x)
{
	assert(ps);
	S1* newnode = Buynode(x);
	S1* next = ps;
	newnode->prve = ps->prve;
	ps->prve->next = newnode;
	newnode->next = ps;
	ps->prve = newnode;
	
}

每做完一个函数就要测试
在这里插入图片描述

头插

void SLpushfront(S1* ps, cwz x);
S1* Buynode(cwz x)
{
	//申请节点
	S1* node = (S1*)malloc(sizeof(S1));
	if (node == NULL)
	{
		perror("malloc");
		exit(1);
	}
	node->data = x;
	node->prve = node->next = node;
	return node;
}

void SLpushfront(S1* ps, cwz x)//头插
{
	assert(ps);
	S1* p = ps;
	S1* newnode = Buynode(x);
	newnode->prve = ps;
	newnode->next = ps->next;
	ps->next->prve = newnode;
	ps->next = newnode;
}

测试
在这里插入图片描述
其他功能我就不一一介绍了

//打印
void SLprint(S1* ps)
{
	assert(ps);
	S1* p = ps->next;

	while (p != ps)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	//走到哨兵位
	printf("\n");
}




void LTPopnback(S1* ps)//尾删
{
	//链表必须有效且链表不能为空
	assert(ps && ps->next != ps);
	S1* p = ps->prve;
	ps->prve->prve->next = ps;
	ps->prve = ps->prve->prve;
	free(ps->prve);
	ps->prve = NULL;
}





void LTPopnfront(S1* ps)//头删
{
	assert(ps && ps->next != ps);
	S1* p = ps->next;
	ps->next = p->next;
	p->next->prve = ps;
	free(p);
	p = NULL;
}



S1* find1(S1* ps, cwz x)//查找元素
{
	S1* p = ps->next;
	while (p != ps)
	{
		if (p->data == x)
		{
			return p;
		}
		p = p->next;
	}
	//没有找到
	return NULL;
}



void insertback(S1* pos, cwz x)//指定位置插入元素
{
	S1* newnode = Buynode(x);
	newnode->prve = pos;
	newnode->next = pos->next;
	pos->next->prve = newnode;
	pos->next = newnode;

}


void deletepos(S1* pos)//指定位置删除元素
{
	//pos理论上来说是不能为哨兵位的,但是没有参数ps,无法增加校验
	assert(pos);
	S1* p = pos;
	p->prve->next = p->next;
	p->next->prve = p->prve;
	free(p);
	p = NULL;
}

void ruin(S1* ps)//函数销毁(这里传的是一级指针并不会影响实参所以要手动置空)
{
	assert(ps);
	S1* pcur = ps->next;
	while (pcur->next != ps)
	{
	 S1* p = pcur->next;
	 free(pcur);
	}
	//哨兵位
	free(ps);
	ps = NULL;
}
//头文件
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
//双向链表
//节点->数据+指向下一个节点的指针+指向前一个节点的指针

typedef int cwz;//这里是可以不int类型转换为其他类

typedef struct S
{
    int data;
    struct S* next;
    struct S* prve;
}S1;



//声明函数
/*void chushihua(S1** ps);*///初始化


//情况二
S1* chushihua();

void SLpushback(S1* ps, cwz x);//尾插

void SLprint(S1* ps);//打印

void SLpushfront(S1* ps, cwz x);//头插

void LTPopnback(S1* ps);//尾删

void LTPopnfront(S1* ps);//头删

S1* find1(S1* ps, cwz x);//查找元素


void insertback(S1* pos, cwz x);//在指定位置插入数据 


void deletepos(S1* pos);//指定位置删除

void ruin(S1* ps);



//传一级指针是为了保存接口一致性


//测试文件
#include"标头.h"
void ceshi()
{
	//S1* ps = NULL;
	S1* ps = chushihua();
	//printf("\n");
	//SLpushback(ps, 1);//尾插1
	//SLprint(ps);
	//SLpushback(ps, 2);//尾插2
	//SLprint(ps);
	//SLpushback(ps, 3);//尾插3
	//SLprint(ps);
	//SLpushback(ps, 4);//尾插4
	//SLprint(ps);
	//SLpushback(ps, 5);//尾插5
	//SLprint(ps);

	SLpushfront(ps, 1);
	SLprint(ps);
	SLpushfront(ps, 2);
	SLprint(ps);
	SLpushfront(ps, 3);
	SLprint(ps);
	SLpushfront(ps, 4);
	SLprint(ps);
	SLpushfront(ps, 5);
	SLprint(ps);
	//printf("\n");
	//S1* find = find1(ps, 3);//查找函数
	//if (find == NULL)
	//{
	//	printf("找不到");
	//}
	//else
	//{
	//	printf("找到了");
	//}
	//insertback(find, 22);//插入函数
	//printf("\n");
	//SLprint(ps);
	//deletepos(find);
	//SLprint(ps);

	//ruin(ps);
	手动传为空是因为保证接口一致性采用以一级才传的
	传一级的问题是形参被修改位NULL,但是实参也没有置空,所以要手动置空
	//ps = NULL;




	//LTPopnback(ps);//尾删
	//SLprint(ps);
	//LTPopnback(ps);
	//SLprint(ps);
	//LTPopnback(ps);
	//SLprint(ps);
	//LTPopnfront(ps);//头删
	//SLprint(ps);
	//LTPopnfront(ps);
	//SLprint(ps);
	//LTPopnfront(ps);
	//SLprint(ps);
	//LTPopnfront(ps);
	//SLprint(ps);


}
int main()
{
	ceshi();
	return 0;
}

#/ 总结
双向链表是比单向链表方便
例如:1:双向链表有着前后指针指向自己前后位置使得操作者更具有灵活性
2:双向链表具有哨兵位可以保证了指针不为空的情况
3:双向链表的循环性大大提高了我们的做题效率和思路

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值