数据结构+带头结点双向循环链表+(C语言)

针对带头的双向循环链表的基本功能展开讲解 

1.结构体定义

 a.首先是定义结构体,第一行使用typedef来进行重命名(方便后续进行更改),然后就是结构体中内容的定义首先是一个数据域用来存储数据,因为是双向循环链表所以必须定义两个指针,一个是_next指向后面,另一个是_prev指针指向前面.因为在结构体定义的时候在前面加了typedef所以最后面的变量ListNode就是代替struct ListNode的作用.

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;

2.创建结点

a.我们不使用二级指针所以将该函数的返回值设成listnode*类型创建好结点之后就可以返回其地址,首先我们先创建一个指针变量来存储所申请的空间地址,然后进行空间的申请,之后就是让其前后指针分别指向空以便于后期进行操作,传的参数x就是数据域的值,最后直接赋值就行.然后再返回他的地址便于进行后面的操作.

ListNode* ListCreate(LTDataType x) {
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->_next = NULL;
	node->_prev = NULL;
	node->_data = x;
	return node;
}

3.初始化

a.初始化就用来完善头结点的,因为我们这是个带头的双向循环链表,因此这个头结点不能带有有效数据我们将它置位0,因为是双向循环链表所以要进行头插数据或者尾插数据鼻血对头结点进行处理让他的前后指针都指向自己,这样在后续插入数据的时候就可以完成了.(这个初始化处理只针对头结点).

void ListNodeInit(ListNode* phead) {
	phead->_next = phead;
	phead->_prev = phead;
}

4.双向链表打印

a.我们要打印结点必须从头结点的后一个开始打印因此创建一个cur指针用来存储,头结点后的第一个数据结点,并使用while循环来进行遍历并打印,因为我们的判出条件是cur的next所以最后一个数据结点并不能打印,所以当循环结束后我们再手动打印最后一个结点.(当然如果对判出条件进行一定处理也可以在while中进行全部打印,这里不进行详细说明).

void ListPrint(ListNode* pHead) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	while (cur->_next != pHead) {
		printf("%d--->", cur->_data);
		cur = cur->_next;
	}
	printf("%d", cur->_data);
	printf("\n");
}

5.双向链表尾插

a.在进行插入前先判断传进来的参数是否合法,如果不为空在进行后续操作,首先定义个指针变量cur存储用前面的结点创建函数所创建的空间的首地址,其次因为是尾插所以我们需要找到尾结点,我们的链表是双向循环链表所以只需取头结点的前一个结点就是尾结点,然后进行指针转换将新节点插入最后面.(具体如图所示)

void ListPushBack(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* tail = pHead->_prev;
	tail->_next = cur;
	pHead->_prev = cur;
	cur->_next = pHead;
	cur->_prev = tail;
}

6.双向链表尾删

a.在进行删除前先判断传进来的参数是否合法,如果不为空在进行后续操作,与尾插相似,我们首先得找到尾结点记作tail,然后进行指针的转换,删除后释放掉空间.(具体看图)

void ListPopBack(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* tail = pHead->_prev;
	pHead->_prev = pHead->_prev->_prev;
	pHead->_prev->_next = pHead;
	free(tail);
}

7.双向链表头插

a..在进行头插前先判断传进来的参数是否合法,如果不为空在进行后续操作,先定义个指针变量cur存储用前面的结点创建函数所创建的空间的首地址,其次因为是头插,所以我们得找到第一个数据结点,也就是head的next,然后进行指针变换进行插入.(具体如图)

void ListPushFront(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* Next = pHead->_next;
	pHead->_next = cur;
	cur->_next = Next;
	Next->_prev = cur;
	cur->_prev = pHead;
}

8.双向链表头删

a.在进行删除前先判断传进来的参数是否合法,如果不为空在进行后续操作,与头插相似,先找到第一个数据结点cur,也就是head的next,其次就是第二个数据结点,也就是head的next的next,再进行相关指针操作,最后释放掉cur.(如图)

void ListPopFront(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	ListNode* Next = pHead->_next->_next;
	pHead->_next = Next;
	Next->_prev = pHead;
	free(cur);
}

9.双向链表查找

a.我们在这里定义函数时设置其返回值为listnode*类型,作用传递相应参数x,使用while循环遍历整个链表找到数据域与x相等的结点返回这个结点的地址,如果没找到则返回空.

ListNode* ListFind(ListNode* pHead, LTDataType x) {
	if (NULL == pHead || pHead->_next == pHead) {
		return NULL;
	}
	ListNode* temp = pHead->_next;
	while (temp != pHead) {
		if (temp->_data == x) {
			return temp;
		}
		temp = temp->_next;
	}
	return NULL;
}

10.双向链表在pos的前面进行插入

a.与头插类似,在进行操作前先判断是否可以找到该位置能找到在进行后续操作,在这里我们要是用到上面的查找函数,找到你所要插入的位置再进行插入,然后就是找到这个位置的前面一个结点记为prv,再就是新创建的结点使用cur来存储其地址,然后进行指针的变换,prv的next指向cur,该位置的结点pos的prev指向cur,再就是cur的prev指向prv,cur的next指向pos,完成插入.

