循环双链表详解(C语言版)


前言

        之前学习的双链表结构,可以实现从表中任一结点出发找到链表中的其他结点,但是该结构如果要从头结点开始查找尾结点,那么就需要通过遍历查找尾结点,这种情况下双链表的效率较低,为了解决这个缺点,将其改进为循环双链表,以此来提高查找效率。

在这里插入图片描述

一、循环双链表的定义

        循环双链表是对双链表的改进,将尾结点与头结点建立连接,使得尾结点可以直接查找到头结点,头结点也能够直接查找到头结点,以此来提高查找的效率。

二、循环双链表的结构

结构图
在这里插入图片描述
代码描述

#define ElemType int
//循环双链表结点结构
typedef struct Node
{
	ElemType data;     //数据域
	struct Node *prio; //前驱结点指针域
	struct Node *next; //后继结点指针域
}Node, *PNode;

//双链表结点管理结构
typedef struct List
{
	PNode first; //指向首结点(首结点不存放数据)
	PNode last;  //指向尾结点
	int   size;  //记录有效结点数
}List;

三、循环双链表的常用操作

初始化循环双联链表

//初始化双链表
void InitDCList(List *list)
{
	//申请头结点空间
	Node *s = (Node *)malloc(sizeof(Node));
	assert(s != NULL);
	//将头结点连接入管理结点
	list->first = list->last = s;
	//设置前驱指针指向(指向头结点)
	list->first->prio = list->last;
	//设置后继结点指向(指向尾结点)
	list->last->next  = list->first;
	list->size = 0;
}

获取结点

//获取结点
Node* _buynode(ElemType x)
{
	Node *s = (Node *)malloc(sizeof(Node));
	assert(s != NULL);
	s->data = x;
	s->next = s->prio = NULL;
	return s;
}

尾插

void push_back(List *list, ElemType x)
{
	//获取结点
	Node *s = _buynode(x);
	//将插入结点与头结点连接
	s->next = list->last->next;
	s->next->prio = s;
	//将插入结点与上一尾结点连接
	s->prio = list->last;
	list->last->next = s;
	//修改双向链表管理结点的尾指针
	list->last = s;
	list->size++;
}

头插

//头插
void push_front(List *list, ElemType x)
{
	//获取插入结点
	Node *s = _buynode(x);
	//连接新插入结点和其下一个结点
	s->next = list->first->next;
	s->next->prio = s;
	//插入结点和头结点
	s->prio = list->first;
	list->first->next = s;
	//如果这个结点是第一个有效结点,需要更改管理结点尾指针的指向
	if(list->first == list->last)
		list->last = s;
	list->size++;
}

查看循环双链表数据

//查看循环双链表数据
void show_list(List *list)
{
	//指向第一个有效结点
	Node *p = list->first->next;
	//将所有有效结点输出
	while(p != list->first)
	{
		printf("%d-->",p->data);
		p = p->next;
	}
	printf("Nul.\n");
}

尾删

//尾删
void pop_back(List *list)
{   
	//如果没有有效结点,那么不进行删除
	if(list->size == 0)
		return;
	//设置管理结点的尾部指针指向
	Node *p = list->last;
	list->last = list->last->prio;
	//删除结点
	p->next->prio = p->prio;
	p->prio->next = p->next;
	free(p);
	list->size--;
}

头删

//头删
void pop_front(List *list)
{
	//如果没有有效结点不进行删除
	if(list->size == 0)
		return;
	//删除结点
	Node *p = list->first->next;
	p->next->prio = p->prio;
	p->prio->next = p->next;
	//如果只有一个有效结点,需要更改管理结点尾指针指向
	if(list->size == 1)
		list->last = list->first;
	list->size--;
}

按值插入(由小到大的顺序)

//按值插入(由小到大的顺序)
void insert_val(List *list, ElemType x)
{
	Node *p = list->first;//先指向头结点
	//查找要插入结点的位置
	while(p->next!=list->last && p->next->data < x)
		p = p->next;
	//如果插入的位置在最后,则进行尾插
	if(p->next==list->last && p->next->data<x)
	{
		push_back(list,x);
	}
	else //普通插入
	{
		Node *s = _buynode(x);
		s->next = p->next;
		s->next->prio = s;
		s->prio = p;
		p->next = s;
		list->size++;
	}
}

查找某一个值所在的位置

