数据结构(C语言版)代码实现(五)——双向循环链表的部分实现

目录

参考材料与格式

线性表的有关知识

头文件

库、宏定义、数据类型声明

线性表的双向链表存储结构

构造空链表

销毁链表

链表长度

按位查找

插入元素

删除元素

打印链表

完整头文件DuLinkList.h

测试函数(主函数)

测试结果

收获


参考材料与格式

参考材料:数据结构(C语言版)严蔚敏 吴伟民编著。

线性表的有关知识

预习到这里,有点感觉概念有点多,线性表,顺序表,链表,单链表,双向链表,静态链表,动态链表,循环链表......有的链表只有头指针,有的链表头指针尾指针都有,有的只有尾指针,那么怎么区分这些概念呢?从头说起。

线性表是数据结构,数据结构是相互之间一种或多种特定关系的数据元素的集合。顺序表与链表都是物理结构,又称存储结构,存储结构包括数据元素的表示和关系的表示。存储结构是数据结构在计算机中的表示,举个例子,可以说,单链表是线性表在计算机中表示的一种方式。所以,2.2节的标题是线性表的顺序表示与实现,意思是,这一节讲用顺序表表示线性表(线性表的顺序表示),用类C代码实现顺序表。

这些概念有一个特点,都是成对出现。如顺序表与链表、单链表与双向链表、静态链表与动态链表、循环链表与非循环链表......这些概念每一对都有且只有一个,如本节实现的存储结构为双向 循环 动态 链表,只有头指针。

为什么说这么多呢?因为课本中虽然单独介绍循环链表(35页2.3.2节),但后面没跟上代码,我一想,静态链表也可以循环,单链表、双向链表也可以循环,所以还是先弄清楚概念,再怎么写文章也不迟。

头文件

库、宏定义、数据类型声明

#include <cstdio>
#include <cstdlib>
#include <cstring>

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;//Status是函数的类型,其值是函数结果状态代码
typedef int ElemType;

线性表的双向链表存储结构

//-----线性表的双向链表存储结构-----
typedef struct DuLNode {
	ElemType data;
	struct DuLNode* prior;
	struct DuLNode* next;
}DuLNode,*DuLinkList;

构造空链表

Status InitList_DuL(DuLinkList& L) {
	//构造一个空的线性链表L
	L = (DuLinkList)malloc(sizeof(DuLNode));
	L->prior = L->next = L;
	return OK;
}

销毁链表

Status DestroyList_DuL(DuLinkList& L) {
	//销毁线性链表L,L不再存在
	DuLinkList p = L;
	while (L->next != L) {
		L->next->prior = L->prior;
		L->prior->next = L->next;
		L = L->next;
		free(p);
		p = L;
	}
	free(L);
	return OK;
}

链表长度

int ListLength_DuL(DuLinkList L) {//求链表长度
	DuLinkList p = L->next;
	int count = 0;
	while (p != L)
	{
		p = p->next;
		count++;
	}
	return count;
}//一定要在GetElem_DuL函数前面

代码写完调试时,报错,找不到标识符,下面是解决文章。

如何解决VC2019中:error C3861: “xxxx”: 找不到标识符_error c3861 pow 找不到标识符-CSDN博客

简单总结,因为我的GetElem_DuL函数调用了这个求链表长度的函数,如果Get这个函数在前面的话,Get函数内部的求链表长度函数没有定义,出现C3861问题。解决方法有二,一是像注释中写的将求链表长度的函数放在前面,二是将求链表长度的函数写函数声明。

按位查找

DuLinkList GetElem_DuL(DuLinkList L, int i) {
	//L为带头结点的单链表的头指针。
	//按位查找
	DuLinkList p = L;
	if (i >= 1 && i <= ListLength_DuL(L) + 1) {
		int m = i;
		while (m--) {
			p = p->next;
		}
	}
	else
		p = NULL;
	return p;
}

插入元素

//算法2.18 插入元素
Status ListInsert_DuL(DuLinkList& L, int i, ElemType e) {
	//在带头结点的双链循环线性表L中第i个位置之前插入元素e,即在p前面插入结点
	//i的合法值为1<=i<=表长+1。
	DuLinkList p, s;//曾试过变量p,s一边声明一边赋值,结果报错,不知道为什么。
	if (!(p = GetElem_DuL(L, i)))//在L中确定插入位置指针p
		return ERROR;//i等于表长加1时,p指向头结点;i大于表长加1时,p=NULL。
	if (!(s = (DuLinkList)malloc(sizeof(DuLNode))))
		return ERROR;
	s->data = e;
	s->prior = p->prior;p->prior->next = s;
	s->next = p;p->prior = s;
	return OK;
}

删除元素

//算法2.19 删除元素
Status ListDelete_DuL(DuLinkList& L, int i, ElemType& e) {
	//删除带头结点的双链循环线性表L的第i个元素,i的合法值为1<=i<=表长
	DuLinkList p;
	if (!(p = GetElem_DuL(L, i)))//在L中确定第i个元素的位置指针p
		return ERROR;//p=NULL,即第i个元素不存在
	e = p->data;
	p->prior->next = p->next;
	p->next->prior = p->prior;
	free(p);
	return OK;
}

