基础数据结构(链表2)

目录

前言:暑期复习之双链表的简单理解

一·双链表的设计思路

 二·双链表的具体代码详解

1.初始化操作

2.双链表的打印

3.查询双链表

4.插入操作

5.删除操作

6.清空与销毁函数

三·双链表的总结


前言:暑期复习之双链表的简单理解

在基础数据结构(链表1)中复习了单链表的设计思路以及代码的具体详解,链表中还有一个双链表,也就是循环链表。

一·双链表的设计思路

双向链表也叫做双向表,是链表的一种,它由多个结点组成,顾名思义,也就是在双向链表的节点中有两个指针域,其中一指向直接后继,另一指向直接前驱,数据域用来存储数据。链表的头节点的数据域不存储数据指向前驱结点的指针域值为null,指向后继节点的指针域值指向第一个真正存储数据的结点

typedef int ElemType;
typedef struct DuLNode
{
	ElemType data;//数据域
	struct DuLNode* prev;//前驱指针
	struct DuLNode* next;//后继指针
}DuLNode,*PDuLNode;

typedef struct
{
	DuLNode* head;//PDuNode head
	int cursize;//数据节点的个数
}DuLinkList;

 二·双链表的具体代码详解

对链表的操作无非就是增删改查操作

1.初始化操作

由上图的逻辑图示可以很清楚的看到,前驱指针和后驱指针都会指向自身,head也指向新申请的这个节点

//双链表的初始化
void InitDuList(DuLinkList* pList)
{
	assert(pList != NULL);
	pList->cursize = 0;
	DuLNode*s = (DuLNode*)malloc(sizeof(DuLNode));//从堆内申请空间
	if (NULL == s)
	{
		exit(1);
	}
	s->next = s;
	s->prev = s;
	pList->head = s;
}

2.双链表的打印

双链表的打印操作:就是以结点的next域为判断条件,如果next域不是指回头节点,那么进入循环进行打印,然后指针向后移动,继续打印,如果next域指回头结点,那么说明链表中的数据结点已经全部打印完成,退出循环即可。

//双链表的打印
void PrintDuList(const DuLinkList* pList)
{
	assert(pList != NULL);
	DuLNode* p = pList->head->next;
	while (p != pList->head)
	{
		printf("%d", p->data);
		p = p->next;
	}
	printf("\n");
}

3.查询双链表

会打印就会查询,这两个函数非常相似,就不过多解释了。

//查询双链表的数据元素
DuLNode* FindValu(const DuLinkList* pList, ElemType val)
{
	assert(pList != NULL);
	DuLNode* p = pList->head->next;
	while (p!=pList->head&&p->data!=val)
	{
		p = p->next;
	}
	if (p == pList->head)
	{
		p = NULL;
	}
	return p;
}

4.插入操作

插入操作包括了,头插,尾插,其余位置插入

(1)其余位置插入:

如图示:看图插入操作理解起来就比较方便

//在ptr指针前进行插入操作
bool Insert_Prev(DuLinkList* pList, DuLNode* p, ElemType val)
{
	assert(pList != NULL);
	if (p == NULL)
	{
		return false;
	}
	DuLNode* s = (DuLNode*)malloc(sizeof(DuLNode));
	s->data = val;
	s->prev = p->prev;
	s->next = p;
	p->prev = s;
	s->prev->next = s;
	pList->cursize += 1;
	return true;
}

(2)头插操作:就是在第一个数据节点前插入一个新节点。

//头插
void Push_Front(DuLinkList* pList, ElemType val)
{
	assert(pList != NULL);
	Insert_Prev(pList, pList->head->next, val);
}

(3)尾插操作:尾插就是在头节点之前插入,反转过来相当于尾插。

//尾插
void Push_Back(DuLinkList* pList, ElemType val)
{
	assert(pList != NULL);
	Insert_Prev(pList, pList->head, val);
}

5.删除操作

删除操作包括头删,尾删,其余位置删除

(1)其余位置删除:其实删除操作非常简单,就是将要删除结点的前驱指针和后继指针的指向改变,结点的前一个结点指向后一个结点,将这个将要删除的结点绕过,然后free掉空间,数据节点总数减1.

//删除操作
Status Erase(DuLinkList* pList, DuLNode* ptr)
{
	assert(pList != NULL);
	if ( NULL== ptr)
	{
		return NULLPTR;
	}
	ptr->prev->next = ptr->next;
	ptr->next->prev = ptr->prev;
	free(ptr);
	pList->cursize -= 1;
	return OK;
}

(2)头删操作:在头节点之后的第一个数据结点

//头删
void Pop_Front(DuLinkList* pList)
{
	assert(pList != NULL);
	Erase(pList, pList->head->next);
}

(3)尾删操作:在头节点之前的结点

//尾删
void Pop_Back(DuLinkList* pList)
{
	assert(pList != NULL);
	Erase(pList, pList->head->prev);
}

6.清空与销毁函数

(1)清空:判断链表是否是一个空链表,在利用循环进行头删,也就是清空链表中的每一个结点数据

//清空函数
void ClearDuList(DuLinkList* pList)
{
	assert(pList != NULL);
	while (!IsEmpty(pList))
	{
		Pop_Front(pList);
	}
}

(2)销毁:利用清空函数,只不过销毁是将整个链表,包括头节点也清空的函数

//摧毁函数
void DestroyDuList(DuLinkList* pList)
{
	assert(pList != NULL);
	ClearDuList(pList);
	free(pList->head);
	pList->head = NULL;
}

三·双链表的总结

(1)双链表有双指针域:前驱指针和后继指针,方便插入删除

(2)双链表的查询操作复杂度高

(3)动态链表在堆上申请空间

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值