带头双向成环链表功能复现

前言:

  • 复现带头双向成环链表功能中将环结点设置为哨兵结点。

  • 虽然带头双向成环链表结构上很复杂,但是实现起来也不是太难。

  • 因为有哨兵结点原因,一方面只需要在初始化的时候进行二级传参,另外一方面是不需要考虑空链表,因此关于增删减查只需要一级指针。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MByXcvtZ-1636642417217)(演示文稿1.png)]

结构体定义

typedef int  DLDateType;

typedef
struct DList
{
    
    
	struct DList* prev;
	  DLDateType date;
	struct DList* next;
}DList;

初始化

void DListInit(DList** pphead)//初始化
//初始化带头双向链表,需改变实参指向,因此二级指针。
//另外因为因为此时链表为空,要注意哨兵结点中的next和prev。
{
    
    
	assert(pphead);
	DList* node = newnode(0);//哨兵结点存储的元素不需要在意,它只是辅助作用。
	                       //空链表,二级指针
	node->next = node;
	node->prev = node;
	*pphead = node;
}

打印

void DListPrint(DList* phead)
{
    
    
	assert(phead);
	DList* cur = phead->next;
	while (cur != phead)//当cur等于哨兵结点时,代码遍历完毕
	{
    
    
		printf("%d->", cur->date);
		cur = cur->next;
	}
	printf("NULL\n");
}

指定pos位置插入

当头插和尾插去复用DListInsert()函数时,是不一样的,需要特殊对待。

尾插其实就是在哨兵结点前插入结点。

头插是在哨兵结点后插,也就是哨兵结点的next结点前,需要特别注意

void  DListInsert(DList* pHead,  DList* pos,  DLDateType x)// 双向链表在pos的前面进行插入
{
    
    
	//本函数是考虑头插,尾插复用的情况,编写的。
	assert(pHead);
	assert(pos!=NULL);
	//不考虑
	//
	/*DList* cur = pHead->next;
	while (cur != pHead)
	{
		if (cur == pos)
		{
			DList* node = newnode(x);
			DList* prevcur = cur->prev;
			prevcur->next = node;
			node->prev = prevcur;
			node->next = cur;
			cur->prev = node;
			return;
		}
		cur = cur->next;
	}
	printf("链表中找不到pos\n");*/

	//考虑的代码
	if (pos != pHead) 
	{
    
    

		DList* cur = pHead->next;
		while (cur != pHead)
		{
    
    
			if (cur == pos)
			{
    
    
				DList* node = newnode(x);
				DList* prevcur = cur->prev;
				prevcur->next = node;
				node->prev = prevcur;
				node->next = cur;
				cur->prev = node;
				return;
			}
			cur = cur->next;
		}
		printf("链表中找不到pos\n");
	}
	else
	{
    
    
		
		if (pHead->next == pHead)//链表此时为空。
		{
    
    
			DList* node = newnode(x);
			node->prev = pHead->prev;
			node->next = pHead;
			pHead->prev = node;
			pHead->next = node;
		}
		else//尾插在复用时很特殊,因此特别对待。
		{
    
    
			DList* node = newnode(x);
			node->next = pHead;
			node->prev = pHead->prev;
			pHead->prev->next = node;
			pHead->prev = node;
		}

	}
}

头插

void DListPushBack(DList* phead, DLDateType x)//尾插

{
    
    
	assert(phead);
	DListInsert(phead, phead,x);//复用DlistInsert()
	
    //不复用
	/*DList* node = newnode(x);
	DList* tail = phead->prev;
	tail->next = node;
	node->prev = tail;
	node->next = phead;
	phead->prev = node;*/

}

尾插

void DListPushFront(DList* phead,  DLDateType x)//复用DlistInsert不方便
{
    
    
	assert(phead);
	DListInsert(phead, phead->next, x);//复用
	//不复用
	/*DList* node = newnode(x);
	node->next = phead->next;
	node->prev = phead;
	phead->next->prev = node;
	phead->next = node;*/
}

