数据结构:超详细的详解链表(下)——双向链表

前面中我们讲解了介绍了链表这种数据结构和单链表的基本有操作。数据结构:超详细的详解链表(上)——单链表_阿威昂的博客-CSDN博客

现在我们将延续链表,我们将介绍一种新的链表类型——带头双向循环链表

这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。他的规律性更强,更容易实现。各种操作都是对应相通的。会一种其余的也都会了。

我们现在就来逐步分析这种链表。

构建链表的结构体成员变量。

typedef int LTdatatype;
typedef struct Dlistnode
{
    LTdatatype data;//数据域
	struct Dlistnode* prev;//节点的上一个节点
	struct Dlistnode* next;//节点的下一个节点

}dlnode;

//初始化

dlnode* DLInit();

//创建节点

dlnode* buydlnode(LTdatatype x);

//尾插节点
void dlistpushback(dlnode* head, LTdatatype x);

//尾插节点

void dlistpopback(dlnode* head);

//头插节点

void dlistpushfront(dlnode* head, LTdatatype x);
//头删节点

void dlistpopfront(dlnode* head);
//pos位置插入节点

void dlistinsert(dlnode* pos, LTdatatype x);
//删除就是删除中间的节点
void dlisterase(dlnode* pos, LTdatatype x);

1.初始化链表

//初始化
dlnode* DLInit()
{
	dlnode* head = buydlnode(0);
	head->next=head;//头节点下一节点是头节点
	head->prev = head;//头节点上一个节点也是头节点
	return head;
}

 2.创建节点

dlnode* buydlnode(LTdatatype x)
{
	dlnode* node = (dlnode*)malloc(sizeof(dlnode));//申请一个空间
	if (node == NULL)
	{
		perror("malloc filed");
		exit(-1);
	}
	node->data = x;//新节点的数据域是x
//创建单独一个节点,后面可以用头插尾插来连接
	node->next = NULL;
	node->prev = NULL;
	return node;
}

 3.头插节点

void dlistpushfront(dlnode* head, LTdatatype x)
{
	assert(head);
	dlnode* headnext = head->next;创建一个headnext表示头节点的下一个节点
	dlnode* node = buydlnode(x);
	head->next = node;
	//先改左边
	headnext->prev = node;
	node->prev = head;
	node->next = headnext;
	
}

图解:

 注意:链表中连接顺序就是先连接左边再连接右边。不然会连接失败,头插失败。

 4.尾插节点

void dlistpushback(dlnode* head, LTdatatype x)
{
	assert(head);
	dlnode* newnode = buydlnode(x);
	dlnode* tail = head->prev;//直接用头节点的上一个节点来表示最后链表中最后一个节点
	//循环链表就可以不用遍历链表来得到最后一个节点
    dlnode* cur;
	/*while (cur)
	{
		cur = cur->next;双向链表不适用
	}*/
   // 同样的连接顺序,先连接左边
	tail->next = newnode;
	newnode->prev = tail;
	head->prev = newnode;
	newnode->next = head;
}

 注意:循环链表可以不用遍历链表来得到最后一个节点,就直接使用head->prev得到最后一个节点

5.头删节点

void dlistpopfront(dlnode* head)
{
	assert(head);
	assert(head->next != head);
	dlnode* first = head->next;
	dlnode*second = first->next;
//删除操作
	head->next = second;
	second->prev = head;
	free(first);
}

 6.尾删节点

void dlistpopback(dlnode* head)
{
	assert(head);
	assert(head->next != head);
	dlnode* tail = head->prev;
	dlnode* tailprev = tail->prev;
	tailprev->next = head;
	head->prev = tailprev;
	free(tail);
}

7.中间插入节点

void dlistinsert(dlnode* pos, LTdatatype x)
{
	assert(pos);
	dlnode* newnode = buydlnode(x);
	dlnode* posprev = pos->prev;
    //插入操作
	posprev->next = newnode;
	newnode->prev = posprev;
	pos->prev = newnode;
	newnode->next = pos;
    //可以来表示头插(dlistinsert(head->next)),尾插(pos之前插入)(dlistinsert(head))
	//顺序
}

 我们发现这个插入节点我们可以用来头插尾插操作,表示头插(dlistinsert(head->next))尾插(pos之前插入)(dlistinsert(head))

8.删除节点

void dlisterase(dlnode* pos, LTdatatype x)
{
	assert(pos);
	dlnode* posprev = pos->prev;
	dlnode* posnext = pos->next;
	posprev->next = posnext;
	posnext->prev = posprev;
	free(pos);
	//同样的也可表示头删(dlisterase(head->next)),尾删(dlistinsert(head->prev))
}

同样的我们可以用来实现头删尾删操作 

通过插入节点或删除节点我们可以快速的进行构建链表,同时我们也可以利用这个进行不同的操作,头插尾插,头删尾删操作

尾结:

链表这种数据结构其实也不难,关键在于画图,发现其中的规律。也欢迎大佬们再评论区里讨论。

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值