前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【数据结构】双向链表

【数据结构】双向链表

作者头像
YoungMLet
发布2024-03-01 10:26:43
910
发布2024-03-01 10:26:43
举报
文章被收录于专栏:C++/Linux
双向链表

带头双向循环链表的实现

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

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;
		}

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-01-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 双向链表
  • 带头双向循环链表的实现
    • 1. 函数的声明
      • 2. 函数的实现
        • 3. 主函数测试
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档