线性表的链式存储(双向链表)

本文介绍了双向链表的概念,包括其结构特点和与单链表的区别。详细阐述了双向链表的基本操作实现,如初始化、创建、查找直接前后继以及插入和删除结点,旨在帮助读者理解和掌握这一数据结构的使用。
摘要由CSDN通过智能技术生成


前言

  T_T此专栏用于记录数据结构及算法的(痛苦)学习历程,便于日后复习(这种事情不要啊)。所用教材为《数据结构 C语言版 第2版》严蔚敏。有关线性表的顺序存储见线性表概念及顺序表的实现,有关单链表见线性表的链式存储(单链表)


一、双向链表是什么?

  双向链表:在链表的结点中有两个指针域,一个指向直接后继,另一个指向直接前驱。如下图所示(图源亿速云)。对于头结点,无直接前驱结点,故prev为NULL;对于最后一个结点,无直接后继结点,故next为NULL。

二、双向循环链表的意义

  以上讨论的链式存储结构的结点中只有一个指示直接后继的指针域,由此,从某个结点出发只能顺指针向后寻查其他结点。若要寻查结点的直接前驱,则必须从表头指针出发。换句话说,在单链表中,查找直接后继结点的执行时间为 0(1), 而查找直接前驱的执行时间为O(n)。为克服单链表这种单向性的缺点,可利用双向链表 (Double Linked List)。
  下面给出双向链表结点结构定义:

//- - - - -双向链表的存储结构-----
typedef struct DuLNode 
ElemType data;   //数据域
struct DuLNode *prior;   //直接前驱
struct DuLNode *next;   //直接后继
}DuLNode,*DuLinkList; 

三、双向链表基本操作的实现

  双向链表的操作与单链表操作基本相同,除了初始化头指针、创建链表、插入结点和删除结点。因此重点关注以上四项的操作即可。有关单链表见线性表的链式存储(单链表)
  下面给出双向链表基本操作的实现:

#include <iostream>
using namespace std;

#define OK 1
#define fail 0
#define overflow -1
typedef int Status;
typedef int Elemtype;
typedef struct DuLnode {
	Elemtype data;
	DuLnode * prir;
	DuLnode * next;
}DuLnode, * DuLlist;

DuLlist L;
Elemtype e;

//初始化双向链表,创建头指针和头结点
Status InitDuList(DuLlist& L)
{
	L = new DuLnode;   //头指针
	if (!L)return fail;   //如果创建失败
	L->data = 123456;  //头结点
	L->next = NULL;
	L->prir = NULL;
	return OK;
}
//创建双向链表,头插法,创建i个结点
//先创建的后访问
Status CreatDuList_Head(DuLlist L, int i)
{
	if (i < 1)return fail;  //至少创建1个结点
	while (i--)
	{
		DuLnode* p = new DuLnode;
		cin >> p->data;
		L->next->prir = p;
		p->prir = L;
		p->next = L->next;
		L->next = p;
	}
	return OK;
}
//创建双向链表,尾插法,创建i个结点
//先创建的先访问
Status CreatDuList_End(DuLlist L, int i)
{
	DuLlist p = L;
	if (i < 1)return fail;   //至少创建1个结点
	while (i--)
	{
		DuLnode* q = new DuLnode;
		cin >> q->data;
		q->prir = p;
		q->next = NULL;
		p->next = q;
		p = q;
	}
	return OK;
}
//得到第i个结点的值,注意首元结点开始算第一个结点
//e为得到的结点数据值
Status GetElem(DuLlist L, int i, Elemtype* e)
{
	DuLlist p = L;
	if (i < 1 || L->next == NULL)return overflow; //如果i不合法或链表为空,则失败
	for (i; i > 0; i--)
	{
		if (p->next != NULL)
			p = p->next;
		else
			return overflow;
	}
	*e = p->data;
	return OK;
}
//已知地址为p的结点,查找其直接前驱存储的数据,注意首元结点开始算第一个结点
//e为得到的结点数据值
Status GetElemPrir(DuLlist L, DuLnode *q, Elemtype* e)
{
	if ( q->prir == L)return overflow; //如果只有一个结点,则失败
	*e = q->prir->data;
	return OK;
}
//查找某个值在链表中的位置,注意首元结点开始算第一个结点
//e为要查找的数据值,ElemPosition为在链表中的位置
DuLnode* LocatElem(DuLlist L, Elemtype e, int& ElemPosition)
{
	ElemPosition = 1;
	DuLlist p = L->next;
	while (p && p->data != e)
	{
		p = p->next;
		ElemPosition++;
	}
	return p;
}
//在第i个位置前插入一个结点,注意首元结点开始算第一个结点
//e插入的结点的数据值
Status InsertElem(DuLlist L, int i, Elemtype e)
{
	DuLlist p = L;
	if (i < 1)return fail;  //如果i不合法则失败
	while (p && i > 1)
	{
		p = p->next;
		i--;
	}
	if (!p || i != 1)return fail;  //如果链表不存在第i个位置,则失败
	DuLnode* q = new DuLnode;
	q->data = e;
	q->prir = p;
	q->next = p->next;
	p->next->prir = q;
	p->next = q;
	return OK;
}
//删除在第i个位置的结点,注意首元结点开始算第一个结点
Status DeleteElem(DuLlist L, int i)
{
	DuLlist p = L;
	if (i < 1)return fail;  //如果i不合法则失败
	while (p && i > 1)
	{
		p = p->next;
		i--;
	}
	if (!p || i != 1)return fail;  //如果链表不存在第i个位置,则失败
	DuLlist q = p->next;
	p->next = q->next;
	q->next->prir = p;
	delete q;
	q = NULL;
	return OK;
}
//销毁链表
Status DestoryDuList(DuLlist L)
{
	DuLlist p = L;
	while (p)
	{
		DuLlist q = p;
		p = p->next;
		delete q;
		q = NULL;
	}
	return OK;
}
int main()
{
	InitDuList(L);
	cout << L << endl;
	cout << L->data << endl;
	cout << L->next << endl;
	cout << L->prir << endl;
	CreatDuList_End(L, 5);
	cout << GetElemPrir(L,L->next->next,&e) << endl;
	cout << e << endl;
	cout << InsertElem(L, 3, 6) << endl;
	cout << GetElemPrir(L, L->next->next->next, &e) << endl;
	cout << e << endl;
	cout << GetElem(L, 3, &e) << endl;
	cout << e << endl;

	return 0;
}

  测试现象:
