带头双向循环链表的增删查改

目录

基本结构

增删查改

测试用例


        带头双向循环链表是一种相对复杂的链表,但在存储数据方面也有一定的优势。本文就简单地介绍下如何用c语言实现——增删查改这基本功能。

基本结构

 

         如图就是基本结构,可以看出每个节点都会存储一个数据两个指针,其中head节点为“哨兵”节点不存储数据。我们把前后两个指针分别命名为prev和next,可以看出头结点的prev指向尾结点,尾结点的next指向头结点,这样形成环结构。

        认识了基本结构后,我们就开始实现相关功能。

增删查改

        首先创建结构体变量:

typedef struct doublelist
{
    struct doublelist* prev;
    int val;
     struct doublelist* next;
}dlist;

        如果命名太长可进行一定的缩写,如上图缩写为dlist。由上方的基本结构可知我们需要一个“哨兵位”,当哨兵位的指针均指向自己时,则为空链表,我们先要进行初始化:

dlist* dlistinit()
{
	dlist* guard = (dlist*)malloc(sizeof(dlist));
	if (guard == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	guard->next = guard;
	guard->prev = guard;

	return guard;

}

        接着我们开始实现增这个功能,可分为尾增、头增、任意位置增。我们先从尾增开始。由基本结构我们可以看出头结点的prev就是指向的尾结点,而我们要做的就是创建一个新结点使它成为新的尾结点。我们先实现创建新结点的函数:

dlist* makenewnode(x)
{
	dlist* newnode = (dlist*)malloc(sizeof(dlist));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->val = x;
	newnode->next = NULL;
	newnode->prev = NULL;

	return newnode;
}

然后根据逻辑关系:

void dlistbackpush(dlist* head, int x)
{
	assert(head);//判断head是否为空指针
	dlist* newnode = makenewnode(x);
	dlist* tail = head->prev;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = head;
	head->prev = newnode;
}

        完成尾增后另外两个的实现也就相对简单:

void dlistfrontpush(dlist* head, int x)
{
	assert(head);
	dlist* newnode = makenewnode(x);
	dlist* next = head->next;
	next->prev = newnode;
	newnode->next = next;
	newnode->prev = head;
	head->next = newnode;
}

        在进行任意位置的增删时,我们要实现一个查找位置的函数:

dlist* dlistfind(dlist* head, int x)
{
	assert(head);
	dlist* cur = head->next;
	while (cur != head)
	{
		if (cur->val == x)
			return cur;
		cur = cur->next;
	}

	return	NULL;
}

这样就可以找出相应位置进行一些列操作了。

void dlistinsert(dlist* pos, int x)
{
	assert(pos);
	dlist* newnode = makenewnode(x);
	dlist* prev = pos->prev;
	newnode->next = pos;
	newnode->prev = prev;
	pos->prev = newnode;
	prev->next = newnode;
}

        接下来就是删除方面,逻辑也很简单,就是将指针的方向重置,并且释放空间。要注意的一点是要确保不为空链表,所以我们就需要一个函数:

bool ifdlistempty(dlist* head)
{
	return (head->next == head);//以bool类型来进行逻辑判断
}

随后就开始:尾删、头删、任意位置删:

void dlistbackpop(dlist* head)
{
	assert(head);
	assert(!(ifdlistempty(head)));
	dlist* tail = head->prev;
	dlist* prev = tail->prev;
	prev->next = head;
	head->prev = prev;
	free(tail);
	tail = NULL;


}

void dlistfrontpop(dlist* head)
{
	assert(head);
	assert(!ifdlistempty(head));
	dlist* cur = head->next;
	dlist* newcur = cur->next;
	head->next = newcur;
	newcur->prev = head;
	free(cur);
	cur = NULL;


}

void dlisterase(dlist* pos)
{
	assert(pos);

	dlist* next = pos->next;
	dlist* prev = pos->prev;
	prev->next = next;
	next->prev = prev;
	free(pos);
	pos = NULL;
}

在实现完成后我们可以打印,需要一个打印函数:

void dlistprint(dlist* head)
{
	assert(head);
	printf("Guard<-->");
	dlist* cur = head->next;
	while (cur != head)
	{
		printf("%d<-->", cur->val);
		cur = cur->next;
	}
	printf("\n");
}

最后也别忘了销毁链表:

void dlistdertory(dlist* head)
{
	assert(head);
	dlist* cur = head->next;
	while (cur != head)
	{
		dlist* next = cur->next;
		free(cur);
		cur = next;
	}

	free(head);
	head = NULL;

}

测试用例

void test()
{
	dlist* head = dlistinit();
	dlistbackpush(head, 1);
	dlistbackpush(head, 2);
	dlistbackpush(head, 3);
	dlistbackpush(head, 7);
	dlistbackpush(head, 8);

	dlistprint(head);
	dlistfrontpush(head, 4);
	dlistfrontpush(head, 5);
	dlistprint(head);
	dlistbackpop(head);
	dlistbackpop(head);
	dlistprint(head);

	dlistfrontpop(head);
	dlistfrontpop(head);
	dlistprint(head);

	dlist* pos = dlistfind(head, 2);
	dlistinsert(pos, 5);
	dlistinsert(pos, 6);
	dlistprint(head);
	dlisterase(pos);
	dlistprint(head);

	dlistdertory(head);
}

int main()
{
	test();

	return 0;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值