打印链表

void PrintList_DuL(DuLinkList L) {//打印链表
	DuLinkList p = L->next;
	while (p != L) {//注意与单链表的有区别,单链表是p!=NULL
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

完整头文件DuLinkList.h

#pragma once
#include <cstdio>
#include <cstdlib>
#include <cstring>

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;//Status是函数的类型,其值是函数结果状态代码
typedef int ElemType;

//-----线性表的双向链表存储结构-----
typedef struct DuLNode {
	ElemType data;
	struct DuLNode* prior;
	struct DuLNode* next;
}DuLNode,*DuLinkList;

Status InitList_DuL(DuLinkList& L) {
	//构造一个空的线性链表L
	L = (DuLinkList)malloc(sizeof(DuLNode));
	L->prior = L->next = L;
	return OK;
}

Status DestroyList_DuL(DuLinkList& L) {
	//销毁线性链表L,L不再存在
	DuLinkList p = L;
	while (L->next != L) {
		L->next->prior = L->prior;
		L->prior->next = L->next;
		L = L->next;
		free(p);
		p = L;
	}
	free(L);
	return OK;
}

int ListLength_DuL(DuLinkList L) {//求链表长度
	DuLinkList p = L->next;
	int count = 0;
	while (p != L)
	{
		p = p->next;
		count++;
	}
	return count;
}//一定要在GetElem_DuL函数前面

DuLinkList GetElem_DuL(DuLinkList L, int i) {
	//L为带头结点的单链表的头指针。
	//按位查找
	DuLinkList p = L;
	if (i >= 1 && i <= ListLength_DuL(L) + 1) {
		int m = i;
		while (m--) {
			p = p->next;
		}
	}
	else
		p = NULL;
	return p;
}

//算法2.18 插入元素
Status ListInsert_DuL(DuLinkList& L, int i, ElemType e) {
	//在带头结点的双链循环线性表L中第i个位置之前插入元素e,即在p前面插入结点
	//i的合法值为1<=i<=表长+1。
	DuLinkList p, s;//曾试过变量p,s一边声明一边赋值,结果报错,不知道为什么。
	if (!(p = GetElem_DuL(L, i)))//在L中确定插入位置指针p
		return ERROR;//i等于表长加1时,p指向头结点;i大于表长加1时,p=NULL。
	if (!(s = (DuLinkList)malloc(sizeof(DuLNode))))
		return ERROR;
	s->data = e;
	s->prior = p->prior;p->prior->next = s;
	s->next = p;p->prior = s;
	return OK;
}

//算法2.19 删除元素
Status ListDelete_DuL(DuLinkList& L, int i, ElemType& e) {
	//删除带头结点的双链循环线性表L的第i个元素,i的合法值为1<=i<=表长
	DuLinkList p;
	if (!(p = GetElem_DuL(L, i)))//在L中确定第i个元素的位置指针p
		return ERROR;//p=NULL,即第i个元素不存在
	e = p->data;
	p->prior->next = p->next;
	p->next->prior = p->prior;
	free(p);
	return OK;
}

void PrintList_DuL(DuLinkList L) {//打印链表
	DuLinkList p = L->next;
	while (p != L) {//注意与单链表的有区别,单链表是p!=NULL
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

测试函数(主函数)

#include "DuLinkList.h"

int main()
{
	DuLinkList L;
	Status i;
	ElemType e;
	i = InitList_DuL(L);
	if (!i)
		printf("构造成功!\n");
	else
		printf("构造失败!\n");
	i = ListInsert_DuL(L, 0, 13);
	if (!i)
		printf("插入失败。\n");
	else
		printf("插入的元素为13。\n");
	for (int j = 5;j <= 10;j++) {
		i = ListInsert_DuL(L, 1, j);
		if (!i)
			printf("插入失败。\n");
		else
			printf("插入的元素为%d。\n",j);
	}
	i = ListInsert_DuL(L, 7, 13);
	if (!i)
		printf("插入失败。\n");
	else
		printf("插入的元素为13。\n");
	i = ListInsert_DuL(L, 9, 13);
	if (!i)
		printf("插入失败。\n");
	else
		printf("插入的元素为13。\n");
	PrintList_DuL(L);
	i = ListDelete_DuL(L, 7, e);
	if (!i)
		printf("删除失败\n");
	else
		printf("删除的元素为%d。\n", e);
	i = ListDelete_DuL(L, 3, e);
	if (!i)
		printf("删除失败\n");
	else
		printf("删除的元素为%d。\n", e);
	PrintList_DuL(L);
	DestroyList_DuL(L);
	return 0;
}

测试结果

收获

1、弄清了本章一些概念的联系。

2、解决了找不到标识符的问题。

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值