数据结构:双向循环链表的实现(附有详细图解)

双向循环链表的实现

双向循环链表结构图
数据结构:双向循环链表的实现(附有详细图解)_第1张图片

具体函数操作如下:
(1)动态申请节点

DCListNode* BuyDCListNode(DLDataType x)// 动态申请节点
{
     
	DCListNode* newNode = (DCListNode*)malloc(sizeof(DCListNode));
	if (NULL == newNode)
	{
     
		assert(0);
		return NULL;
	}

	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;

	return newNode;

}

(2)初始化
申请好头节点,头节点的前驱和后继都指向自己

DCListNode* DCListInit()// 初始化
{
     
	//只需要申请好头节点
	DCListNode* head = BuyDCListNode(0);

	head->next = head;
	head->prev = head;

	return head;
}

(3)将有效节点都清除
这里采取头删的方法清除有效节点,头删方法的具体实现下面会仔细解释。在对双向链表进行遍历时,需要对指针加以限定,否则会进入无限循环的状态,判断循环指针是否等于头节点即可。
清除有效节点后,元素为空,所以需要将头指针的前驱和后继指向自己。

void DCListClear(DCListNode* pHead)// 将有效节点清除
{
     
	assert(pHead);
	DCListNode* tail = pHead->next;

	while (tail != pHead)
	{
     
		//采用头删的方法
		pHead->next = tail->next;
		free(tail);
		tail = pHead->next;

	}
	//链表为空
	pHead->next = pHead;
	pHead->prev = pHead;
}

(4)销毁
在进行销毁操作时,需要将头节点和有效节点都进行清除,调用清除的函数,然后将头节点进行删除设置为空,避免成为野指针。

void DCListDestory(DCListNode** pHead)// 双向链表销毁 头节点和有效节点都需要进行清除
{
     
	assert(pHead);
	DCListClear(*pHead);
	free(*pHead);
	*pHead = NULL;
}

(5)双向链表的打印

void DCListPrint(DCListNode* head)// 双向链表打印
{
     
	DCListNode* tail = head->next;
	while (tail != head)
	{
     
		printf("%d ", tail->data);
		tail = tail->next;
	}
	printf("\n");
}

(6)双向链表的长度
完成链表的遍历后,使用count进行计数,则可得到链表的长度

int DCListSize(DCListNode* pHead) //双向链表长度
{
     
	assert(pHead);
	DCListNode* tail = pHead->next;
	int count = 0;
	while (tail != pHead)
	{
     
		++count;
		tail = tail->next;
	}
	return count;
}

(7)判空
链表为空,则头指针指向自身

int DCListEmpty(DCListNode* pHead) //判断是否为空
{
     
	assert(pHead);
	return pHead->next == pHead;
}

(8)查找

DCListNode* DCListFind(DCListNode* pHead, DLDataType x)// 双向链表查找
{
     
	assert(pHead);
	DCListNode* tail = pHead->next;
	while (tail != pHead)
	{
     
		if (x == tail->data)
			return tail;

		tail = tail->next;
	}
	return NULL;
}

(9)在pos位置插入新节点
双向链表不存在插入pos位置之后还是之前的问题,所以这里我们选择插入pos位置之前。
①先不要断开原来的链表,先将新节点连接在原链表中
②断开原链表,插入新节点,让对应节点的指针域指向新插入的节点
数据结构:双向循环链表的实现(附有详细图解)_第2张图片

void DCListInsert(DCListNode* pos, DLDataType x)// 双向链表在pos的前面进行插入
{
     
	if (NULL == pos)
		return;

	DCListNode* newNode = BuyDCListNode(x);

	//1.先将新节点连接在原链表中
	newNode->next = pos;
	newNode->prev = pos->prev;

	//2.断开原链表,插入新节点
	newNode->prev->next = newNode;
	pos->prev = newNode;

}

(10)在pos位置删除节点
先连接再删除,如图解:
数据结构:双向循环链表的实现(附有详细图解)_第3张图片

void DCListErase(DCListNode* pos)// 双向链表删除pos位置的节点
{
     
	if (NULL == pos)
		return;

	pos->prev->next = pos->prev;
	pos->next->prev = pos->prev;
	free(pos);
}

(11)尾插
实现尾插操作时,可以直接调用插入元素函数,头节点即是pos位置
数据结构:双向循环链表的实现(附有详细图解)_第4张图片

void DCListPushBack(DCListNode* pHead, DLDataType x)// 双向链表尾插
{
     
	assert(pHead);
	DCListInsert(pHead, x);
}

(12)尾删
在进行尾删操作时,调用任意位置删除函数,pos位置是pHead->prev,即可完成删除操作
数据结构:双向循环链表的实现(附有详细图解)_第5张图片

void DCListPopBack(DCListNode* pHead)// 双向链表尾删
{
     
	if (DCListEmpty(pHead))
		return;
	DCListErase(pHead->prev);
}

(13)头插
实现头插操作时,调用插入元素的函数,pos位置为pHead->next 即可实现头插操作

void DCListPushFront(DCListNode* pHead, DLDataType x)// 双向链表头插
{
     
	assert(pHead);
	DCListInsert(pHead->next, x);
}

(14)头删
在进行头删操作时,调用任意位置删除函数,pos位置是pHead->next,即可完成删除操作

void DCListPopFront(DCListNode* pHead)// 双向链表头删
{
     
	if (DCListEmpty(pHead))
		return;
	DCListErase(pHead->next);
}

