双向循环链表

本文介绍一种带有哨兵位的双向循环链表。

目录

哨兵位的双向循环链表

结构

初始化

创建新节点BuyNode()

尾插

头插

尾删

头删

查找

插入

删除

销毁

完整代码

感谢观看!谢谢!!


哨兵位的双向循环链表

结构

如图所示,此链表有两个节点,一个prev指向前一个节点,一个next指向下一个节点。

代码:

typedef int LNDataType;//方便改动数据类型
typedef struct DListNode
{
	LNDataType data;
	struct DListNode* prev;//前一个节点
	struct DListNode* next;//后一个节点
}DLTN;

 

初始化

此链表带有哨兵位的 头节点,初始化时,就是将哨兵位创建出来,应是这样的结构:

让它自己指向自己。

代码: 

//初始化
DLTN* InitList()
{
	DLTN* head = BuyNode(-1);//BuyNode()是创建节点的函数
	head->next = head;
	head->prev = head;
	return head;
}

创建新节点BuyNode()

代码:

DLTN* BuyNode(LNDataType x)
{
	DLTN* node = (DLTN*)malloc(sizeof(DLTN));
	if (node == NULL)
	{
		perror("BuyNode malloc:");
		return NULL;
	}
	node->data = x;
	node->next = NULL;
	node->prev = NULL;

	return node;
}

尾插

在链表尾部插入新节点,首先要找到尾部位置,此链表的优势就体现出来了,头节点的prev就是指向链表的尾部的,所以我们这样做:

代码: 