//查找某一个值所在的位置
Node* find(List *list, ElemType key)
{
	//从第一个有效结点位置开始查找
	Node *p = list->first->next;
	while(p != list->first && p->data != key)
		p = p->next;
	//如果没有找到返回空
	if(p == list->first)
		return NULL;
	return p;
}

获取有效结点个数

//获取有效结点个数
int  length(List *list)
{
	return list->size;
}

按值删除

//按值删除
void delete_val(List *list, ElemType key)
{
	//如果没有有效结点,则无需删除
	if(list->size == 0)
		return;
	//查找要删除结点所在的位置
	Node *p = find(list,key);
	//如果没有找到,无需删除
	if(p == NULL)
	{
		printf("要删除的数据不存在.\n");
		return;
	}
	//如果这个结点是尾结点,则进行尾删
	if(p == list->last)
	{
		pop_back(list);
	}
	else//否则进行普通删除
	{
		p->next->prio = p->prio;
		p->prio->next = p->next;
		free(p);
		list->size--;
	}
}

排序(按照由小到大排序)

//排序(按照由小到大排序)
void sort(List *list)
{
	//如果有效结点数小于等于1,则无需排序
	if(list->size==0 || list->size==1)
		return;
	//将原来的循环双链表,分解成两条
	Node *s = list->first->next;
	Node *q = s->next;
	list->last->next = NULL;
	list->last = s;
	list->last->next = list->first;
	list->first->prio = list->last;

	//从第二条链表中不断拿取元素按值插入(由小到大)到第一条循环链表中
	while(q != NULL)
	{
		//拿取元素
		s = q;
		q = q->next;

		//按值插入(由小到大)
		Node *p = list->first;//查找插入位置
		while(p->next!=list->last && p->next->data < s->data)
			p = p->next;
		
		if(p->next==list->last && p->next->data<s->data)
		{//尾插
			s->next = list->last->next;
			s->next->prio = s;
			s->prio = list->last;
			list->last->next = s;
			list->last = s;
		}
		else
		{//普通插入
			s->next = p->next;
			s->next->prio = s;
			s->prio = p;
			p->next = s;
		}
	}
}

逆置

//逆置
void resver(List *list)
{
	//如果有效结点数小于等于1,则无需排序
	if(list->size==0 || list->size==1)
		return;
	//将原来的循环双链表,分解成两条
	Node *p = list->first->next;
	Node *q = p->next;
	list->last->next = NULL;
	list->last = p;
	list->last->next = list->first;
	list->first->prio = list->last;

	//从第二条链表中不断拿取元素头插到第一条循环链表中
	while(q != NULL)
	{
		//获取元素
		p = q;
		q = q->next;
		//头插
		p->next = list->first->next;
		p->next->prio = p;
		p->prio = list->first;
		list->first->next = p;
	}
}

清空有效结点

//清空有效结点
void clear(List *list)
{
	//如果没有结点
	if(list->size == 0)
		return;
	//从第一个有效结点开始
	Node *p = list->first->next;
	while(p != list->first)
	{
		//清空释放有效结点(头删)
		p->next->prio = list->first;
		list->first->next = p->next;
		free(p);
		p = list->first->next;
	}
	list->last = list->first;
	list->size = 0;
}

销毁循环双链表

//销毁循环双链表
void destroy(List *list)
{
	//清空释放所有有效结点
	clear(list);
	free(list->first);
	//清空管理结点
	list->first = list->last = NULL;
}

结语

        对循环双链表的介绍就到这里,希望这篇文章能够给予你一些帮助。

在这里插入图片描述

附录

以下提供循环双链表的测试代码

DCList.h

#ifndef __DCLIST_H__
#define __DCLIST_H__

#include<stdio.h>
#include<malloc.h>
#include<assert.h>

#define ElemType int
//循环双链表结点结构
typedef struct Node
{
	ElemType data;     //数据域
	struct Node *prio; //前驱结点指针域
	struct Node *next; //后继结点指针域
}Node, *PNode;

//双链表结点管理结构
typedef struct List
{
	PNode first; //指向首结点(首结点不存放数据)
	PNode last;  //指向尾结点
	int   size;  //记录有效结点数
}List;

Node* _buynode(ElemType x);
void InitDCList(List *list);

void push_back(List *list, ElemType x);
void push_front(List *list, ElemType x);
void show_list(List *list);

void pop_back(List *list);
void pop_front(List *list);
void insert_val(List *list, ElemType x);
Node* find(List *list, ElemType key);
int  length(List *list);
void delete_val(List *list, ElemType key);
void sort(List *list);
void resver(List *list);
void clear(List *list);
void destroy(List *list);

#endif //__DCLIST_H__

DCList.cpp

#include"DCList.h"

//获取结点
Node* _buynode(ElemType x)
{
	Node *s = (Node *)malloc(sizeof(Node));
	assert(s != NULL);
	s->data = x;
	s->next = s->prio = NULL;
	return s;
}

//初始化双链表
void InitDCList(List *list)
{
	//申请头结点空间
	Node *s = (Node *)malloc(sizeof(Node));
	assert(s != NULL);
	//将头结点连接入管理结点
	list->first = list->last = s;
	//设置前驱指针指向(指向头结点)
	list->first->prio = list->last;
	//设置后继结点指向(指向尾结点)
	list->last->next  = list->first;
	list->size = 0;
}
//尾插
void push_back(List *list, ElemType x)
{
	//获取结点
	Node *s = _buynode(x);
	//将插入结点与头结点连接
	s->next = list->last->next;
	s->next->prio = s;
	//将插入结点与上一尾结点连接
	s->prio = list->last;
	list->last->next = s;
	//修改双向链表管理结点的尾指针
	list->last = s;
	list->size++;
}

//头插
void push_front(List *list, ElemType x)
{
	//获取插入结点
	Node *s = _buynode(x);
	//连接新插入结点和其下一个结点
	s->next = list->first->next;
	s->next->prio = s;
	//插入结点和头结点
	s->prio = list->first;
	list->first->next = s;
	//如果这个结点是第一个有效结点,需要更改管理结点尾指针的指向
	if(list->first == list->last)
		list->last = s;
	list->size++;
}
//查看循环双链表数据
void show_list(List *list)
{
	//指向第一个有效结点
	Node *p = list->first->next;
	//将所有有效结点输出
	while(p != list->first)
	{
		printf("%d-->",p->data);
		p = p->next;
	}
	printf("Nul.\n");
}
//尾删
void pop_back(List *list)
{   
	//如果没有有效结点,那么不进行删除
	if(list->size == 0)
		return;
	//设置管理结点的尾部指针指向
	Node *p = list->last;
	list->last = list->last->prio;
	//删除结点
	p->next->prio = p->prio;
	p->prio->next = p->next;
	free(p);
	list->size--;
}
//头删
void pop_front(List *list)
{
	//如果没有有效结点不进行删除
	if(list->size == 0)
		return;
	//删除结点
	Node *p = list->first->next;
	p->next->prio = p->prio;
	p->prio->next = p->next;
	//如果只有一个有效结点,需要更改管理结点尾指针指向
	if(list->size == 1)
		list->last = list->first;
	list->size--;
}
//按值插入(由小到大的顺序)
void insert_val(List *list, ElemType x)
{
	Node *p = list->first;//先指向头结点
	//查找要插入结点的位置
	while(p->next!=list->last && p->next->data < x)
		p = p->next;
	//如果插入的位置在最后,则进行尾插
	if(p->next==list->last && p->next->data<x)
	{
		push_back(list,x);
	}
	else //普通插入
	{
		Node *s = _buynode(x);
		s->next = p->next;
		s->next->prio = s;
		s->prio = p;
		p->next = s;
		list->size++;
	}
}

//查找某一个值所在的位置
Node* find(List *list, ElemType key)
{
	//从第一个有效结点位置开始查找
	Node *p = list->first->next;
	while(p != list->first && p->data != key)
		p = p->next;
	//如果没有找到返回空
	if(p == list->first)
		return NULL;
	return p;
}
//获取有效结点个数
int  length(List *list)
{
	return list->size;
}
//按值删除
void delete_val(List *list, ElemType key)
{
	//如果没有有效结点,则无需删除
	if(list->size == 0)
		return;
	//查找要删除结点所在的位置
	Node *p = find(list,key);
	//如果没有找到,无需删除
	if(p == NULL)
	{
		printf("要删除的数据不存在.\n");
		return;
	}
	//如果这个结点是尾结点,则进行尾删
	if(p == list->last)
	{
		pop_back(list);
	}
	else//否则进行普通删除
	{
		p->next->prio = p->prio;
		p->prio->next = p->next;
		free(p);
		list->size--;
	}
}

