[数据结构]---带头双向循环链表基本功能实现及与单链表,顺序表区别

本文介绍了带头双向循环链表的增删查改功能实现,包括初始化、申请结点、销毁、尾插、尾删、头插、头删、查找、插入和删除操作。强调了该链表结构在操作上的便利性,时间复杂度为O(1),并对比了顺序表和单链表,认为在现代计算机中,应优先考虑效率而非空间复杂度。
摘要由CSDN通过智能技术生成

带头双向循环链表增删查改实现

实现功能:

//初始化
void ListInit(List* plist);

//申请结点
ListNode* BuyListNode();

//销毁
void ListDestory(List* plist);

//尾插
void ListPushBack(List* plist, LTDataType x);

//尾删
void ListPopBack(List* plist);

//头插
void ListPushFront(List* plist, LTDataType x);

//头删
void ListPopFront(List* plist);

//查找
ListNode* ListFind(List* plist, LTDataType x);

// 在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);

// 删除pos位置的节点
void ListErase(List*plist,ListNode* pos);

//打印
void ListPrint(List* plist);

//List.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

//带头双向循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;

typedef struct List
{
	ListNode* _head;
}List;

//初始化
void ListInit(List* plist);

//申请结点
ListNode* BuyListNode();

//销毁
void ListDestory(List* plist);

//尾插
void ListPushBack(List* plist, LTDataType x);

//尾删
void ListPopBack(List* plist);

//头插
void ListPushFront(List* plist, LTDataType x);

//头删
void ListPopFront(List* plist);

//查找
ListNode* ListFind(List* plist, LTDataType x);

// 在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);

// 删除pos位置的节点
void ListErase(List*plist,ListNode* pos);

//打印
void ListPrint(List* plist);

//List.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "List.h"

//初始化
void ListInit(List* plist)
{
	plist->_head = BuyListNode(-1);
	plist->_head->_next = plist->_head;
	plist->_head->_prev = plist->_head;
}

//申请新的结点
ListNode* BuyListNode(LTDataType x)
{
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	//判断分配是否成功
	assert(newNode);
	newNode->_data = x;
	newNode->_next = NULL;
	newNode->_prev = NULL;
	return newNode;
}


//销毁
void ListDestory(List* plist)
{
	assert(plist);
	ListNode* tmp;
	tmp = plist->_head->_next;
	//不能先释放头结点,防止二次释放
	while (tmp!=plist->_head)
	{
		ListNode* next = tmp->_next;
		tmp = next;
	}
	//最后释放
	free(plist->_head);
	plist->_head = NULL;
}

// 在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);

	ListNode* newnode = BuyListNode(x); // 申请新结点
	ListNode* prev = pos->_prev; // pos前一个结点

	prev->_next = newnode; // 连接pos前一个结点和新结点
	newnode->_prev = prev;

	pos->_prev = newnode; // 连接pos和新结点
	newnode->_next = pos;

}

//头插
void ListPushFront(List* plist, LTDataType x)
{
	assert(plist);
	//头插和在第一个结点前面插入一样
	ListInsert(plist->_head->_next, x);
	//ListNode* next;
	//ListNode* newNode = BuyListNode(x);
	//next = plist->_head->_next;
	//plist->_head->_next = newNode;
	//newNode->_prev = plist->_head;
	//newNode->_next = next;
	//next->_prev = newNode;
}

//尾插
void ListPushBack(List* plist, LTDataType x)
{
	assert(plist);
	尾插和在头结点前面插入一个结点一样
	ListInsert(plist->_head, x);
	//ListNode* tail;
	//ListNode* newNode =BuyListNode(x) ;
	//tail = plist->_head->_prev;
	//tail->_next = newNode;
	//newNode->_prev = tail;
	//newNode->_next = plist->_head;
	//plist->_head->_prev = newNode;
}

// 删除当前pos位置的节点
void ListErase(List*plist,ListNode* pos)
{
	ListNode*prev, *next;
	assert(pos && plist);
	//注意空链表
	if (plist->_head == pos)
		return;

	prev = pos->_prev;
	next = pos->_next;
	prev->_next = next;
	next->_prev = prev;
	free(pos);
	pos = NULL;
}

//头删
void ListPopFront(List* plist)
{
	//assert(plist);
	//等于删除头结点后面的结点
	ListErase(plist,plist->_head->_next);
	//ListNode* ret,*next;
	不能删掉头节点
	//if (plist->_head = plist->_head->_next)
	//	return;
	//next = plist->_head->_next;
	//ret = next->_next;
	//plist->_head->_next = ret;
	//ret->_prev = plist->_head;
	//free(next);
	//next = NULL;
}

//尾删
void ListPopBack(List* plist)
{
	//assert(plist);
	//等于删除头结点前面的结点
	ListErase(plist,plist->_head->_prev);
	//ListNode* ret,*tail;
	注意不能删掉头节点
	//if (plist->_head == plist->_head->_next)
	//	return;
	//tail = plist->_head->_prev;
	//ret = tail->_prev;

	//ret->_next = plist->_head;
	//plist->_head->_prev = ret;
	//free(tail);
	//tail = NULL;
}

//查找
ListNode* ListFind(List* plist, LTDataType x)
{
	ListNode* NewNode;
	assert(plist);
	NewNode = plist->_head;
	while (NewNode->_next!=plist->_head)
	{
		NewNode = NewNode->_next;
		if(NewNode->_data==x)
		{
			return NewNode;
		}
	}
	printf("不存在此结点\n");
	return NULL;
}

//打印
void ListPrint(List* plist)
{
	assert(plist);
	//头不存放数据,所以直接指向头的下一个结点
	ListNode* ret=plist->_head->_next;
	while (ret!=plist->_head)
	{
		printf("%d  ", ret->_data);
		ret = ret->_next;
	}
	printf("\n");
}

注意:实现功能部分进行了必要的代码复用,注释中包含了不用进行复用时的写法

//ListTest.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "List.h"

int main()
{
	List phead;
	//ListNode* ret;
	ListInit(&phead);
	ListPushFront(&phead, 3);
	ListPushFront(&phead, 2);
	ListPushFront(&phead, 1);
    ListPushBack(&phead, 4);
	ListPushBack(&phead, 5);
	//ListPrint(&phead);//1 2 3 4 5
	ListPopBack(&phead);
	ListPopBack(&phead);
	ListPopBack(&phead);
	ListPopBack(&phead);
	ListPopBack(&phead);
	ListPopBack(&phead);
	//ListPopFront(&phead);
	//ListPopFront(&phead);
	//ListPopFront(&phead);
	//ListPopFront(&phead);
	//ListPopFront(&phead);
	//ListPopFront(&phead);
	//ret = ListFind(&phead, 3);
	//printf("%d\n", ret->_data);
	//ListPopBack(&ret);
	ListPrint(&phead);//2 4
	ListDestory(&phead);
	system("pause");
	return 0;
}

通过练习带头双向循环链表,我们不难发现这种结构进行操作时十分方便,因为无论给出哪一个节点我们都可以通过prev或者next指针直接拿到我们所需要的数据,操作时间复杂度为O(1),避免了对链表进行遍历.

   图片来源于: https://blog.csdn.net/belonghwl/article/details/100835387

对比前面的顺序表,单链表,还有今天看的双链表来看,如果我们有要去完成一个任务,假设以上三种数据结构我们都可以使用并完成任务,那么个人觉得使用顺序:顺序表>双链表>单链表. 因为现代计算机已经在存储大小方面远远优胜于之前的计算机,因此内存占用大小(空间复杂度)已经没有那么重要,更重要的关注的是效率(时间复杂度).

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值