这次依然将主要的代码分成了三个源文件,DCList.c,test.c,DCList.h,其中DCList.c实现代码中的各个函数,test.c实现顺序表的相关测试,DCList.h实现用到的各种头文件与函数声明。
DCList.h

#pragma once

typedef int DLDataType;
typedef struct DCListNode
{
     
	DLDataType data;
	struct DCListNode* next;//指向当前节点的下一个节点
	struct DCListNode* prev;//指向当前节点的前一个结点
}DCListNode;
 
DCListNode* BuyDCListNode(DLDataType x);// 动态申请节点
DCListNode* DCListInit();// 创建返回链表的头结点
void DCListClear(DCListNode* pHead);// 将有效节点清除
void DCListDestory(DCListNode** pHead);// 双向链表销毁
void DCListPrint(DCListNode* pHead);// 双向链表打印
int DCListSize(DCListNode* pHead); //双向链表长度

void DCListPushBack(DCListNode* pHead, DLDataType x);// 双向链表尾插
void DCListPopBack(DCListNode* pHead);// 双向链表尾删
void DCListPushFront(DCListNode* pHead, DLDataType x);// 双向链表头插
void DCListPopFront(DCListNode* pHead);// 双向链表头删


DCListNode* DCListFind(DCListNode* pHead, DLDataType x);// 双向链表查找
void DCListInsert(DCListNode* pos, DLDataType x);// 双向链表在pos的前面进行插入
void DCListErase(DCListNode* pos);// 双向链表删除pos位置的节点

DCList.c

#include "DCList.h"
#include 
#include 
#include 

DCListNode* BuyDCListNode(DLDataType x)// 动态申请节点
{
     
	DCListNode* newNode = (DCListNode*)malloc(sizeof(DCListNode));
	if (NULL == newNode)
	{
     
		assert(0);
		return NULL;
	}

	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;

	return newNode;

}

DCListNode* DCListInit()// 初始化
{
     
	//只需要申请好头节点
	DCListNode* head = BuyDCListNode(0);

	head->next = head;
	head->prev = head;

	return head;
}

void DCListClear(DCListNode* pHead)// 将有效节点清除
{
     
	assert(pHead);
	DCListNode* tail = pHead->next;

	while (tail != pHead)
	{
     
		//采用头删的方法
		pHead->next = tail->next;
		free(tail);
		tail = pHead->next;

	}
	//链表为空
	pHead->next = pHead;
	pHead->prev = pHead;
}

void DCListDestory(DCListNode** pHead)// 双向链表销毁 头节点和有效节点都需要进行清除
{
     
	assert(pHead);
	DCListClear(*pHead);
	free(*pHead);
	*pHead = NULL;
}

void DCListPrint(DCListNode* head)// 双向链表打印
{
     
	DCListNode* tail = head->next;
	while (tail != head)
	{
     
		printf("%d ", tail->data);
		tail = tail->next;
	}
	printf("\n");
}

int DCListSize(DCListNode* pHead) //双向链表长度
{
     
	assert(pHead);
	DCListNode* tail = pHead->next;
	int count = 0;
	while (tail != pHead)
	{
     
		++count;
		tail = tail->next;
	}
	return count;
}

int DCListEmpty(DCListNode* pHead) //判断是否为空
{
     
	assert(pHead);
	return pHead->next == pHead;
}

void DCListPushBack(DCListNode* pHead, DLDataType x)// 双向链表尾插
{
     
	assert(pHead);
	DCListInsert(pHead, x);
}

void DCListPopBack(DCListNode* pHead)// 双向链表尾删
{
     
	if (DCListEmpty(pHead))
		return;
	DCListErase(pHead->prev);
}

void DCListPushFront(DCListNode* pHead, DLDataType x)// 双向链表头插
{
     
	assert(pHead);
	DCListInsert(pHead->next, x);
}

void DCListPopFront(DCListNode* pHead)// 双向链表头删
{
     
	if (DCListEmpty(pHead))
		return;
	DCListErase(pHead->next);
}

DCListNode* DCListFind(DCListNode* pHead, DLDataType x)// 双向链表查找
{
     
	assert(pHead);
	DCListNode* tail = pHead->next;
	while (tail != pHead)
	{
     
		if (x == tail->data)
			return tail;

		tail = tail->next;
	}
	return NULL;
}

void DCListInsert(DCListNode* pos, DLDataType x)// 双向链表在pos的前面进行插入
{
     
	if (NULL == pos)
		return;

	DCListNode* newNode = BuyDCListNode(x);

	//1.先将新节点连接在原链表中
	newNode->next = pos;
	newNode->prev = pos->prev;

	//2.断开原链表,插入新节点
	newNode->prev->next = newNode;
	pos->prev = newNode;

}

void DCListErase(DCListNode* pos)// 双向链表删除pos位置的节点
{
     
	if (NULL == pos)
		return;

	pos->prev->next = pos->prev;
	pos->next->prev = pos->prev;
	free(pos);
}

test.c

#include "DCList.h"

int main()
{
     
	DCListNode* head = DCListInit();

	DCListPushBack(head, 1);
	DCListPushBack(head, 2);
	DCListPushBack(head, 3);
	DCListPushBack(head, 4);
	DCListPushBack(head, 5);
	DCListPrint(head);

	DCListPushFront(head, 0);
	DCListPrint(head);


	DCListPopBack(head);
	DCListPopFront(head);
	DCListPrint(head);

	DCListInsert(DCListFind(head, 3), 10);
	DCListPrint(head);

	DCListErase(DCListFind(head, 3));
	DCListPrint(head);

	DCListDestory(&head);

	return 0;
}

你可能感兴趣的:(数据结构)