【数据结构】第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 参考

王道,侵删

猜你喜欢

转载自blog.csdn.net/qq_44714521/article/details/106989648