tips:本章节只介绍线性表的顺序存储结构
1.线性的定义、特点和基本操作
1.1线性表的定义
线性表是具有相同类型的n(n >= 0)个数据元素的有限序列,其中n为表长,当n=0时,线性表为空表。
若用L命名线性表,则一般表示为:L=(a1,a2,...ai,ai+1,...,an)。其中,a1是唯一的“第一个”数据元素,又称为表头元素;an是唯一的“最后一个元素”,又称为表尾元素。
除第一个元素外,每个元素有且仅有一个直接前驱;除最后一个元素外,每个元素有且仅有一个直接后继。以上为线性表的逻辑特性。
1.2线性表的特点
1.表中元素个数有限
2.表中元素具有逻辑上的顺序性,表中元素有其先后次序。
3.表中元素都是数据元素,每个元素都是单个元素。
4.表中元素的数据类型都相同,这意味着每个元素占有相同大小的存储空间。
5.表中元素具有抽象性,即仅仅讨论元素之间的逻辑关系,而不考虑元素究竟表达什么内容。
注意:线性表是一种逻辑结构,表示元素之间一对一的相邻关系。而顺序表和链式表是指存储结构。两者不是同一个概念。
1.3线性表的基本操作
一个数据结构的基本操作是指其最核心、最基本的操作。其他比较复杂的操作都可以通过调用其基本操作来实现。
线性表的主要操作如下:
//初始化表
//求表长
//按值查找操作
//按位查找操作
//插入操作
//删除操作
//输出操作
//判空操作
//销毁操作
2.线性表的顺序表示
2.1 顺序表的定义
线性表的顺序存储又称顺序表。它是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。
第 1 个元素存储在线性表的起始位置,第i个元素的存储位置后面紧接着存储的是第i+1个元素,称i为元素ai在线性表中的位序。
因此,顺序表的特点是表中元素的逻辑顺序与其物理顺序相同。
每个数据元素的存储位置都和线性表的起始位置相差一个和该数据元素的位序成正比的常数,因此,线性表中的任何一数据元素都可以随机存取,所以线性表的顺序存储结构是一种随机存取的存储结构。通常用高级程序设计语言中的数组来描述线性表的顺序存储结构。
注意:线性表中元素的位序是从1开始的,而数组中元素的下标是从0开始的。
2.2 顺序表的具体实现
1. 使用前准备
#define _CRT_SECURE_NO_WARNINGS 1 //忽略编译器不通过的不安全函数,方便程序移植到不同机器上
//头文件声明
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
//宏定义
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
2. 线性表的顺序存储符结构代码
#define MAXSIZE 20 /* 存储空间初始分配量 */
typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */
typedef struct
{
ElemType data[MAXSIZE]; /* 数组,存储数据元素 */
int length; /* 线性表当前长度 */
}SqList;
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
3. 初始化表
/* 初始化顺序线性表 */
Status InitList(SqList* L)
{
L->length = 0; //将顺序表的长度置为0
return OK;
}
注意:
当你传递一个参数给函数时,这个参数会不会在函数内部被改动决定了使用什么参数形式。
如果需要被改动,则需要传递指向这个参数的指针。
如果不用被改动,可以直接传递这个参数。
4. 判空操作
/* 初始条件:顺序线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
Status ListEmpty(SqList L)
{
if (L.length == 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
5. 求表长操作
/* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
int ListLength(SqList L)
{
return L.length;
}
6. 遍历输入操作
//查看线性表中的元素
Status visit(ElemType c)
{
printf("%d ", c);
return OK;
}
/* 初始条件:顺序线性表L已存在 */
/* 操作结果:依次对L的每个数据元素输出 */
Status ListTraverse(SqList L)
{
int i;
for (i = 0; i < L.length; i++)
{
visit(L.data[i]);
}
printf("\n");
return OK;
}
7.按位查找操作
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:用e返回L中第i个数据元素的值,注意i是指位置,第1个位置的数组是从0开始 */
Status GetElem(SqList L, int i, ElemType* e)
{
if (L.length == 0 || i<1 || i>L.length) //判断合法性
{
return ERROR;
}
*e = L.data[i - 1];
return OK;
}
注意:
if分支语句的判断条件中 符号 || 为C语言中的 逻辑或 运算符。符号两边的表达式 同假为假。在该例中,即只要一个条件为真就会进入分支语句中 ,return ERROR。
且 符号 || 逻辑或运算符 ,遵循 短路原则,在本例中,即当条件1为真时,不管之后的条件是真还是假,整个if语句的条件都为真,所以不会执行判断后面的条件2和条件3 。
8.按值查找操作
/* 初始条件:顺序线性表L已存在 */
/* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */
/* 若这样的数据元素不存在,则返回值为0 */
int LocateElem(SqList L, ElemType e)
{
int i;
if (L.length == 0)
return 0;
for (i = 0; i < L.length; i++)
{
if (L.data[i] == e)
break;
}
if (i >= L.length)
return 0;
return i + 1;
}
9.插入操作
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
Status ListInsert(SqList* L, int i, ElemType e)
{
int k;
if (L->length == MAXSIZE) /* 顺序线性表已经满 */
return ERROR;
if (i<1 || i>L->length + 1)/* 当i比第一位置小或者比最后一位置后一位置还要大时 */
return ERROR;
if (i <= L->length) /* 若插入数据位置不在表尾 */
{
for (k = L->length - 1; k >= i - 1; k--) /* 将要插入位置之后的数据元素向后移动一位 */
L->data[k + 1] = L->data[k];
/*for(k = L->length; k >= i; k--)
{
L->data[k] = L->data[k - 1];
}*/
}
L->data[i - 1] = e; /* 将新元素插入 */
L->length++;
return OK;
}
10. 删除元素操作
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
Status ListDelete(SqList* L, int i, ElemType* e)
{
int k;
if (L->length == 0) /* 线性表为空 */
return ERROR;
if (i<1 || i>L->length) /* 删除位置不正确 */
return ERROR;
*e = L->data[i - 1];
if (i < L->length) /* 如果删除不是最后位置 */
{
for (k = i; k < L->length; k++)/* 将删除位置后继元素前移 */
L->data[k - 1] = L->data[k];
}
L->length--;
return OK;
}
3.测试代码
SqList La; //创建表La
Status i_init; //接受初始化函数的状态
Status i_empty; //接受判空函数的状态
Status i_clear; //接收清空函数的状态
//1.初始化线性表
i_init = InitList(&La);
printf("InitList 的状态:%d\n",i_init); //InitList 的状态:1
//表长
printf("初始化L后:La.length=%d\n", La.length); //初始化L后:La.length=0
//是否为空
i_empty = ListEmpty(La);
printf("初始化后表La是否为空(1为是/0为否)%d\n", i_empty); //初始化后表La是否为空(1为是/0为否)1
printf("--------------------------------------\n");
//2.在表L中插入1~5 (头插法)
for (int j = 1; j <=5; j++)
{
ListInsert(&La, 1, j);
}
printf("在L的表头依次插入1~5后:La.data= "); //在L的表头依次插入1~5后:La.data=
ListTraverse(La); //遍历表中的元素 //5 4 3 2 1
//表长
printf("头插5个元素后,La.length = %d\n", La.length); //头插5个元素后,La.length = 5
//判空
i_empty = ListEmpty(La);
printf("表La是否为空(1为是/0为否):%d\n", i_empty); //表La是否为空(1为是/0为否):0
printf("--------------------------------------\n");
//3.清空表
i_clear = ClearList(&La);
printf("清空后的ClearList的状态:%d\n", i_clear); //清空后的ClearList的状态:1
i_empty = ListEmpty(La);
printf("表La是否为空(1为是/0为否):%d\n", i_empty); //表La是否为空(1为是/0为否):1
//表长
printf("清空后表La的长度,La.length = %d\n", La.length); // 清空后表La的长度,La.length = 0
printf("--------------------------------------\n");
//4.在表中插入1~10 (尾插法)
for (int j = 1; j <= 10; j++)
{
ListInsert(&La, j, j);
}
//遍历
printf("使用尾插法,插入1~10后,La.data = "); //使用尾插法,插入1~10后,La.data =
ListTraverse(La); //1 2 3 4 5 6 7 8 9 10
//表长
printf("尾插10个元素后,La.length = %d\n", La.length); //尾插10个元素后, La.length = 10
//判空
i_empty = ListEmpty(La);
printf("表La是否为空(1为是/0为否):%d\n", i_empty); //表La是否为空(1为是 / 0为否):0
printf("--------------------------------------\n");
//5.查找元素操作
ElemType e;
GetElem(La, 5, &e); //按位查找
printf("第5个元素的值为:%d\n", e); //第5个元素的值为:5
for (int j = 10; j <= 11; j++)
{
int k = LocateElem(La, j); //按值查找
if (k)
printf("第%d个元素的值为%d\n", k, j);
else
printf("没有值为%d的元素\n", j);
}
//第10个元素的值为10
//没有值为11的元素
printf("--------------------------------------\n");
//6.删除操作
Status i_delete;
printf("删除前表长:La.length = %d\n", La.length);
//删除前遍历
printf("删除前遍历: ");
ListTraverse(La);
//删除第5个数据
int j = 5;
ListDelete(&La, j, &e);
//删除后遍历
printf("删除后遍历: ");
ListTraverse(La);
printf("删除后表长:La.length = %d\n", La.length);
/*删除前表长:La.length = 10
删除前遍历: 1 2 3 4 5 6 7 8 9 10
删除后遍历: 1 2 3 4 6 7 8 9 10
删除后表长:La.length = 9 */
4. 复合操作(由基础操作结合组成)
4.1 实现两个线性表A与B的并集操作
原理:将存在在集合B,但不存在在集合A的数据元素,插入到集合A中。
/*将所有的在线性表Lb中但不在La中的数据元素插入到La中*/
void unionL(SqList* La, SqList Lb)
{
int La_len, Lb_len, i;
ElemType e; /*声明与La和Lb相同的数据元素e*/
La_len = ListLength(*La); /*求线性表的长度 */
Lb_len = ListLength(Lb);
for (i = 1; i <= Lb_len; i++)
{
GetElem(Lb, i, &e); /*取Lb中第i个数据元素赋给e*/
if (!LocateElem(*La, e)) /*La中不存在和e相同数据元素*/
ListInsert(La, ++La_len, e); /*插入*/
}
}
//7.求并集操作
//构造一个有10个数的Lb
SqList Lb;
Status i_insert;
i_init = InitList(&Lb);
for (int j = 6; j <= 15; j++)
i_insert = ListInsert(&Lb, 1, j);
unionL(&La, Lb);
printf("依次输出合并了Lb的La的元素:"); //依次输出合并了Lb的La的元素:
ListTraverse(La); //1 2 3 4 6 7 8 9 10 15 14 13 12 11
5. 练习题及答案
1.从顺序表中删除具有最小值的元素(假设唯一)并由函数返回被删元素的值。空出的位置由最后一个元素填补,若顺序表为空,则显示出错误信息并退出运行。
Status Del_Min(SqList* L, ElemType* value)
{
//判断合法性
if (L->length == 0)
{
return ERROR;
}
//假设位序为一的元素值最小
*value = L->data[0];
int pos = 0;
//遍历L表中的元素与第一个元素相比,让value记忆当前具有最小值的元素
for (int i = 1; i < L->length; i++)
{
if (L->data[i] < *value)
{
*value = L->data[i];
pos = i;
}
}
//空出的位置由最后一个元素填补
L->data[pos] = L->data[L->length - 1];
L->length--;
return OK;
}
程序运行结果为: