小猪的数据结构辅助教程——2.7 线性表中的双向循环链表

小猪的数据结构辅助教程——2.7 线性表中的双向循环链表

标签(空格分隔): 数据结构


本节学习路线图与学习要点

学习要点

1.了解引入双向循环链表的原因
2.熟悉双向循环链表的特点以及存储结构
3.掌握双向循环链表的一些基本操作的实现逻辑
4.掌握逆序输出双向循环链表元素逻辑


1.双向循环链表的引入


2.双向循环链表的存储结构

双向循环链表的特点

上面也说了,空间换时间,比起循环链表只是多了一个指向前驱的指针
特点的话:
判断空表:L ->next = L -> prior = L;

存储结构

typedef struct LNode
{
	ElemType data;         //数据域
	struct LNode *prior;   //前驱指针 
	struct LNode *next;   //后继指针
	 
}LNode;  
typedef struct LNode *LinkList; 

双向循环链表的结构图


3.相关基本操作的代码实现

1)构建空表

Status InitList(LinkList L)
{
	L = (LinkList)malloc(sizeof(LNode));
	if(!L)exit(ERROR);
	else L ->next = L ->prior = L;
	return OK;
} 

逻辑解析

很简单,就是头结点自己指自己而已~


2)将表置空

void ClearList(LinkList L)
{
	LinkList p = L ->next;   //指向第一个结点 
	while(p != L)
	{
		p = p ->next;  //指向下一个结点 
		free(p->prior); //释放该结点的前驱结点 
	}
	L ->next = L ->prior = L; //自己指自己 
}

3)判断是否为空表

Status ListEmpty(LinkList L)
{
	return L->next == L && L ->prior == L?TRUE:FALSE;
}

4)销毁表

void DestoryList(LinkList L)
{
	ClearList(L);
	free(L);
	L = NULL;
}

5)获得表长度

int ListLength(LinkList L)
{
	int i = 0;
	LinkList p = L ->next;
	while(p != L)
	{
		i++;
		p = p ->next;
	}
	return i;
} 

6)获得表中第i个元素的值

Status GetElem(LinkList L,int i,ElemType *e)
{
	int j = 1;
	LinkList p = L ->next;  //指向第一个结点
	while(p != L && j < i)   //指针后移 
	{
		j++;
		p = p ->next;
	} 
	if(p == L || j > i)return ERROR;  //找不到该元素
	e = p ->data;
	return OK; 	
}

7)查找表中是否存在满足条件的元素

int LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType)) 
{
    int i = 0;
    LinkList p = L ->next ->next;  //指向第一个结点
    while(p != L ->next)
    {
        i++;
        if(compare(p->data,e))return i;
        p = p ->next; 
    } 
    return 0;   //找不到,返回0 
}

8)获得某个节点的直接前驱

Status BeforeElem(LinkList L,ElemType choose,ElemType *before)
{
	LinkList p = L ->next ->next;  //指向第二个结点
	while(p != L)   //未指向头结点
	{
		if(p ->data == choose)
		{
			before = p ->prior ->data; 
			return OK;
		}
		p = p ->next;
	} 
	return ERROR;
}

9)获得某个节点的直接后继

Status NextElem(LinkList L,ElemType choose,ElemType *behind)
{
	LinkList p = L ->next ->next; //指向第二个结点
	while(p != L)
	{
		if(p ->prior ->data == choose)
		{
			behind = p ->data;
			return OK;
		}
		p = p ->next;
	} 
	return ERROR;
}

10)返回第i个元素的地址

LinkList GetElemAdd(LinkList L,int i)
{
	int j;
	LinkList p = L;
	if(i < 0 || i > ListLength(L))return NULL; //判断i值位置是否合法
	for(j = 1;j < = i;j++)
	{
		p = p ->next;
	} 
	return p;
}   

11)往第i个位置插入元素