在这里插入图片描述


总结

  路漫漫其修远兮,吾将上下而摆烂。
  有任何疑问和补充,欢迎交流。(但我显然不会)

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
线性表是一种常用的数据结构,它是由一系列具有相同数据类型的元素组成的有限序列。线性表的链式表示是一种基于指针的表示方法,它通过指针将线性表中的元素连接起来。 链式表示的实现通常有两种方式:单链表双向链表。 1. 单链表链表是一种最简单的链式表示方式,它由一系列节点组成,每个节点包含两部分数据:数据域和指针域。其中,数据域存储节点的数据,指针域存储下一个节点的地址。 单链表的实现需要定义一个节点结构体,在结构体中定义数据域和指针域。我们可以使用指针变量来表示链表的头节点,通过遍历指针域来访问链表中的各个节点。 定义节点结构体: ```c typedef struct ListNode{ int data; struct ListNode *next; }ListNode; ``` 创建单链表: ```c ListNode* createList(int a[], int n){ ListNode* head = (ListNode*)malloc(sizeof(ListNode)); ListNode* p = head; for(int i = 0; i < n; i++){ ListNode* node = (ListNode*)malloc(sizeof(ListNode)); node->data = a[i]; node->next = NULL; p->next = node; p = node; } return head; } ``` 其中,a[] 是存储数据的数组,n 是数组长度。 遍历单链表: ```c void traverseList(ListNode* head){ ListNode* p = head->next; while(p){ printf("%d ", p->data); p = p->next; } printf("\n"); } ``` 2. 双向链表 双向链表是在单链表的基础上增加了一个指向前驱节点的指针域,它可以实现双向遍历,但是相对于单链表,它需要额外的空间开销。 定义节点结构体: ```c typedef struct DoubleListNode{ int data; struct DoubleListNode *prev; struct DoubleListNode *next; }DoubleListNode; ``` 创建双向链表: ```c DoubleListNode* createList(int a[], int n){ DoubleListNode* head = (DoubleListNode*)malloc(sizeof(DoubleListNode)); DoubleListNode* p = head; for(int i = 0; i < n; i++){ DoubleListNode* node = (DoubleListNode*)malloc(sizeof(DoubleListNode)); node->data = a[i]; node->prev = p; node->next = NULL; p->next = node; p = node; } return head; } ``` 遍历双向链表: ```c void traverseList(DoubleListNode* head){ DoubleListNode* p = head->next; while(p){ printf("%d ", p->data); p = p->next; } printf("\n"); } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值