指定pos位置删

删除就不需要考虑头删,尾删的特殊情况

void DListErase(DList* pHead,DList* pos)// 双向链表删除pos位置的节点
{
    
    
	assert(pHead);
	assert(pHead->next != pHead);
	assert(pos);
	DList* cur = pHead->next;
	while (cur!=pHead)
	{
    
    
		if (cur == pos)
		{
    
    
			DList* prevcur = cur->prev;
			DList* bankcur = cur->next;
			prevcur->next = bankcur;
			bankcur->prev = prevcur;
			free(cur);
			cur = NULL;
			printf("删除成功\n");
			return;
		}
		cur = cur->next;
	}

	printf("链表中找不到pos\n");

}

头删

void DListPopBack(DList* phead)//尾删
{
    
    
	assert(phead);
	assert(phead->next != phead);
	DListErase(phead, phead->prev);//复用
	//不复用
	/*DList* tail = phead->prev;
	DList* tailprev = tail->prev;
	tailprev->next = phead;
	phead->prev = tailprev;
	free(tail);
	tail = NULL;*/

}

尾删

void DListPopFront(DList* phead)//头删
{
    
    
	assert(phead);
	assert(phead->next != phead);
	/*DList* front = phead->next;
	DList* frontback = front->next;
	phead->next = frontback;
	frontback->prev = phead;
	free(front);*/
	DListErase(phead, phead->next);
}

查找

找到返回结点地址,否则NULL

DList* DListFind(DList* pHead,  DLDateType x)//双向链表查找,找到就返回结点地址,否则返回NULL指针。
{
    
    

	assert(pHead);
	assert(pHead->next != pHead);
	DList* cur = pHead->next;
	while (cur!=pHead)//结点的环是哨兵结点
	{
    
    
		if (cur->date == x)
		{
    
    
			return cur;
		}
		cur = cur->next;
	}
	printf("未找到结点\n");
	return NULL;
}

销毁

void DlistDestroy(DList* pHead)//为了保持代码整齐性,我们free完所有结点,一定要将实参置为NULL;
{
    
    
	assert(pHead);
	DList* cur = pHead->next;
	//我们在销毁的时候,最后一个结点的next是pHead。
	while (cur!=pHead)
	{
    
    
		DList* tmp = cur;
		cur = cur->next;
		free(tmp);
		tmp = NULL;
	}
	free(pHead);
	pHead = NULL;
}

main

#include"DList.h"


void Text()
{
    
    

	DList* plist = NULL;
	DListInit(&plist);
	DListPushBack(plist, 1);
	DListPushBack(plist, 2);
	DListPushBack(plist, 3);
	DListPushBack(plist, 4);
	DListPushBack(plist, 5);
	DListPrint(plist);
	DListPushFront(plist, 6);
	DListPushFront(plist, 7);
	DListPushFront(plist, 8);
	DListPushFront(plist, 9);
	DListPrint(plist);
	DList* Find = DListFind(plist,9);
	DListInsert(plist, Find, 10);
	DListPrint(plist);
	DListErase(plist, Find);
	DListPrint(plist);

	DListPopBack(plist);
	DListPopBack(plist);
    DListPopBack(plist);
	DListPrint(plist);
	DListPopFront(plist);
	DListPopFront(plist);
	DListPopFront(plist);
	DListPopFront(plist);
	DListPopFront(plist);
	DListPrint(plist);
	DlistDestroy(plist);
	plist = NULL;
	printf("\t\t\t\t\t ---BY New Young\n");
}
int main ()
{
    
    

	Text();

	return 0;
}

效果展示

image-20211111225152805

总结

代码的实践,调试能力是一点一点积累,现在不锻炼,以后卖红薯!!!!哈哈哈~。

猜你喜欢

转载自blog.csdn.net/qq_55439426/article/details/121278390