【数据结构】双向链表

2024-03-01 10:26:43 浏览数 (2)

双向链表

带头双向循环链表的实现

今天我们来实现一下带头双向循环链表,顾名思义,带头就是有哨兵位,哨兵位不是链表的头,它是连接头节点的一个节点,方便操作链表而已;双向就是一个节点可以找到下一个节点,也可以找到上一个节点,所以每个节点应该有两个结构体指针;循环就是没有空指针,哨兵位的上一个节点是尾,尾的下一个节点是哨兵位;如下图为带头双向循环链表:

1. 函数的声明

以下是函数的声明部分,我们主要实现双向链表的增删查改功能;

代码语言:javascript复制
		#pragma once
		#include <stdio.h>
		#include <stdlib.h>
		#include <assert.h>
		#include <stdbool.h>
		
		//类型的命名
		typedef int LTDataType;
		
		//定义节点
		typedef struct ListNode
		{
			struct ListNode* prev;
			struct ListNode* next;
			LTDataType data;
		}LTNode;
		
		//初始化链表
		LTNode* ListInit();
		
		//打印链表
		void ListPrint(LTNode* phead);
		
		//判断是否是空链表
		bool IsEmpty(LTNode* phead);
		
		//尾插、头插、头删、尾删
		void ListPushBack(LTNode* phead, LTDataType x);
		void ListPushFront(LTNode* phead, LTDataType x);
		void ListPopFront(LTNode* phead);
		void ListPopBack(LTNode* phead);
		
		//查找
		LTNode* LTFindPos(LTNode* phead, LTDataType x);
		
		//在pos位置之前插入
		void LTInsertPos(LTNode* pos, LTDataType x);
		
		//删除pos位置
		void LTErasePos(LTNode* pos);
		
		//销毁
		void LTDestroy(LTNode* phead);

2. 函数的实现

为了防止创建新的节点的时候重复出现,我们将创建节点写成一个函数:

代码语言:javascript复制
		LTNode* BuyListNode(int x)
		{
			LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
			assert(newnode);
		
			newnode->data = x;
			newnode->next = NULL;
			newnode->prev = NULL;
			return newnode;
		}

初始化节点,只需要一个哨兵位,它的next和prev都指向自己;

代码语言:javascript复制
		LTNode* ListInit()
		{
			LTNode* phead = BuyListNode(-1);
			phead->next = phead;
			phead->prev = phead;
		
			return phead;
		}

打印链表的函数:

代码语言:javascript复制
		void ListPrint(LTNode* phead)
		{
			assert(phead);
		
			LTNode* cur = phead->next;
			printf("guard<==>");
			while (cur != phead)
			{
				printf("%d<==>",cur->data);
				cur = cur->next;
			}
			printf("n");
		}

由于头删尾删的时候不能是空链表,带头的链表的空链表相当于只有一个哨兵位,所以头删尾删的时候链表中不能只剩下哨兵位;

代码语言:javascript复制
		bool IsEmpty(LTNode* phead)
		{
			assert(phead);
			return phead->next == phead;
		}

尾插函数:

代码语言:javascript复制
		void ListPushBack(LTNode* phead,LTDataType x)
		{
			assert(phead);
			LTNode* newnode = BuyListNode(x);
			LTNode* tail = phead->prev;
		
			newnode->next = phead;
			tail->next = newnode;
		
			newnode->prev = tail;
			phead->prev = newnode;
		}

头插函数:

代码语言:javascript复制
		void ListPushFront(LTNode* phead, LTDataType x)
		{
			assert(phead);
			LTNode* newnode = BuyListNode(x);
			LTNode* next = phead->next;
		
			newnode->next = next;
			newnode->prev = phead;
			next->prev = newnode;
			phead->next = newnode;
		}

头删函数:

代码语言:javascript复制
		void ListPopFront(LTNode* phead)
		{
			assert(phead);
			assert(!IsEmpty(phead));
		
			LTNode* del = phead->next;
			LTNode* next = del->next;
		
			phead->next = next;
			next->prev = phead;
			free(del);
		}

尾删函数:

代码语言:javascript复制
		void ListPopBack(LTNode* phead)
		{
			assert(phead);
			assert(!IsEmpty(phead));
		
			LTNode* tail = phead->prev;
			LTNode* tailprev = tail->prev;
		
			tailprev->next = phead;
			phead->prev = tailprev;
			free(tail);
		}

查找某个节点的函数,返回找到的节点,否则返回空;

代码语言:javascript复制
		LTNode* LTFindPos(LTNode* phead, LTDataType x)
		{
			assert(phead);
		
			LTNode* cur = phead->next;
			while (cur)
			{
				if (cur->data == x)
				{
					return cur;
				}
				cur = cur->next;
			}
			return NULL;
		}

在pos位置之前插入的插入函数:

代码语言:javascript复制
		void LTInsertPos(LTNode* pos, LTDataType x)
		{
			assert(pos);
		
			LTNode* newnode = BuyListNode(x);
		
			LTNode* ptr = pos->prev;
		
			ptr->next = newnode;
			newnode->next = pos;
			pos->prev = newnode;
			newnode->prev = ptr;
		}

删除pos位置的函数:

代码语言:javascript复制
		void LTErasePos(LTNode* pos)
		{
			assert(pos);
			assert(!IsEmpty(pos));
		
			LTNode* ptr = pos->prev;
			LTNode* next = pos->next;
		
			ptr->next = next;
			next->prev = ptr;
			free(pos);
		}

销毁链表的函数:

代码语言:javascript复制
		void LTDestroy(LTNode* phead)
		{
			assert(phead);
			LTNode* cur = phead->next;
		
			while (cur != phead)
			{
				LTNode* next = cur->next;
				free(cur);
				cur = next;
			}
			free(phead);
		}

3. 主函数测试

代码语言:javascript复制
		#include "List.h"
		
		int main()
		{
			LTNode* phead = ListInit();
		
			ListPushBack(phead, 1);
			ListPushBack(phead, 2);
			ListPushBack(phead, 3);
			ListPushBack(phead, 4);
		
			ListPushFront(phead, 10);
		
			ListPopBack(phead);
		
			ListPopFront(phead);
		
			LTNode* pos = LTFindPos(phead, 3);
			LTInsertPos(pos, 20);
			LTErasePos(pos);
		
		
			ListPrint(phead);
			LTDestroy(phead);
			return 0;
		}

以上就是带头双向循环链表的基本实现,感兴趣的伙伴可以自行尝试噢!!!

0 人点赞