线性表【双向链表基本定义与操作】(带头结点)

1.为什么要有双向链表

1.单向链表查找某结点的缺陷

单链表的结点————有指示后继的指针域————找后继结点方便;

即:查找某结点的后继结点的执行时间为 O(1)。

————无指示前驱的指针域————找前驱结点难:从表头出发查找。

即:查找某结点的前驱结点的执行时间为O(n)。

为了解决找前驱结点难的问题,我们就有了双向链表

2.双向链表

1.定义:在单链表的每个结点里再增加一个指向其直接前驱的指针域 prior ,这样链表中就形成了有两个方向不同的链,故称为双向链表。

在这里插入图片描述

2.双向链表的对称性(设指针 p指向某一结点)

在这里插入图片描述
该结点前驱元素的next域指向自己后继元素的prior域指向自己
在这里插入图片描述
在双向链表中有些操作(如:ListLength、GetElem,ClearList,Destroy等),因仅涉及一个方向的指针,故它们的算法与单链表的相同。在插入、删除时,则需同时修改两个方向上的指针,两者的操作的时间复杂度均为 O(n)。

3.了解双向循环链表

在这里插入图片描述

4.双向链表的相关操作

1.双向链表的结构定义

在这里插入图片描述

//1.双向链表的结构定义多一个指向前驱的指针域
typedef struct DNode
{
	ElemType data;// 数据域
	struct DNode* prior, * next; //前驱和后继指针
}DNode,*DLinkList;
//DNode * 用来定义结点指针
//DLinkList  用来定义链表指针

2.双向链表的初始化

InitDLinkList(DLinkList *L)
1.头结点的特征:
  • 头结点的proir 域永远指向NULL
  • 由于头结点之后暂时没有结点,故初始时头结点next域指向NULL
    在这里插入图片描述
2.算法步骤:

注意:需要改变头指针指向,故传入二级指针

(1)初始化头结点内存空间
(2)初始化头结点两个指针域指向NULL

