欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

带头循环双链表

程序员文章站 2022-03-15 20:33:26
...

带头循环双链表,顾名思义“带头”就是说你能知道头结点的位置,“循环”就是说这个链表是循环的,“双链表”就是说这个链表除了有一个指针指向下一个结点还有一个指针指向前一个结点

typedef int DataType;

typedef struct dList{
	DataType data;
	struct dList *pPrev;
	struct dList *pNext;
}dList;

在双链表中我们实现了以下的基本操作

//创建一个新结点
dList *dListCreateNewNode(DataType data);

//双链表的初始化
void dListInit(dList **ppHead);

//双链表的打印
void dListPrint(dList *pHead);

//双链表的插入:尾插、头插、插入任一位置
void dListPushBack(dList *pHead, DataType data);
void dListPushFront(dList *pHead, DataType data);
void dListInsert(dList *pHead, dList *pos, DataType data);

//双链表的删除:尾删、头删、删除任一位置
void dListPopBack(dList *pHead);
void dListPopFront(dList *pHead);
void dListErase(dList *pHead, dList *pos);

//双链表的清理
void dListClear(dList *pHead);

//双链表的销毁
void dLisrDestroy(dList **ppHead);

1)申请结点

dList *dListCreateNewNode(DataType data)
{
	dList *node = (dList *)malloc(sizeof(dList));
	assert(node);
	node->data = data;
	node->pNext = NULL;
	node->pPrev = NULL;
	return node;
}

2)初始化(此时申请的这一个节点很重要,它的pNext将指向第一个数据结点,pPrev将指向最后一个数据结点)

void dListInit(dList **ppHead)
{
	assert(ppHead != NULL);
	//创建一个结点,用其指向首结点(结点里面的数据无意义)
	(*ppHead) = dListCreateNewNode(0);
	//让结点的两个指针暂且指针自己
	(*ppHead)->pNext = *ppHead;
	(*ppHead)->pPrev = *ppHead;
}

3)打印

void dListPrint(dList *pHead)
{
	assert(pHead != NULL);
	dList *pdList = pHead->pNext;
	//依次遍历链表将每个结点打印
	while (pdList != pHead){
		printf("%d -> ", pdList->data);
		pdList = pdList->pNext;
	}
	printf("NULL\n");
}

4)插入

void dListPushBack(dList *pHead, DataType data)
{
	assert(pHead != NULL);
#if 0
	//创建一个结点
	dList *pdList = dListCreateNewNode(data);

	//让申请的结点的pNext指向头结点,pPrev指向原来的最后一个节点
	pdList->pNext = pHead;
	pdList->pPrev = pHead->pPrev;

	//让头结点的pPrev指向现在最后一个结点,让原来的最后一个结点的pNext指向现在的最后一个节点
	pHead->pPrev = pdList;
	pdList->pPrev->pNext = pdList;
#endif
	dListInsert(pHead, pHead, data);
}

void dListPushFront(dList *pHead, DataType data)
{
	assert(pHead != NULL);
#if 0
	dList *pdList = dListCreateNewNode(data);

	//让新的结点的pNext指向原来的第一个结点,pPrev指向头结点
	pdList->pNext = pHead->pNext;
	pdList->pPrev = pHead;

	//头结点的pNext指向现在的第一个结点,让原来的第一个结点的pPrev指向现在的第一个结点
	pHead->pNext = pdList;
	pdList->pNext->pPrev = pdList;
#endif
	dListInsert(pHead, pHead->pNext, data);
}

void dListInsert(dList *pHead, dList *pos, DataType data)
{
	assert(pHead != NULL);
	dList *pdList = dListCreateNewNode(data);

	//让新结点的pNext指向指定结点,pPrev指向指定结点的前一个结点
	pdList->pNext = pos;
	pdList->pPrev = pos->pPrev;

	//指定结点的pPrev指向新结点,指定结点的pNext指向新结点
	pos->pPrev = pdList;
	pdList->pPrev->pNext = pdList;
}

5)删除

void dListPopBack(dList *pHead)
{
	assert(pHead != NULL);
	//删除结点得保证现在至少有一个数据结点
	assert(pHead->pNext != pHead);
#if 0
	//记录要删除的结点
	dList *pos = pHead->pPrev;

	//将最后一个结点的前一个结点的pNext指向头结点
	//将头结点的pPrev指向最后一个结点的前一个节点
	pHead->pPrev->pPrev->pNext = pHead;
	pHead->pPrev = pHead->pPrev->pPrev;

	//释放最后一个结点
	free(pos);
#endif
	dListErase(pHead, pHead->pPrev);
}

void dListPopFront(dList *pHead)
{
	assert(pHead != NULL);
	assert(pHead->pNext != pHead);
#if 0
	dList *pos = pHead->pNext;

	//将头结点的pNext指向第一个结点的下一个结点
	//将第一个结点的下一个节点的pPrev指向头结点
	pHead->pNext = pHead->pNext->pNext;
	pHead->pNext->pNext->pPrev = pHead;

	free(pos);
#endif
	dListErase(pHead, pHead->pNext);
}

void dListErase(dList *pHead, dList *pos)
{
	assert(pHead != NULL);
	assert(pHead->pNext != pHead);

	//指定结点的前一个结点的pNext指向指定结点的下一个结点
	//指定结点的下一个节点的pPrev指向指定结点的前一个节点
	pos->pPrev->pNext = pos->pNext;
	pos->pNext->pPrev = pos->pPrev;

	free(pos);
}

6)清理(清理的结果是将所有的数据结点都删掉,只留下头结点)

void dListClear(dList *pHead)
{
	assert(pHead);
	dList *pdList = pHead->pNext;
	dList *pos;
	while ( pdList != pHead){
		pos = pdList;
		pdList = pdList->pNext;
		free(pos);
	}
	pHead->pNext = pHead;
	pHead->pPrev = pHead;
}

7)销毁(清理所有申请的结点)

void dLisrDestroy(dList **ppHead)
{
	dListClear(*ppHead);
	free(*ppHead);
	(*ppHead) = NULL;
}

总结:双链表的每个结点都知道它的前一个结点是谁后一个结点是谁,查找就相对容易,当然如果对链表进行改动就相对麻烦了些,单双链表的选择还需视情况而定

相关标签: 带头循环双链表