【数据结构】第1章 线性表

本文深入探讨了线性表这一核心数据结构,详细介绍了线性表的特性与存储方式,包括顺序存储与链式存储,以及各种操作如初始化、查找、插入、删除等实现细节。

开发环境

window10
visual studio 2019
c

1 线性表的特点

(1)表中元素个数有限
(2)表中元素具有逻辑上的顺序性,表中元素有其先后次序
(3)表中元素都是数据元素,每个元素都是单个元素
(4)表中元素的数据类型都相同,这意味着每个元素占有相同大小的存储空间
(5)表中元素具有抽象性,即仅讨论元素见的逻辑关系,而不考虑元素究竟表示什么内容

2 线性表的存储结构

#define MAXSIZE 50			//定义线性表的最大长度
#define true 1
#define false 0

2.1 顺序存储

2.1.1 静态分配存储空间

typedef struct {
	ElemType data[MAXSIZE];	//顺序表的元素
	int length;				//顺序表的当前长度
}SqList;					//顺序表的类型定义

2.1.2 动态分配存储空间

typedef struct {
	ElemType* data;			//顺序表的元素
	int Maxsize;			//顺序表的最大容量
	int length;				//顺序表的当前长度
}SeqList;	

2.1.3 顺序表的基本操作

对动态分配空间的线性表进行操作定义

void InitList(SeqList* L);
int Length(SeqList L);
int LocateElem(SeqList L,ElemType e);
int GetElem(SeqList L,int i);
_Bool ListInsert(SeqList* L, int i, ElemType e);
_Bool ListDelete(SeqList* L, int i, ElemType* e);
void PrintList(SeqList L);
_Bool Empty(SeqList L);
_Bool DestroyList(SeqList* L);
void main() {
	SeqList* L = (SeqList*)malloc(sizeof(SeqList) * MAXSIZE);	//解决error:C4700错误,初始化局部变量
	InitList(L);
	······
}
(1) 初始化表

构造一个空的线性表

void InitList(SeqList* L){
	L->data = (ElemType*)malloc(sizeof(ElemType) * MAXSIZE);
	L->Maxsize = MAXSIZE;
	L->length = 0;
}
(2) 求表长

返回线性表L的长度,即L中数据元素的个数

int Length(SeqList L) {
	return L.length;
}
(3) 按值查找操作

按值查找操作。在表L中查找具有给定关键字值的元素

int LocateElem(SeqList L,ElemType e) {
	int i;
	for (i = 0; i < L.length; i++) 
		if (L.data[i] == e) 
			return i+1;						//下标为i的元素值等于e,返回其位序i+1
	return false;							//退出循环,说明查找失败
}
(4) 按位查找操作

按位查找操作。获取表L中得第i个位置的元素的值

int GetElem(SeqList L,int i) {
	if (i < 1 || i > L.length)
		return false;
	return L.data[i];
}
(5) 插入操作

在表L中的第i个位置(1<=i<=L.length+1)上插入指定元素e

_Bool ListInsert(SeqList* L, int i, ElemType e) {
	//判断合法性
	if (i < 1 || i > L->length + 1)
		return false;						//判断i的范围是否有效
	if (L->length >= L->Maxsize)
		return false;						//当前存储空间已满,不能插入

	//插入元素
	int j;
	for (j = L->length; j >= i; j--)
		L->data[j] = L->data[j - 1];		//将第i个元素及之后的元素后移
	L->data[i - 1] = e;						//在位置i处放入e
	L->length++;							//线性表长度+1
	return true;
}
(6) 删除操作

删除表L中的第i个(1<=i<=L.length)位置的元素,并用e返回删除元素的值

_Bool ListDelete(SeqList* L, int i, ElemType* e) {
	//判断合法性
	if (i < 1 || i > L->length) 
		return false;						//判断i的范围是否有效

	e = L->data[i-1];						//将被删除的元素赋值给e

	//删除元素
	int j;
	for (j = i; j < L->length; j++)
		L->data[j - 1] = L->data[j];		//将第i个位置后的元素前移
	L->length--;							//线性表长度-1
	return false;
}
(7) 输出操作

