第2章 线性表之顺序存储
自定义头文件
#include <stdio.h>
/* 状态码 */
#define TRUE 1 // 真/是
#define FALSE 0 // 假/否
#define OK 1 // 通过/成功
#define ERROR 0 // 错误/失败
//系统中已有此状态码定义,要防止冲突
#ifndef OVERFLOW
#define OVERFLOW -2 //堆栈上溢
#endif
//系统中已有此状态码定义,要防止冲突
#ifndef NULL
#define NULL ((void*)0)
#endif
/* 状态码类型 */
typedef int Status;
typedef int ElemType;
/* 布尔类型 */
typedef int Boolean;
线性结构: 线性表、栈、队列、串和数组。
线性结构基本特点: 除第一个元素无直接前驱,最后 一 个元素无直接后继之外,其他每个数据元素都有一个前驱和 后继。
线性表: 零个或多个数据元素的有限序列。
2.1 线性表的抽象数据类型
ADT List{
数据对象:D
数据关系:S
基本操作:
InitList(&L)//初始化操作,建立一个空的线性表L
DestroyList(&L)//销毁线性表,并释放线性表L所占用的内存空间。
CreateList(&L,len)//表的扩容操作
ListEmpty(L)//若线性表为空,返回true,否则返回false
ClearList(&L)//将线性表清空
GetElem(L,i,&e)//按位查找
LocateElem(L,e)//按值查找
ListInsert(&L,i,e)//在线性表第i个位置插入新元素e
ListDelete(&L.i,&e)//删除线性表第i个位置的元素,并用e返回值
ListLength(L)//返回线性表L的元素个数
PrintList(L)//前后顺序输出线性表L的所有元素值
}ADT List;
假设La表示线性表A,Lb表示线性表B;求在线性表Lb而不在线性表La中的数据插入到La中
//顺序表的合并
Status ListUnion(SqList& La, SqList Lb) {
int La_len = La.length;
int Lb_len = Lb.length;
//循环Lb中的每一个元素
for (int i = 0; i < Lb_len; i++) {
//如果La中存在与Lb相等的值则返回位序,未找到返回0
int exit = LocatElem(La, Lb.data[i]);
if (exit == 0) {//说明在La中没有找到和Lb相同的元素
//插入之前,看是否需要扩容
if (La.length == La.MaxSize) CreateList(La, 10);
//调用插入函数
ListInsert(La, La.length ++, Lb.data[i]);
}
}
return OK;
}
当你传递一个参数给函数的时候,这个参数会不会在函数内被改动。决定了使用什么参数形式,如果需要被改动,则需要传递指向这个参数的指针。如果不用被改动,可以直接传递这个参数。
2.2 线性表的顺序存储结构
顺序表(Sequent ialList ) :采用顺序存储结构的线性表为顺序表。
其特点是,逻辑上相邻的数据元素, 其物理次序也是相邻的。
假设线性表的每一个数据占用c个存储单元,那么第i个元素的存储单元为
- Loc(ai)=Loc(a1)+(i-1)*c
//数组静态分配
# include<stdio.h>
# define InitSize 10 // 线性表初始容量
# define ElemType int// ElemType根据你要存什么元素而确定类型
typedef struct {
ElemType data[InitSize]; // 用静态的“数组”存放数据元素
int length;// 顺序表当前长度
} SqList;
//数组动态分配
# include<stdio.h>
# define InitSize 10 // 线性表初始容量
# define ElemType int// ElemType根据你要存什么元素而确定类型
typedef struct {
ElemType *data; // 用静态的“数组”存放数据元素
int MaxSize;// 用于扩容的字段
int length;// 顺序表当前长度
} SqList;
ps:在任意时刻,线性表的长度应该小于数组的长度
2.3 顺序表基本操作的实现
顺序表的初始化
① 为顺序表L动态分配一个预定义大小的数组空间,使data指向这段空间的基地址。
②将表的当前长度设为0。
//初始化顺序表
Status InitList(SqList& L) {
//分配空间方式一:malloc
L.data = (ElemType*)malloc(InitSize * sizeof(ElemType));//为局部指针变量分配空间
// 分配空间方式二:C++的new分配
//L.data = new ElemType(InitSize);//为顺序表分配一个大小为InitSize的数组空间
if (!L.data) exit(OVERFLOW);//内存空间分配失败
L.MaxSize = InitSize;//初始化最大空间
L.length = 0;//将线性表的长度置空
return OK;
}
顺序表的基础操作
//清空线性表
void ClearList(SqList& L) {
for (int i = 0; i < L.length; i++) {
L.data[i] = NULL;//将每一个元素释放
}
L.length = 0;//长度置空
printf("线性表已清空!");
}
//销毁顺序表
void DestroyList(SqList& L) {
//前提条件:存在此空间
if (L.data) {
//new p→delete p; malloc p→free(p)
free(L.data);//删除分配空间
L.data = NULL;//基地址置空
}
L.length = L.MaxSize = 0;//表空间和表长度置空
printf("顺序表已销毁!\n");
}
//顺序表判空
Status ListEmpty(SqList L) {
//前提条件不是空表
if (L.length != 0) return 0;//不是空表
return 1;//是空表
}
//顺序表的遍历
void ListPrint(SqList L) {
for (int i = 0; i < L.length; i++) {
printf("data[%d]=%d\n", i, L.data[i]);
}
}
malloc(m)函数,开辟m字节长度的地址空间,并返回这段空间的首地址
sizeof(x)运算,计算变量x的长度
free(p)函数,释放指针p所指变量的存储空间,即彻底删除一个变量
delete 指针p,释放指针P所指向的内存。P必须建new操作的返回值
顺序表的扩容
//顺序表的扩容操作
Status CreateList(SqList& L, int len) {
//1、将原来的空间备份一份
ElemType* p = L.data;
//2、重新分配一个增加len后的新空间
L.data = (ElemType*)malloc((L.MaxSize + len) * sizeof(ElemType));
if (!L.data) exit(OVERFLOW);//空间分配失败
//3、将每个元素放入新空间
for (int i = 0; i < L.length; i++) {
L.data[i] = p[i];
}
//4、线性表的长度+len
L.MaxSize += len;
//5、释放临时空间
free(p);
return OK;
}
顺序表的查找
按位查找:
- ① 判断指定的位置序号i值是否合理(1≤i≤L.length ),若不合理,则返回ERROR。
- ② 若i值合理,则将第i个数据元素L.data[i-1]赋给参数e,通过e返回第i个数据元素的传值。
按值查找:
- ① 从第一个元素起,依次和e相比较,若找到与e相等的元素L.data[i],则查找成功,返回该元素的序号i+1。
- ② 若查遍整个顺序表都没有找到,则查找失败,返回0。
//顺序表的按位查找
Status GetElem(SqList L, int i, ElemType& e) {
if (L.length == 0) return NULL;//顺序表中没有元素
if (i<1 || i>L.length) return ERROR;//插入位置不合理
e = L.data[i - 1];//第data[i-1]的存储单元存放第i个元素
return OK;
}
//顺序表的按值查找
Status LocatElem(SqList L, ElemType e) {
if (L.length == 0) return ERROR;//顺序表中没有元素
for (int i = 0; i < L.length; i++) {
if (L.data[i] == e) return i + 1;//查找成功,返回序号i+1;
}
return 0;//查找失败,返回0
}
顺序表的插入
① 判断插入位置i是否合法(i值的合法范围是1≤i≤n+1),若不合法则返回 ERROR。
②判断顺序表的存储空间是否已满,若满则返回ERROR。
③将第n个至第 i个位置的元素依次向后移动一个位置,空出第i个位置((i=n+1时无需移动)。
④将要插入的新元素e放入第i个位置。
⑤表长加1。
//顺序表的插入
Status ListInsert(SqList& L, int i, ElemType e) {
if (i<1 || i>(L.length + 1)) return ERROR;//插入位置不合法
if (L.length == L.MaxSize) return ERROR;//顺序表空间已满
//非最后一个元素,需要挪动
for (int j = L.length; j >= i; j--) {
L.data[j] = L.data[j - 1];//插入位置及之后的的元素后移
}
//最后一个元素可以直接插
L.data[i - 1] = e;//将新的元素放到第i个位置
++L.length;//插入元素后,表长+1
return OK;
}
顺序表的删除
① 判断删除位置i是否合法(合法值为1≤i≤n),若不合法则返回ERROR。
②将第i+1个至第n个的元素依次向前移动一个位置( i=n时无需移动)
③表长减1。
//顺序表的删除
Status ListDelete(SqList& L, int i, ElemType& e) {
if (i<1 || i>L.length) return ERROR;//判断删除位置是否合法
if (L.data == 0) return ERROR; //空间没有可以删除的元素
e = L.data[i - 1];//先将要删除的值返回出去
for (int j = i - 1; j < L.length - 1; j++) {
L.data[j] = L.data[j + 1];//被删除的元素之后依次前移
}
--L.length;//每删除一个元素,表长减1;
return OK;
}