//排序(按照由小到大排序)
void sort(List *list)
{
	//如果有效结点数小于等于1,则无需排序
	if(list->size==0 || list->size==1)
		return;
	//将原来的循环双链表,分解成两条
	Node *s = list->first->next;
	Node *q = s->next;
	list->last->next = NULL;
	list->last = s;
	list->last->next = list->first;
	list->first->prio = list->last;

	//从第二条链表中不断拿取元素按值插入(由小到大)到第一条循环链表中
	while(q != NULL)
	{
		//拿取元素
		s = q;
		q = q->next;

		//按值插入(由小到大)
		Node *p = list->first;//查找插入位置
		while(p->next!=list->last && p->next->data < s->data)
			p = p->next;
		
		if(p->next==list->last && p->next->data<s->data)
		{//尾插
			s->next = list->last->next;
			s->next->prio = s;
			s->prio = list->last;
			list->last->next = s;
			list->last = s;
		}
		else
		{//普通插入
			s->next = p->next;
			s->next->prio = s;
			s->prio = p;
			p->next = s;
		}
	}
}
//逆置
void resver(List *list)
{
	//如果有效结点数小于等于1,则无需排序
	if(list->size==0 || list->size==1)
		return;
	//将原来的循环双链表,分解成两条
	Node *p = list->first->next;
	Node *q = p->next;
	list->last->next = NULL;
	list->last = p;
	list->last->next = list->first;
	list->first->prio = list->last;

	//从第二条链表中不断拿取元素头插到第一条循环链表中
	while(q != NULL)
	{
		//获取元素
		p = q;
		q = q->next;
		//头插
		p->next = list->first->next;
		p->next->prio = p;
		p->prio = list->first;
		list->first->next = p;
	}
}
//清空有效结点
void clear(List *list)
{
	//如果没有结点
	if(list->size == 0)
		return;
	//从第一个有效结点开始
	Node *p = list->first->next;
	while(p != list->first)
	{
		//清空释放有效结点(头删)
		p->next->prio = list->first;
		list->first->next = p->next;
		free(p);
		p = list->first->next;
	}
	list->last = list->first;
	list->size = 0;
}
//销毁循环双链表
void destroy(List *list)
{
	//清空释放所有有效结点
	clear(list);
	free(list->first);
	//清空管理结点
	list->first = list->last = NULL;
}

Main.cpp

#include"DCList.h"

void main()
{
	List mylist;
	InitDCList(&mylist);

	ElemType Item;
	Node *p = NULL;
	int select = 1;
	while(select)
	{
		printf("**************************************\n");
		printf("* [1] push_back      [2] push_front  *\n");
		printf("* [3] show_list      [4] pop_back    *\n");
		printf("* [5] pop_front      [6] insert_val  *\n");
		printf("* [7] find           [8] length      *\n");
		printf("* [9] delete_val     [10] sort       *\n");
		printf("* [11] resver        [12] clear      *\n");
		printf("* [13*] destroy      [0] quit_system *\n");
		printf("**************************************\n");
		printf("请选择:>");
		scanf("%d",&select);
		if(select == 0)
			break;
		switch(select)
		{
		case 1:
			printf("请输入要插入的数据(-1结束):>");
			while(scanf("%d",&Item),Item != -1)
			{
				push_back(&mylist,Item);
			}
			break;
		case 2:
			printf("请输入要插入的数据(-1结束):>");
			while(scanf("%d",&Item),Item != -1)
			{
				push_front(&mylist,Item);
			}
			break;
		case 3:
			show_list(&mylist);
			break;
		case 4:
			pop_back(&mylist);
			break;
		case 5:
			pop_front(&mylist);
			break;
		case 6:
			printf("请输入要插入的数据:>");
			scanf("%d",&Item);
			insert_val(&mylist,Item);
			break;
		case 7:
			printf("请输入要查找的数据:>");
			scanf("%d",&Item);
			p = find(&mylist,Item);
			if(p == NULL)
			{
				printf("要查找的数据在链表中不存在.\n");
			}
			break;
		case 8:
			printf("双向循环链表的长度为:> %d \n",length(&mylist));
			break;
		case 9:
			printf("请输入要删除的值:>");
			scanf("%d",&Item);
			delete_val(&mylist,Item);
			break;
		case 10:
			sort(&mylist);
			break;
		case 11:
			resver(&mylist);
			break;
		case 12:
			clear(&mylist);
			break;
		//case 13:
		//	destroy(&mylist);
		//	break;
		default:
			printf("输入的命令错误,请重新输入.\n");
			break;
		}
	}
	destroy(&mylist);
}
  • 27
    点赞
  • 142
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值