按前后顺序输出线性表L的所有元素值

void PrintList(SeqList L) {
	int i;
	for (i = 0; i < L.length; i++)
		printf("%d\t", L.data[i]);
	printf("\n");
}
(8) 判空操作

若L为空表,则返回true,否则返回false

_Bool Empty(SeqList L) {
	if (L.length <= 0)
		return true;
	return false;
}
(9) 销毁操作

销毁线性表,并释放线性表L所占用的内存空间

_Bool DestroyList(SeqList* L) {
	if (L != NULL)
		free(L);
	else
		return false;
	L = NULL;
	return true;
}

2.2 链式存储

2.2.1 单链表及其基本操作

#define MAXSIZE 50			//定义线性表的最大长度
#define true 1
#define false 0

typedef int ElemType;		//定义线性表的元素类型

typedef struct LNode {		//定义单链表结点类型
	ElemType data;			//数据域
	struct LNode* next;		//指针域
}LNode, * LinkList;
LinkList List_HeadInsert(LinkList L);
LinkList List_TailInsert(LinkList L);
LNode* GetElem(LinkList L, int i);
LNode* LocateElem(LinkList L, ElemType e);
_Bool ListInsertAfter(LinkList L, int i, ElemType e);
_Bool ListInsertFront(LinkList L, int i, ElemType e);
_Bool ListDeletePos(LinkList L, int i, ElemType e);
_Bool ListDeleteNode(LinkList L, LNode* p, ElemType e);
void PrintList(LinkList L);
_Bool DestroyList(LinkList L);
_Bool Empty(LinkList L);
void main() {
	LinkList L = NULL;
	······;
}
(1)建立单链表
(1.1)头插法建立单链表

从一个空表开始,生成新结点,并将读取到的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头,即头结点之后。

LinkList List_HeadInsert(LinkList L) {				//头插法,逆向建立单链表
	LNode* s;
	ElemType x;

	L = (LinkList)malloc(sizeof(LNode));			//创建链表/头结点
	L->next = NULL;									//初始为空
	scanf_s("%d", &x);								//引用:取地址
	while (x != 9999) {								//输入9999表示结束
		s = (LNode*)malloc(sizeof(LNode));			//创建新结点
		s->data = x;
		s->next = L->next;
		L->next = s;								//将新结点插入表中,L为头指针
		scanf_s("%d",&x);
	}
	return L;
}
(1.2)尾插法建立单链表

将新结点插入到当前链表的表尾,为此必须增加一个尾指针tail,使其始终指向当前链表的尾结点。

LinkList List_TailInsert(LinkList L) {				//尾插法,正向建立单链表
	LNode* s;
	ElemType x;

	L = (LinkList)malloc(sizeof(LNode));			//创建链表
	LNode* tail = L;								//创建尾结点
	
	scanf_s("%d", &x);								//引用:取地址
	while (x != 9999) {								//输入9999表示结束
		s = (LNode*)malloc(sizeof(LNode));			//创建新结点
		s->data = x;
		tail->next = s;
		tail = s;									//将新结点插入表中,L为头指针
		scanf_s("%d", &x);
	}
	tail->next = NULL;								//为空
	return L;
}
(2)查找结点值
(2.1)按序号

从第一个结点出发,顺指针next域逐个往下搜索,直到找到第i个结点为止,否则放回最后一个结点指针域NULL。

LNode* GetElem(LinkList L, int i) {
	int count = 1;									//计数,初始为1
	LNode* p = L->next;								//头结点指针赋给p

	if (i == 0)
		return L;									//若i等于0,则返回头结点
	if (i < 1)
		return NULL;								//若i无效,则返回NULL
	while (p && count < i) {						//从第1个结点开始找,查找第i个结点
		p = p->next;
		count++;
	}
	return p;										//返回第i个结点的指针,若i大于表长则返回NULL
}
(2.1)按值