Status ListInsert(LinkList L,int i,ElemType e)
{
	LinkList p,q;
	//判断i值是否合法
	if(i < 1 || i > ListLength(L) + 1)return ERROR;
	p =  GetElemAdd(L,i - 1);
	//NULL的话说明,第i个结点的前驱不存在,
	//这里假设头节点为第一个结点的前驱 	 
	if(!p)return ERROR; 
	q = (LinkList)malloc(sizeof(LNode));
	if(!q)return ERROR;
	q ->data = e;  //给新结点赋值
	q ->prior = p;  //新结点的前驱为第i - 1个结点
	q ->next = p ->next; //新结点的后记为第i个结点
	p ->next ->prior = q; //第i个结点前驱指向新结点 
	p ->next = q;  //第i-1个结点的后继指向新结点 
	return OK; 
}

实现逻辑图

12)删除第i个位置的元素

Status ListDelete(LinkList L,int i,ElemType *e)
{
	LinkList p;
	if(i < 1)return ERROR; //判断删除位置是否合法
	p = GetElemAdd(L,i);
	if(!p)return ERROR;  //为NULL说明第i个元素不存在
	e = p ->data;
	p ->prior ->next = p ->next; //i-1个结点的后继指向滴i+1个结点
	p ->next ->prior = p ->prior; //第i+1个结点的前驱指向第i - 1个结点
	free(p); //释放第i个结点
	return OK; 
}

实现逻辑图

嘿嘿,是不是觉得少了个遍历表中元素的基本操作呢,别急,我们下面写个例子,
按正序遍历链表,以及逆序来遍历表中的所有元素~


4.简单例子:正序和逆序遍历表中元素

运行截图

代码实现

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

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int ElemType;  
typedef int Status;
typedef struct LNode
{
	ElemType data;         //数据域
	struct LNode *prior;   //前驱指针 
	struct LNode *next;   //后继指针
}LNode;  
typedef struct LNode *LinkList;

//定义一个创建N个结点的方法
 LinkList ListCreate(int N)
 {
 	LinkList p,q,head;
 	int i,data;
 	q = head;
 	head = (LinkList)malloc(sizeof(LNode));	
 	head ->prior = head;
 	head ->next = head;
 	p = head;
 	for(i = 0;i < N;i++)
 	{
	 	printf("请输入第%d个结点的值:",i + 1);
	 	scanf("%d",&data);
	 	q = (LinkList)malloc(sizeof(LNode));
	 	q ->data = data;
	 	p ->next = q;
	 	q ->prior = p;
		q ->next = head; 
		head ->prior = q; 
		p = q; 
 	}
 	return head;
 } 
 
 //定义一个打印结点数据的方法
 void PrintNode(ElemType e)
 {
 	printf("%d\t",e);
 } 
 
 //定义一个正序输出链表的方法
 void ListTraverse(LinkList L)
 {
 	LinkList p = L->next;  //指向首元结点
	while(p!=L)
	{
		PrintNode(p->data);
		p = p ->next;
	} 
	printf("\n");
 } 
 
 //定义一个逆序输出链表的方法
 void ListTraverseBack(LinkList L)
 {
 	LinkList p = L ->prior;  //指向最后一个结点 
	while(p!=L)
	{
		PrintNode(p->data);
		p = p ->prior;
	} 
	printf("\n");
 } 
 
 int main()
 {
 	LinkList p;
 	int N = 0;
 	printf("请输入双向链表的结点个数:");
 	scanf("%d", &N);
 	p = ListCreate(N);
 	printf("正序打印链表中的结点:\n");
 	ListTraverse(p);
 	printf("逆序打印链表中的结点:\n");
 	ListTraverseBack(p); 
 	return 0;
 }

很简单,就不BB了~


5.本节示例代码下载:

https://github.com/coder-pig/Data-structure-auxiliary-tutorial/blob/master/List/list5.c
https://github.com/coder-pig/Data-structure-auxiliary-tutorial/blob/master/List/list6.c

  • 3
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值