//2.初始化双向链表
bool InitDLinkList(DLinkList *L)
{
	//[1]初始化头结点内存空间
	*L = (DNode*)malloc(sizeof(DNode));
	
	if((*L)==NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	(*L)->data = 0;//头结点数据域同样可以不存数据,因人而异。

	//[2]初始化头结点两个指针域指向NULL
	(*L)->next = NULL;
	(*L)->prior = NULL;
	return true;
}

3.判断双向链表是否为空

DLinkListEmpty(DLinkList L)
1.算法步骤:

(0)检查头指针是否为NULL,如果是,则链表未初始化,可以认为是空
(1)检查头结点的next指针是否为NULL,如果是,则链表为空

这里不需要检查头结点的prior域,因为头结点的prior域与尾结点的next域一定指向NULL

//3.判断双链表是否为空
bool DLinkListEmpty(DLinkList L)
{
	// [0] 检查头指针是否为NULL,如果是,则链表未初始化,可以认为是空
	if (L == NULL)
	{
		return true; // 链表未初始化,可以认为是空
	}

	// [1] 检查头结点的next指针是否为NULL,如果是,则链表为空
	if (L->next == NULL)
	{
		return true; // 链表为空,因为头结点的后继是NULL
	}
	else
	{
		return false; // 链表不为空,因为头结点的后继不是NULL
	}
		
}

4.双向链表的按位插入(十分重要)

InsertDLinkList(DLinkList* L, int i, ElemType e)
1.1插入的核心思路:覆盖指针前,要注意后续会不会再用到这个指针,若后续会用到,则当前思路就不对,需另寻它法

假设在p结点p->next结点中插入一个新结点s
这里的p结点相当于指向待插入结点的前一个结点

  • (1)首先解决新结点的前驱的指向
  • (2)其次解决新结点的后继的指向
  • (3)然后解决p->next结点的前驱指向
  • (4)最后再解决p结点的后继指向
  <1>s->prior=p;//改变新结点前驱指向
  <2>s->next=p->next;//改变新结点后继指向
  <3>p->next->prior=s;//改变p->next结点前驱指向
  <4>p->next=s;//改变p结点后继指向

注意:(1)(2)(3)步可以随便互换顺序,
但是最关键的一点是 (4)步必须在以上三步的后面
因为(2)(3)步 都要用到原来的p结点的后继指向
所以 不能先改变p结点的后继指向,这样会出现插入错误

1.2.正常情况下的插入图解:

在这里插入图片描述

2.1在表尾插入的特殊情况

也就是 p->next == NULL
分为两种情况:

  • 【1】该双向链表为空表的情况
  • 【2】该双向链表有结点,但是插入到尾结点之后的情况

由于待插入位置后面没有结点,所以与正常的插入操作相比只是省略p->next结点的prior域前驱指向的修改(因为根本不存在这个结点);
同时由于待插入结点应该是新的尾结点,所以其next域一定指向NULL

<1>s->prior=p;//改变新结点前驱指向
<2>s->next=NULL;//改变新结点后继指向
<4>p->next=s;//改变p结点后继指向
2.2头结点之后插入(空表)图解

在这里插入图片描述

2.3.尾结点之后插入图解

在这里插入图片描述
经过画图分析,我们可以明显感觉到头结点之后插入(空表)这种情况就是特殊的在表尾插入

3.算法步骤:

(0)错误处理:头结点不存在 或 i小于1
(1)初始化临时指针p指向头结点(方便正常情况下在表头插入)
(2)初始化计数器为0
(3)循环查找第i-1个结点的位置
(4)异常判断(防止插入位置大于链表长度加1)
(5)建立并初始化插入结点
(6)插入新结点(表尾插入的情况需特殊讨论)

//5.双向链表的按位插入操作
bool InsertDLinkList(DLinkList L, int i, ElemType e)
{
	//[0]错误处理:头结点不存在 或 i小于1
	if (L == NULL||i<1)
	{
		return false;
	}

	//[1]初始化临时指针p指向头结点
	DNode* p = L;

	//[2]初始化计数器为0
	int j = 0;

	//[3]循环查找第i-1个结点的位置
	//p!=NULL:
	    //1.保证头结点不存在时,不进入循环
 	    //2.保证i大于表长加1时, p指向尾结点的next域NULL,终止循环(此时待插入位置是尾结点 后面的 后面, 该位置是错误的)
	//j<i-1:
	    //正常情况下,找到第i-1个结点
	while (p!= NULL && j < i-1)
	{
		p = p->next;
		j++;
	}

	//[4]异常判断
	//p==NULL:
	    //1.头结点不存在的情况
		//2.i大于表长加1的情况(该插入位置错误)
	if (p == NULL)
	{
		return false;
	}

	//[5]建立并初始化插入结点
	DNode* s = (DNode*)malloc(sizeof(DNode));
	
	if(s==NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	s->data = e;
	
	
	//[6]解决待插入结点在表尾的情况
	   //p->next == NULL:
	        //<1>只有头结点(空表)时,在表中插入一个元素
	        //<2>表中有多个结点时,在表尾插入一个结点
	if  (p->next == NULL )
	{
		//<1>先解决待插入结点的前驱后继指向
		//但是这里注意:1.在表尾插入时,不管是否非空,待插入结点的next都指向NULL;
		             //2.在表尾插入时,不管是否非空,都不存在p->next结点,所以不能更改p->next结点的前驱指向
		s->prior = p;
		s->next = NULL;
		p->next = s;
	}
	//[7]核心:正常情况的插入:
	else
	{
		
	    //<1>先解决待插入结点的前驱后继指向
		s->prior = p;
		s->next = p->next;
		//<2>再解决第i个结点的前驱指向
		p->next->prior = s;
		//<3>最后解决第i-1个结点的后继指向
		p->next = s;
	}
	return true;
}

5. 双向链表的按位删除(十分重要)

DeleteDLinkList(DLinkList L, int i, ElemType* e)
1.1删除的核心思路:

假设删除p结点,即待删除结点

  • (1)首先解决 待删除结点 前驱结点的 后继指向
  • (2)其次解决 待删除结点 后继结点的 前驱指向
  • 注意:两者的顺序可以更换
1.2正常情况下的删除图解:

在这里插入图片描述

2.1删除尾结点的特殊情况
  • 此时只需要改变 待删除结点 前驱结点的 后继指向 NULL即可
p->prior->next = NULL;
2.2删除尾结点图解

在这里插入图片描述

3.算法步骤:

(0)错误处理:头结点不存在 或 i小于1
(1)初始化临时指针p指向首元结点
(2)初始化计数器为1
(3)循环查找i个结点的位置
(4)异常判断(防止删除位置大于链表长度)
(5)核心:删除结点(尾结点删除的情况需要特殊讨论)

//6.双向链表的按位删除操作
bool DeleteDLinkList(DLinkList L, int i, ElemType* e)
{
	//[0]错误处理:头结点不存在 或 i小于1
	if (L == NULL || i < 1)
	{
		return false;
	}

	//[1]初始化临时指针p指向首元结点
	DNode* p = L->next;

	//[2]初始化计数器为1
	int j = 1;

	//[3]循环查找i个结点的位置
	//p->next!=NULL:
		//1.保证头结点不存在时,不进入循环
		//2.保证i大于表长时,p指向尾结点next域,终止循环(比如:i=表长加1:此时待删除位置是尾结点 的后面 该位置是不存在元素)
	//j<i-1:
		//正常情况下,找到第i-1个结点
	while (p!= NULL && j < i)
	{
		p = p->next;
		j++;
	}

	//[4]异常判断
	//p==NULL:
		//1.首元结点不存在的情况(空表)
		//2.i大于表长的情况(该删除位置没有元素)
	if (p== NULL)
	{
		return false;
	}

	//[5]核心:删除结点
	*e = p->data;//保存待删除数据
	//<1>删除尾结点时,只需要改变尾结点next域指向NULL即可
	if (p->next == NULL)
	{
		p->prior->next = NULL;
	}
	//<2>删除非尾结点时,需要改变待删除结点前驱结点的next域  和  待删除结点后继结点的prior域
	else
	{
		p->prior->next = p->next;
		p->next->prior = p->prior;
	}
	
	free(p);//释放待删除结点空间
	return true;
}

6.头插法建立双向链表

 CreateDLinkList_H(DLinkList* L, int n)
1.1插入分析(第一次插入):

在这里插入图片描述

1.2插入分析(之后正常插入):

在这里插入图片描述

2.算法步骤:

(1)建立链表头结点并初始化next与prior域
(2)循环插入新结点到表头
<1>初始化新结点>
<2>改变指针指向(与按位插入的操作大同小异)
<2.1>p插入到空表表头
<2.2>p插入到非空表表头

//6.双向链表的头插法建立整表
bool CreateDLinkList_H(DLinkList* L, int n)
{
	//[1]建立链表头结点并初始化next与prior域
	*L = (DLinkList)malloc(sizeof(DNode));
	
	if(*L==NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}
	
	(*L)->next = NULL;
	(*L)->prior = NULL;

	//[2]循环插入新结点到表头
	DNode* p;
	for (int i = 0; i < n; i++)
	{
		//<1>初始化新结点
		p = (DNode*)malloc(sizeof(DNode));
		if (p == NULL)
		{
			printf("内存分配失败!\n");
			return false;
		}

		scanf_s("%d", &p->data);

		//<2>改变指针指向(与按位插入的操作大同小异)
		//<2.1>p插入到空表表头
		if ((*L)->next == NULL)
		{
			p->prior = *L;
			p->next = NULL;

			(*L)->next = p;
		}
		//<2.2>p插入到非空表表头
		else
		{
			p->prior = *L;
			p->next = (*L)->next;

			(*L)->next->prior = p;
			(*L)->next = p;
		}
		
	}
	return true;
}

7.尾插法建立双向链表

CreateDLinkList_T(DLinkList* L, int n)
1.插入分析:

由于尾指针永远更新指向最新的尾结点,故后续的插入操作与第一次插入操作完全一致
在这里插入图片描述

2.算法步骤:

(1)建立链表头结点并初始化next与prior域
(2)初始化尾指针,指向链表的头结点
(3)循环插入新结点到表尾
<1> 初始化新结点
<2> 改变指针指向 (插入到尾部)
<2.1>先解决新结点的next与prior域
<2.2> 再解决当前尾结点的next域 并 更新尾指针指向

//7.双向链表的尾插法建立整表
bool CreateDLinkList_T(DLinkList* L, int n)
{
	// [1] 建立链表头结点并初始化next与prior域
	*L = (DLinkList)malloc(sizeof(DNode));

	if (*L == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	(*L)->next = NULL;
	(*L)->prior = NULL;

	// [2] 初始化尾指针,指向链表的头结点
	DNode* tail = *L;

	// [3] 循环插入新结点到表尾
	DNode* p;
	for (int i = 0; i < n; i++)
	{
		// <1> 初始化新结点
		p = (DNode*)malloc(sizeof(DNode));
		if (p == NULL)
		{
			printf("内存分配失败!\n");
			return false;
		}

		scanf_s("%d", &p->data);

		// <2> 改变指针指向 (插入到尾部)
		//<2.1>先解决新结点的next与prior域
		p->prior = tail;       // 新结点的 prior 指向当前尾结点
		p->next = NULL;        // 新结点的 next 指向 NULL

		//<2.2> 再解决当前尾结点的next域 并 更新尾指针指向
		tail->next = p;        // 当前尾结点的 next 指向新结点
		tail = p;              // 尾指针更新为新结点
	}

	return true;
}

8. 从头遍历并输出整个双向链表(与单链表一致)

printDLinkList_H(DLinkList L)
1.算法步骤:

(0)错误处理: 头结点不存在
(1)初始化临时指针p指向首元结点
(2)错误处理: 首元结点不存在 (空表)
(3)循环遍历链表直到尾结点

// 8. 从头遍历并输出整个双向链表
bool printDLinkList_H(DLinkList L)
{
	// [0] 错误处理: 头结点不存在
	if (L == NULL)
	{
		printf("NULL DList!\n");
		return false;
	}

	// [1] 初始化临时指针p指向首元结点
	DNode* p = L->next;

	// [2] 错误处理: 首元结点不存在 (空表)
	if (p == NULL)
	{
		printf("NULL DList!\n");
		return false;
	}

	// [3] 循环遍历链表直到尾结点
	while (p != NULL)
	{
		// 输出当前节点数据
		printf("%d-->", p->data);
		// p指向下一个结点
		p = p->next;
	}

	// [4] 输出结束标志
	printf("end\n");
	return true;
}

9.从尾遍历并输出整个双向链表

printDLinkList_T(DLinkList L)
1.算法步骤:

(0)错误处理: 头结点不存在
(1)初始化临时指针p指向首元结点
(2)错误处理: 首元结点不存在 (空表)
(3)循环遍历链表直到尾结点(核心)
(4)从尾结点向头遍历链表直到头结点(核心)

// 9. 从尾遍历并输出整个双向链表
bool printDLinkList_T(DLinkList L)
{
	// [0] 错误处理: 头结点不存在
	if (L == NULL)
	{
		printf("NULL DList!\n");
		return false;
	}

	// [1] 初始化临时指针p指向首元结点
	DNode* p = L->next;

	// [2] 错误处理: 首元结点不存在 (空表)
	if (p == NULL)
	{
		printf("NULL DList!\n");
		return false;
	}

	// [3] 循环遍历链表直到尾结点
	while (p->next != NULL)
	{
		// p指向下一个结点
		p = p->next;
	}

	// [4] 从尾结点向头遍历链表直到头结点
	//因为不输出头结点的数据,故终止条件为p->prior != NULL
	while (p->prior != NULL)
	{
		// 输出当前结点数据
		printf("%d-->", p->data);
		// p指向前一个结点
		p = p->prior;
	}

	// [5] 输出结束标志
	printf("end\n");
	return true;
}

10.基本操作如下:

#include<stdio.h>
#include<stdlib.h>

#define bool int
#define true 1
#define false 0

typedef int ElemType;

//1.双向链表的结构定义多一个指向前驱的指针域
typedef struct DNode
{
	ElemType data;// 数据域
	struct DNode* prior, * next; //前驱和后继指针
}DNode,* DLinkList;
//DNode * 用来定义结点指针
//DLinkList  用来定义链表指针




//2.初始化双向链表
bool InitDLinkList(DLinkList *L)
{
	//[1]初始化头结点内存空间
	*L = (DNode*)malloc(sizeof(DNode));
	
	if((*L)==NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	(*L)->data = 0;//头结点数据域同样可以不存数据,因人而异。

	//[2]初始化头结点两个指针域指向NULL
	(*L)->next = NULL;
	(*L)->prior = NULL;
	return true;
}


//3.判断双链表是否为空
bool DLinkListEmpty(DLinkList L)
{
	// [0] 检查头指针是否为NULL,如果是,则链表未初始化,可以认为是空
	if (L == NULL)
	{
		return true; // 链表未初始化,可以认为是空
	}

	// [1] 检查头结点的next指针是否为NULL,如果是,则链表为空
	if (L->next == NULL)
	{
		return true; // 链表为空,因为头结点的后继是NULL
	}
	else
	{
		return false; // 链表不为空,因为头结点的后继不是NULL
	}
		
}



//4.双向链表的按位插入操作
bool InsertDLinkList(DLinkList L, int i, ElemType e)
{
	//[0]错误处理:头结点不存在 或 i小于1
	if (L == NULL||i<1)
	{
		return false;
	}

	//[1]初始化临时指针p指向头结点
	DNode* p = L;

	//[2]初始化计数器为0
	int j = 0;

	//[3]循环查找第i-1个结点的位置
	//p!=NULL:
	    //1.保证头结点不存在时,不进入循环
 	    //2.保证i大于表长加1时, p指向尾结点的next域NULL,终止循环(此时待插入位置是尾结点 后面的 后面, 该位置是错误的)
	//j<i-1:
	    //正常情况下,找到第i-1个结点
	while (p!= NULL && j < i-1)
	{
		p = p->next;
		j++;
	}

	//[4]异常判断
	//p==NULL:
	    //1.头结点不存在的情况
		//2.i大于表长加1的情况(该插入位置错误)
	if (p == NULL)
	{
		return false;
	}

	//[5]建立并初始化插入结点
	DNode* s = (DNode*)malloc(sizeof(DNode));
	
	if(s==NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	s->data = e;
	
	
	//[6]解决待插入结点在表尾的情况
	   //p->next == NULL:
	        //<1>只有头结点(空表)时,在表中插入一个元素
	        //<2>表中有多个结点时,在表尾插入一个结点
	if  (p->next == NULL )
	{
		//<1>先解决待插入结点的前驱后继指向
		//但是这里注意:1.在表尾插入时,不管是否非空,待插入结点的next都指向NULL;
		             //2.在表尾插入时,不管是否非空,都不存在p->next结点,所以不能更改p->next结点的前驱指向
		s->prior = p;
		s->next = NULL;
		p->next = s;
	}
	//[7]核心:正常情况的插入:
	else
	{
		
	    //<1>先解决待插入结点的前驱后继指向
		s->prior = p;
		s->next = p->next;
		//<2>再解决第i个结点的前驱指向
		p->next->prior = s;
		//<3>最后解决第i-1个结点的后继指向
		p->next = s;
	}
	return true;
}





//5.双向链表的按位删除操作
bool DeleteDLinkList(DLinkList L, int i, ElemType* e)
{
	//[0]错误处理:头结点不存在 或 i小于1
	if (L == NULL || i < 1)
	{
		return false;
	}

	//[1]初始化临时指针p指向首元结点
	DNode* p = L->next;

	//[2]初始化计数器为1
	int j = 1;

	//[3]循环查找i个结点的位置
	//p->next!=NULL:
		//1.保证头结点不存在时,不进入循环
		//2.保证i大于表长时,p指向尾结点next域,终止循环(比如:i=表长加1:此时待删除位置是尾结点 的后面 该位置是不存在元素)
	//j<i-1:
		//正常情况下,找到第i-1个结点
	while (p!= NULL && j < i)
	{
		p = p->next;
		j++;
	}

	//[4]异常判断
	//p==NULL:
		//1.首元结点不存在的情况(空表)
		//2.i大于表长的情况(该删除位置没有元素)
	if (p== NULL)
	{
		return false;
	}

	//[5]核心:删除结点
	*e = p->data;//保存待删除数据
	//<1>删除尾结点时,只需要改变尾结点next域指向NULL即可
	if (p->next == NULL)
	{
		p->prior->next = NULL;
	}
	//<2>删除非尾结点时,需要改变待删除结点前驱结点的next域  和  待删除结点后继结点的prior域
	else
	{
		p->prior->next = p->next;
		p->next->prior = p->prior;
	}
	
	free(p);//释放待删除结点空间
	return true;
}


//6.双向链表的头插法建立整表
bool CreateDLinkList_H(DLinkList* L, int n)
{
	//[1]建立链表头结点并初始化next与prior域
	*L = (DLinkList)malloc(sizeof(DNode));
	
	if(*L==NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}
	
	(*L)->next = NULL;
	(*L)->prior = NULL;

	//[2]循环插入新结点到表头
	DNode* p;
	for (int i = 0; i < n; i++)
	{
		//<1>初始化新结点
		p = (DNode*)malloc(sizeof(DNode));
		if (p == NULL)
		{
			printf("内存分配失败!\n");
			return false;
		}

		scanf_s("%d", &p->data);

		//<2>改变指针指向(与按位插入的操作大同小异)
		//<2.1>p插入到空表表头
		if ((*L)->next == NULL)
		{
			p->prior = *L;
			p->next = NULL;

			(*L)->next = p;
		}
		//<2.2>p插入到非空表表头
		else
		{
			p->prior = *L;
			p->next = (*L)->next;

			(*L)->next->prior = p;
			(*L)->next = p;
		}
		
	}
	return true;
}


//7.双向链表的尾插法建立整表
bool CreateDLinkList_T(DLinkList* L, int n)
{
	// [1] 建立链表头结点并初始化next与prior域
	*L = (DLinkList)malloc(sizeof(DNode));

	if (*L == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	(*L)->next = NULL;
	(*L)->prior = NULL;

	// [2] 初始化尾指针,指向链表的头结点
	DNode* tail = *L;

	// [3] 循环插入新结点到表尾
	DNode* p;
	for (int i = 0; i < n; i++)
	{
		// <1> 初始化新结点
		p = (DNode*)malloc(sizeof(DNode));
		if (p == NULL)
		{
			printf("内存分配失败!\n");
			return false;
		}

		scanf_s("%d", &p->data);

		// <2> 改变指针指向 (插入到尾部)
		//<2.1>先解决新结点的next与prior域
		p->prior = tail;       // 新结点的 prior 指向当前尾结点
		p->next = NULL;        // 新结点的 next 指向 NULL

		//<2.2> 再解决当前尾结点的next域 并 更新尾指针指向
		tail->next = p;        // 当前尾结点的 next 指向新结点
		tail = p;              // 尾指针更新为新结点
	}

	return true;
}




// 8. 从头遍历并输出整个双向链表
bool printDLinkList_H(DLinkList L)
{
	// [0] 错误处理: 头结点不存在
	if (L == NULL)
	{
		printf("NULL DList!\n");
		return false;
	}

	// [1] 初始化临时指针p指向首元结点
	DNode* p = L->next;

	// [2] 错误处理: 首元结点不存在 (空表)
	if (p == NULL)
	{
		printf("NULL DList!\n");
		return false;
	}

	// [3] 循环遍历链表直到尾结点
	while (p != NULL)
	{
		// 输出当前节点数据
		printf("%d-->", p->data);
		// p指向下一个结点
		p = p->next;
	}

	// [4] 输出结束标志
	printf("end\n");
	return true;
}

// 9. 从尾遍历并输出整个双向链表
bool printDLinkList_T(DLinkList L)
{
	// [0] 错误处理: 头结点不存在
	if (L == NULL)
	{
		printf("NULL DList!\n");
		return false;
	}

	// [1] 初始化临时指针p指向首元结点
	DNode* p = L->next;

	// [2] 错误处理: 首元结点不存在 (空表)
	if (p == NULL)
	{
		printf("NULL DList!\n");
		return false;
	}

	// [3] 循环遍历链表直到尾结点
	while (p->next != NULL)
	{
		// p指向下一个结点
		p = p->next;
	}

	// [4] 从尾结点向头遍历链表直到头结点
	//因为不输出头结点的数据,故终止条件为p->prior != NULL
	while (p->prior != NULL)
	{
		// 输出当前结点数据
		printf("%d-->", p->data);
		// p指向前一个结点
		p = p->prior;
	}

	// [5] 输出结束标志
	printf("end\n");
	return true;
}

int main()
{

	DLinkList L1;
	DLinkList L2;
	printf("头插法建立双向链表L1:\n");
	CreateDLinkList_H(&L1, 6);
	printf("尾插法建立双向链表L2:\n");
	CreateDLinkList_T(&L2, 6);
	printf("从头打印双向链表L1:\n");
	printDLinkList_H(L1);
	printf("从尾打印双向链表L1:\n");
	printDLinkList_T(L1);
	printf("\n");

	printf("从头打印双向链表L2:\n");
	printDLinkList_H(L2);

	printf("向L2中插入部分元素!\n");
	printf("表尾插入:\n");
	InsertDLinkList(L2, 7, 3);
	printDLinkList_H(L2);

	printf("表头插入:\n");
	InsertDLinkList(L2, 1, 33);
	printDLinkList_H(L2);

	printf("删除L2中部分元素:\n");
	ElemType e1;
	printf("删除表头元素!\n");
	DeleteDLinkList(L2, 1,&e1);
	printDLinkList_H(L2);


	ElemType e2;
	printf("删除表尾元素!\n");
	DeleteDLinkList(L2, 7,&e2);
	printDLinkList_H(L2);



	if (DLinkListEmpty(L2))
	{
		printf("L2为空表!\n");
	}
	else
	{
		printf("L2不为空表!\n");
	}

	return 0;
}

在这里插入图片描述

  • 21
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值