从第一个结点开始,由前往后依次比较表中各结点数据域的值,若某结点数据域的值等于给定值e,则返回该结点的指针;若整个表中没有这样的结点,则返回NULL。

LNode* LocateElem(LinkList L, ElemType e) {
	LNode* p = L->next;
	while (p != NULL && p->data != e)				//从第1个结点开始查找data域为e的结点
		p = p->next;
	return p;										//找到后返回该结点指针,否则返回NULL
}
(3)插入结点操作
(3.1)后插

将值为x的新结点插入到单链表的第i个位置上

_Bool ListInsertAfter(LinkList L, int i, ElemType e) {
	LNode* s;
	LNode* p;
	if (!(p = GetElem(L, i - 1)))					//查找插入位置的前驱结点
		return false;

	s = (LNode*)malloc(sizeof(LNode));				//创建新结点
	s->data = e;

	s->next = p->next;
	p->next = s;
	return true;
}
(3.2)前插

将值为x的新结点插入到单链表的第i个位置结点的前面

_Bool ListInsertFront(LinkList L, int i, ElemType e) {
	LNode* s;
	LNode* p;
	ElemType temp;
	if (!(p = GetElem(L, i - 1)))
		return false;

	s = (LNode*)malloc(sizeof(LNode));				//创建新结点
	s->data = e;

	s->next = p->next;
	p->next = s;

	/*交换数据域*/
	temp = p->data;
	p->data = s->data;
	s->data = temp;
	return true;
}
(4)删除结点操作
(4.1)按位置

将单链表的第i个结点删除

_Bool ListDeletePos(LinkList L, int i, ElemType e) {
	LNode* p;
	LNode* q;
	if (!(p = GetElem(L, i - 1)))					//查找删除位置的前驱结点
		return false;

	q = p->next;									//令q指向被删除结点
	p->next = q->next;								//将*q结点从链中“断开”
	e = q->data;

	free(q);										//释放结点的存储空间
	return true;
}
(4.2)按结点

将单链表的指定结点删除

_Bool ListDeleteNode(LinkList L, LNode* p, ElemType e) {
	if (p == NULL)
		return false;
	LNode* q;
	q = p->next;									//令q指向*p的后继结点
	e = q->data;
	p->data = q->data;								//和后继结点交换数据域
	p->next = q->next;								//将*q结点从链中“断开”
	free(q);										//释放后继结点的存储空间
	return true;
}
(5)求表长操作
int Length(LinkList L) {
	int count = 0;
	LNode* p = L;
	while (p->next != NULL) {
		count++;
		p = p->next;
	}
	return count;
}
(6)打印表操作
void PrintList(LinkList L) {
	LNode* p = L;
	p = p->next;
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
	printf("链表长度:%d\n", Length(L));
}
(7)销毁表操作
_Bool DestroyList(LinkList L) {
	if (!L)
		return false;
	while (L) {
		LNode* p;
		p = L;
		L = L->next;
		free(p);
	}
	return true;
}
(8)判空操作
_Bool Empty(LinkList L) {
	if (!L)
		return true;
	return false;
}

2.2.2 双链表

(1)双链表定义
typedef struct DNode {				//定义双链表结点类型
	ElemType data;					//数据域
	struct DNode* prior, * next;	//前驱和后继指针
}DNode,* DLinkList;
(2)插入

在结点p后插入结点s

	s->next = p->next;
	s->prior = p;
	p->next->prior = s;
	p->next = s;
(3)删除

删除结点s,p指向结点s的前驱结点

	p = s->prior;
	p->next = s->next;
	s->next->prior = p;
	free(s);

2.2.3 循环链表

(1)循环单链表

尾结点甩回头结点。

(2)循环双链表

尾结点后继指针甩回头结点,头结点前驱指针指向尾结点。

2.2.4 静态链表

typedef struct{						//静态链表结构类型的定义
	ElemType data;					//存储数据元素
	int next;						//下一个元素的数组下标
}SLinkList[MAXSIZE];

3 参考

王道,侵删

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

社恐患者

赚钱不易呜呜呜

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值