C语言双向链表(完整工程。创建,增删查)

之前写了一篇基于C语言的单向链表,顺便也把双向链表写了。双向链表跟单向链表的区别就是多了一个向前的指针域。老实说,到目前为止我还没有真正体会到双向链表带给我的好处和优势。。。。
之前写的单链表链接:https://blog.csdn.net/tiramisu_L/article/details/107549773

双向链表的结构如下,两个指针域

typedef struct _list
{
	struct _list *prev;
	struct _list *next;
	int data;
}list;

根据《数据结构与算法分析》这本的介绍,双向链表是没有头节点的,关于头节点的个人理解在我单链表的文章上有说到,可以去看一下。然后有一个需要注意的点,第一个节点的prev指针要指向NULL,最后一个节点的next要指向NULL

双链表的一些操作跟单链表没有太多差别,这里具体实现一下

1、创建节点

list *Create_List_Node(int data)
{
	list *pNode = (list *)malloc(sizeof(list));
	pNode->data = data;
	pNode->prev = NULL;
	pNode->next = NULL;
	
	return pNode;
}

2、插入节点,插入节点分头插和尾插

1)、尾插法。尾插法很简单,只要遍历到末尾,把末尾的指针的next指向新的节点,然后把新的节点的prev指向末尾节点即可

list *Add_List_Node_Tail(list *pHead, list *pNode)
{
	if (pHead == NULL || pNode == NULL)
	{
		printf("pHead is null or pNode is null\n");
		return NULL;
	}
	
	while (pHead->next != NULL)
	{
		pHead = pHead->next;
	}
	
	pHead->next = pNode;    //末尾节点的next指向新节点
	pNode->prev = pHead;    //新节点的prev直线末尾节点
	
	return pHead;
}

2)、头插法。这里的头插法是指在第一个节点后面插入新的节点。头插法一般是需要4个步骤来完成节点插入,步骤是有一定顺序的,如果顺序出错是没办法正常插入的。下面来具体说一下要怎么确定这个顺序

 首先需要注意的是,上面的第一个节点是pHead这个只是习惯命名,你完全可以用其他名字来代替。上面的顺序只是其中一种下面还会介绍另外一种

首先是要先把Node1的prev指针域指向新节点pNode,即Node1->prev = pNode。但是我们是不知道Node1的,我们只能通过pHead来知道Node1,也就是pHead->next = Node1,那么就有pHead->next->prev = pNode;

第二,我们需要用pNode的next来指向Node1,也就是pNode->next = Node1;同理我们是不知道Node1的,但是我们知道pHead->next = Node1;所以我们就会有pNode->next = pHead->next;

第三,直接就是pHead->next = pNode;

第四,直接就是pNode->prev = pHead;

 

这里再介绍另外一种方式

 第一, 直接pNode->prev = pHead;

第二,pNode的next指向Node1,即pNode->next = Node1,但是我们不知道Node1,可是我们有pHead->next = Node1,所以我们就有pNode->next = pHead->next;

第三,直接有pHead->next = pNode;

第四,Node1的prev要指向pNode,即Node1->prev = pNode;但是我们 不知道Node1,可是我们有pHead->next = Node1;所以我们有pHead->next->prev = pNode;

这个看似很复杂,我们要如何记住他,不需要每次都画一个链表图呢。这里我自己总结了一个方法,虽然不确定是否真的正确,但是就目前来看暂时没有什么大问题。对比下面正确和错误的方法我自己的总结是,pHead->next如果做了左值,那么pHead->next就不能在后面作为右值了


//方法一
pHead->next->prev = pNode;
pNode->next       = pHead->next;
pHead->next       = pNode;
pNode->prev       = pHead;


//方法二
pNode->prev       = pHead;
pNode->next       = pHead->next;
pHead->next       = pNode;
pHead->next->prev = pNode;


//错误的方法,此方法会进入死循环
pHead->next       = pNode;          //pHead->next作为左值,后面不能再做右值了
pNode->next       = pHead->next;    //这里pHead->next做右值,出错了
pHead->next->prev = pNode;
pNode->prev       = pHead;


//错误的方法二,此方法会进入死循环
pHead->next       = pNode;        //pHead->next作为左值,后面不能再做右值了
pNode->prev       = pHead;
pHead->next->prev = pNode;
pNode->next       = pHead->next;   //这里pHead->next做右值,出错了

下面给出具体的实现过程,我把错误的方法也一起放上去可以测试一下。

list *Add_List_Node_Head(list *pHead, list *pNode)
{
	if (pHead == NULL || pNode == NULL)
	{
		printf("pHead is null or pNode is null\n");
		return NULL;
	}
	
	pHead->next->prev = pNode;
	pNode->next       = pHead->next;
	pHead->next       = pNode;
	pNode->prev       = pHead;
	
	/*
	//这个也可以正常使用
	pNode->prev       = pHead;
	pNode->next       = pHead->next;
	pHead->next       = pNode;
	pHead->next->prev = pNode;
	
	//这个也是可以正常使用,后面两句的顺序跟前面的不一样
	pNode->prev       = pHead;
	pNode->next       = pHead->next;
	pHead->next->prev = pNode;
	pHead->next       = pNode;
	*/
	
	/*
	//这个会进入死循环
	pHead->next       = pNode;
	pNode->next       = pHead->next;
	pHead->next->prev = pNode;
	pNode->prev       = pHead;
	
	//这个会进入死循环
	pHead->next       = pNode;
	pNode->prev       = pHead;
	pHead->next->prev = pNode;
	pNode->next       = pHead->next;
	*/
	
	
	return pHead;
}

3、查找链表

查找链表还是需要考虑边界条件

list *Search_List_Node(list *pHead,int data)
{
	
	if (pHead == NULL)
	{
		printf("pHead is null\n");
		return NULL;
	}
	
	while (pHead->next != NULL)
	{
		if (pHead->data == data)
		{
			printf("find node\n");
			return pHead;
		}
		
		pHead = pHead->next;
	}
	
	if (pHead->data == data)			//最后一个节点
	{
		printf("last node\n");
		return pHead;
	}
	else
	{	
		printf("llllll\n");
		return NULL;
	}
	
	return pHead;
}

4、删除节点

void Delete_List_Node(list *pHead, list *pNode)
{
	if (pHead == NULL || pNode == NULL)
	{
		printf("kkkkkkk\n");
		printf("pHead is null or pNode is null\n");
		return;
	}
	
	while (pHead->next != NULL)
	{
		if (pHead->data == pNode->data)		//双向链表头结点是有数据的,要检测头结点
		{
                                 //这里pHead就是要删除的节点
			printf("delet node\n");
			
			pNode->next->prev = pNode->prev;
			pNode->prev->next = pNode->next;
		}
		
		pHead = pHead->next;
	}
	
	if (pHead->data == pNode->data)			//如果删掉最后一个节点
	{
		printf("Delete last node\n");
		//pNode->next->prev = pNode->prev;	//如果删掉最后一个节点不需要前驱了
		pNode->prev->next = NULL;
	}
	
	
	return;
}

 剩下的就不在 放在上了,main函数的写法跟之前单链表一样,可以参考自己实现一下。

这边博客可能还是会有问题,如果大家发现什么问题,提出来大家一起学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值