void DLPushBack(DLTN* phead, LNDataType x)
{
	//找尾
	DLTN* tail = phead->prev;
	//创建新节点
	DLTN* newnode = BuyNode(x);

	//连接
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

头插

在链表头部插入新节点,注意:此头部不是哨兵位节点,而是指有效链表的第一个节点。

和尾插一样的思路,先找头部位置:哨兵位的next指向头部位置。然后创建新节点,插入。

代码:

void DLPushFront(DLTN* phead, LNDataType x)
{
	//找头
	DLTN* head = phead->next;
	//创建新节点
	DLTN* newnode = BuyNode(x);

	//连接
	newnode->next = head;
	head->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
}

尾删

删除尾部节点

代码:

void DLTPopBack(DLTN* phead)
{
	assert(phead);
	assert(!DLTEmpty(phead));

	DLTN* tail = phead->prev;//记录尾部节点
	DLTN* tailprev = tail->prev;//记录尾部的前一个节点
    //连接尾部的前一个节点,使其成为新的尾部
	tailprev->next = phead;
	phead->prev = tailprev;
    //释放旧尾部节点
	free(tail);
}

注意:有效链表全部删除后,就只剩哨兵位,此时不可再删除。DLTEmpty()是一个判空函数,当有效链表为空时,会返回true,否则返回false。

代码:

bool DLTEmpty(DLTN* phead)
{
	assert(phead);
	return phead == phead->next;
}

头删

删除头部节点

代码:

void DLTPopFront(DLTN* phead)
{
	assert(phead);
	assert(!DLTEmpty(phead));

	DLTN* head = phead->next;//找头
	DLTN* headnext = head->next;//找:头部位置的下一个节点
    //连接,形成新的头
	phead->next = headnext;
	headnext->prev = phead;
    //释放旧的头
    free(head);
}

头删尾删大同小异,可互相参考。

查找

代码:

DLTN* DLTFind(DLTN* phead, LNDataType x)
{
	assert(phead);
	DLTN* cur = phead->next;
    //根据提供的数据查找,遍历一遍
    //注意:此链表是循环的,遍历时从头开始,回到头结束,就视为一遍。
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
    //找不到返回空指针
	return NULL;
}

插入

这里的插入是在指定位置的前面插入新的节点。

代码:

//插入
void DLTInsert(DLTN* pos, LNDataType x)
{
	assert(pos);
	DLTN* posprev = pos->prev;//记录指定位置的前一个节点
	DLTN* newnode = BuyNode(x);
    //插入新节点
	posprev->next = newnode;
	newnode->prev = posprev;
	pos->prev = newnode;
	newnode->next = pos;
}

删除

这里的删除是删除指定位置的节点。

代码:

//删除
void DLTErase(DLTN* pos)
{
	assert(pos);
	DLTN* posprev = pos->prev;//记录当前位置的前一个节点
	DLTN* posnext = pos->next;//记录当前位置的下一个节点
    //连接前一个节点和下一个节点
	posnext->prev = posprev;
	posprev->next = posnext;
    //释放当前节点
	free(pos);
}

销毁

遍历一遍链表,进行销毁

代码:

//销毁
void LTDestory(DLTN* phead)
{
	assert(phead);
	DLTN* cur = phead->next;
	while (cur != phead)
	{
		DLTN* curnext = cur->next;
		free(cur);
		cur = curnext;
	}
	free(phead);
}

完整代码

分三个文件:

list.h

存放函数的声明,结构体的定义,头文件的引用等

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int LNDataType;
typedef struct DListNode
{
	LNDataType data;
	struct DListNode* prev;
	struct DListNode* next;
}DLTN;

//初始化
DLTN* InitList();
//打印
void DLTPrint(DLTN* phead);

void DLPushBack(DLTN* phead, LNDataType x);
void DLPushFront(DLTN* phead, LNDataType x);


void DLTPopFront(DLTN* phead);
void DLTPopBack(DLTN* phead);

//查找
DLTN* DLTFind(DLTN* phead, LNDataType x);
//插入
void DLTInsert(DLTN* pos, LNDataType x);
//删除
void DLTErase(DLTN* pos);
//销毁
void LTDestory(DLTN* phead);

list.c

具体实现链表的功能。

#include"list.h"
DLTN* BuyNode(LNDataType x)
{
	DLTN* node = (DLTN*)malloc(sizeof(DLTN));
	if (node == NULL)
	{
		perror("BuyNode malloc:");
		return NULL;
	}
	node->data = x;
	node->next = NULL;
	node->prev = NULL;

	return node;
}

bool DLTEmpty(DLTN* phead)
{
	assert(phead);
	return phead == phead->next;
}

//初始化
DLTN* InitList()
{
	DLTN* head = BuyNode(-1);
	head->next = head;
	head->prev = head;
	return head;
}
//打印
void DLTPrint(DLTN* phead)
{
	assert(phead);
	DLTN* cur = phead->next;
	printf("哨兵位<==>");
	while (cur != phead)
	{
		printf("%d<==>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
void DLPushBack(DLTN* phead, LNDataType x)
{
	//找尾
	DLTN* tail = phead->prev;
	//创建新节点
	DLTN* newnode = BuyNode(x);

	//连接
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;

	
	//DLTInsert(phead, x);
}

void DLPushFront(DLTN* phead, LNDataType x)
{
	//找头
	DLTN* head = phead->next;
	//创建新节点
	DLTN* newnode = BuyNode(x);

	//连接
	newnode->next = head;
	head->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;


	//DLTInsert(phead->next, x);
}

void DLTPopBack(DLTN* phead)
{
	assert(phead);
	assert(!DLTEmpty(phead));

	DLTN* tail = phead->prev;
	DLTN* tailprev = tail->prev;

	tailprev->next = phead;
	phead->prev = tailprev;
	free(tail);

	//DLTErase(phead->prev);
}

void DLTPopFront(DLTN* phead)
{
	assert(phead);
	assert(!DLTEmpty(phead));

	DLTN* head = phead->next;
	DLTN* headnext = head->next;

	free(head);

	phead->next = headnext;
	headnext->prev = phead;

	//DLTErase(phead->next);
}


//查找
DLTN* DLTFind(DLTN* phead, LNDataType x)
{
	assert(phead);
	DLTN* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
//插入
void DLTInsert(DLTN* pos, LNDataType x)
{
	assert(pos);
	DLTN* posprev = pos->prev;
	DLTN* newnode = BuyNode(x);

	posprev->next = newnode;
	newnode->prev = posprev;
	pos->prev = newnode;
	newnode->next = pos;
}
//删除
void DLTErase(DLTN* pos)
{
	assert(pos);
	DLTN* posprev = pos->prev;
	DLTN* posnext = pos->next;
	posnext->prev = posprev;
	posprev->next = posnext;
	free(pos);
}
//销毁
void LTDestory(DLTN* phead)
{
	assert(phead);
	DLTN* cur = phead->next;
	while (cur != phead)
	{
		DLTN* curnext = cur->next;
		free(cur);
		cur = curnext;
	}
	free(phead);
}

test.c

用来测试链表,可自行编写。

感谢观看!谢谢!!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值