void ListInsert(ListNode* pos, LTDataType x) {
	if (NULL == pos) {
		printf("插入数据的位置不存在!\n");
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* prv = pos->_prev;
	prv->_next = cur;
	pos->_prev = cur;
	cur->_prev = prv;
	cur->_next = pos;
}

11.双向链表删除pos位置的节点

a.在进行操作前先判断是否可以找到该位置能找到在进行后续操作,首先就是调用查找函数找到要插入的位置记作pos,然后取该位置的前一个记作prv,后一个记作Next,之后就是指针的操作,prv的下一个指向Next,Next的上一个指向prv,最后释放掉pos位置结点的空间.成功删除.

void ListErase(ListNode* pos) {
	if (NULL == pos) {
		printf("删除数据的位置不存在!\n");
		return;
	}
	ListNode* prv = pos->_prev;
	ListNode* Next = pos->_next;
	prv->_next = Next;
	Next->_prev = prv;
	free(pos);
}

12.销毁链表

a.在我们的所有操作进行完之后,需要把链表进行销毁.这非常简单,我们使用while循环调用头删或尾删函数把除头结点之外的数据结点全部删除并释放空间(我们这里使用头删),这里要注意我们传参的时候传的是二级指针,因为我释放完头结点的空间之后,head这个指针存着的地址就没有空间了也就表示head这个指针成为了野指针,我们需要对其进行重新赋值NULL,使其不为野指针,但要真正改变指针的值在传参的时候则必须传递这个指针的地址,才能进行修改,因此我们使用二级指针,也就是传递指针的地址.

void ListDestory(ListNode** pHead) {
	if (NULL == (*pHead)) {
		return;
	}
	while ((*pHead)->_next!=*pHead) {
		ListPopFront(*pHead);
	}
	free(*pHead);
	(*pHead) = NULL;
}

最后就是进行的一系列测试和测试截图以及全部代码:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//结构体定义
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;
//创建结点
ListNode* ListCreate(LTDataType x) {
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->_next = NULL;
	node->_prev = NULL;
	node->_data = x;
	return node;
}
//初始化
void ListNodeInit(ListNode* phead) {
	phead->_next = phead;
	phead->_prev = phead;
}
// 双向链表销毁
void ListDestory(ListNode** pHead) {
	if (NULL == (*pHead)) {
		return;
	}
	while ((*pHead)->_next!=*pHead) {
		ListPopFront(*pHead);
	}
	free(*pHead);
	(*pHead) = NULL;
}
// 双向链表打印
void ListPrint(ListNode* pHead) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	while (cur->_next != pHead) {
		printf("%d--->", cur->_data);
		cur = cur->_next;
	}
	printf("%d", cur->_data);
	printf("\n");
}
// 双向循环链表尾插
void ListPushBack(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* tail = pHead->_prev;
	tail->_next = cur;
	pHead->_prev = cur;
	cur->_next = pHead;
	cur->_prev = tail;
}
// 双向链表尾删
void ListPopBack(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* tail = pHead->_prev;
	pHead->_prev = pHead->_prev->_prev;
	pHead->_prev->_next = pHead;
	free(tail);
}
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* Next = pHead->_next;
	pHead->_next = cur;
	cur->_next = Next;
	Next->_prev = cur;
	cur->_prev = pHead;
}
// 双向链表头删
void ListPopFront(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	ListNode* Next = pHead->_next->_next;
	pHead->_next = Next;
	Next->_prev = pHead;
	free(cur);
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x) {
	if (NULL == pHead || pHead->_next == pHead) {
		return NULL;
	}
	ListNode* temp = pHead->_next;
	while (temp != pHead) {
		if (temp->_data == x) {
			return temp;
		}
		temp = temp->_next;
	}
	return NULL;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x) {
	if (NULL == pos) {
		printf("插入数据的位置不存在!\n");
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* prv = pos->_prev;
	prv->_next = cur;
	pos->_prev = cur;
	cur->_prev = prv;
	cur->_next = pos;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos) {
	if (NULL == pos) {
		printf("删除数据的位置不存在!\n");
		return;
	}
	ListNode* prv = pos->_prev;
	ListNode* Next = pos->_next;
	prv->_next = Next;
	Next->_prev = prv;
	free(pos);
}
//测试函数
void main() {
	ListNode* phead = ListCreate(0);
	ListNodeInit(phead);
	ListPushBack(phead, 10);
	ListPushBack(phead, 11);
	ListPushBack(phead, 12);
	ListPushBack(phead, 13);
	ListPushBack(phead, 14);
	ListPushBack(phead, 15);
	ListPrint(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPrint(phead);
	ListPushFront(phead, 1);
	ListPushFront(phead, 5);
	ListPushFront(phead, 3);
	ListPushFront(phead, 4);
	ListPrint(phead);
	ListPopFront(phead);
	ListPopFront(phead);
	ListPopFront(phead);
	ListPrint(phead);
	ListInsert(ListFind(phead,11), 90);
	ListPrint(phead);
	ListErase(ListFind(phead, 90));
	ListPrint(phead);
	ListDestory(&phead);
}

测试截图

猜你喜欢

转载自blog.csdn.net/weixin_49312527/article/details/121582276