线性表定义和表示
1.线性表的定义
线性表是具有相同特性的数据元素的有限序列,如下图所示:
上图表示线性表由n(n>=0)个数据元素组成的有限序列。其中数据元素的个数n定义为表的长度;当n=0时为空表;将非空的线性表记作();这里的数据元素
(
)知识一个抽象的符号,其具体含义在不同的情况下可以不同。
注:在同一线性表中的元素必定具有相同特性,数据元素间的关系时线性关系。
2.线性表的基本操作
- InitList(&L) :构造一个空的线性表L;
- DestryList(&L):销毁线性表L;
- ClearList(&L):将线性表L重置为空表;
- ListEmpty(L):判断线性表L是否为空;
- ListLength(L):计算线性表L中元素的个数;
- GetELem(L,i,&e):取线性表L中第i个位置的元素并储存到e中;
- LocateElem(L,e,compare()):查找一个与e满足compare()数据元素的位置,返回L中第一个满足条件的元素位置,若不满足则返回0;
- PriorElem(L,cur_e,&pre_e):cur_e为L中当前的数据元素,且不为第一个,则用pre_e返回它的前驱,否则操作失败,pre_e无意义;
- NextElem(L,cur_e,&next_e):cur_e为L中当前的数据元素,且不为最后一个,则用pre_e返回它的后继,否则操作失败,pre_e无意义;
- ListInsert(&L,i,e):在L的第i个位置之前插入新元素e,L的长度加一;
- ListDelete(&L,i,&e):删除线性表L中的第i个元素,并用e返回其值,L长度减一;
- ListTraverse(&L,visited):依次对线性表中的每个元素都调用visited(),即对线性表遍历。
注:其中如果需要操作线性表L元素的,则用传址操作,即需要取得线性表的地址,之后对地址中的元素进行修改等操作,如果不需要修改元素的则可以直接传值操作,即将线性表中的元素进行拷贝再进行操作。
元素插入操作:
元素删除操作:
3.线性表顺序表示
线性表顺序表示又称顺序存储结构或顺序映像。
顺序存储:把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构。如下图表示:
注:线性表的顺序存储是物理上是储存在一块连续的内存上的,因此如果知道内存首地址,是可以进行计算往后数据的存储位置。
顺序表的存储跟一维数组很相似,只是顺序表的长度是可以变的,数组长度是不可动态定义的;
4.顺序表的定义
#define LiIST_INIT_SIZE 100 //线性表存储空间的初始分配量
typedef struct{
ElemType elem[LiIST_INIT_SIZE];
int length; //当前长度
}SqList;
用以下例子来进行说明:
这是一个多项式,用线性表来存储,其中一个元素包含常数p跟指数e;
#define MAXSIZE 1000 //多项式可能达到的最大长度
typedef struct{ //多项式非零项的定义
float p; //系数
int e; //指数
}Polynomial;
typedef struct{
Polynomial *elem; //存储空间的基地址
int length; //多项式中当前项的个数
}SqList; //多项式的顺序存储结构类型为SqList
5.数组的静态分配和动态分配
静态分配,也就是data[MaxSize]也就是data里头的元素空间是固定的,在初始化的时候就将内存分配好了,无法进行动态的改变。
#define MaxSize 100 //线性表存储空间的初始分配量
typedef struct{
ElemType data[MaxSize];
int length; //当前长度
}SqList;
动态分配,就是动态对数组的空间进行添加,使用指针的方式,在需要添加时往后开辟内存,让指针往后加。
typedef struct{
ElemType *data;
int length; //当前长度
}SqList;
使用函数malloc(m)函数,开辟m字节长度的地址 空间,并返回这段空间的首地址。sizeof(x)运算,计算变量x的长度;
Sqlist L;
L.data = (ElmeType*)malloc(sizeof(ElemType)*MaxSize);
然后在删除元素时,则使用free(p)函数,对指针p所指变量的存储空间进行释放,即彻底删除一个变量。
顺序表的实现
1.顺序表的定义与相关注意
顺序表的逻辑结构下标从1开始依次向后增加,而存储结构是相当于数组般,从0开始往后进行存储,因此逻辑位序和物理位序相差1,如下图所示:
其类型定义:
其中元素是以数组的方式存储的,也就是L.elem[ i ]的样式,而在线性表L中,是存储在一个内存块中的,其表示可如图显示
2.顺序表操作函数定义
添加操作进行在下标为i的位置插入一个元素图解:
具体操作就是借用指针来进行操作,具体如下图所示:
3.代码以及运行结果
test.c
#include "Operation.h"
int main()
{
SqList L;
int i;
int e;
//初始化
InitList_Sq(&L);
if (!InitList_Sq(&L))
{
printf("初始化错误");
return -1;
}
// 插入数据
for (i = 1; i <= 10; i++)
{
ListInsert(&L, i, i);
}
printf("输出顺序表内容:");
// 打印数据
ListPrint(L);
// 查找数据
printf("请输入查找的数据:");
scanf("%d", &e);
if (LocateElem(L, e))
{
printf("查找成功,元素下标为%d \n", LocateElem(L, e));
}
else {
printf("查找失败!\n");
}
// 获取第i位置的元素内容
printf("请输入查找元素下标:");
scanf("%d", &i);
if (GetElem(L, i, &e))
{
printf("查找到第%d个元素为%d \n", i, e);
}
else {
printf("查找失败!\n");
}
// 删除数据
printf("请输入要删除的元素下标:");
scanf("%d", &i);
if (ListDelete(&L, i, &e))
{
printf("删除成功,删除的元素为%d\n", e);
}
else {
printf("删除失败!\n");
}
// 打印数据
ListPrint(L);
// 清空线性表
ClearList(&L);
// 打印数据
ListPrint(L);
// 销毁已存在的线性表
DestroyList(&L);
// 打印数据
ListPrint(L);
return 0;
}
头文件:Operation.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define INFEASIBLE -1
typedef int Status;
typedef int ElemType;
#define MAXSIZE 100
#define LISTINCREMENT 10
typedef struct
{
ElemType* elem;
int length;
}SqList; //定义顺序表类型
//初始化顺序表
Status InitList_Sq(SqList* L);
// 插入数据
Status ListInsert(SqList* L,int i,ElemType e);
// 打印数据
Status ListPrint(SqList L);
// 查找数据
Status LocateElem(SqList L,ElemType e);
// 获取第i位置的元素内容
Status GetElem(SqList L,int i, ElemType* e);
// 删除数据
Status ListDelete(SqList* L,int i,ElemType* e);
// 清空线性表
Status ClearList(SqList* L);
// 销毁已存在的线性表
Status DestroyList(SqList* L);
//判断错误问题
void QuestPrint(int Q);
对函数的实现Operation.c
#include "Operation.h"
//初始化顺序表
Status InitList_Sq(SqList* L) {
L->elem = (ElemType*)malloc(sizeof(ElemType) * MAXSIZE);
if (!L->elem) exit(OVERFLOW);
L->length = 0;
return OK;
}
// 插入数据
Status ListInsert(SqList* L, int i, ElemType e)
{
int* Newbase;
//判断i是否在线性表的长度范围内
if (i >= 1 && i <= L->length + 1)
{
//判断线性表的长度是否还有内存,如果已经初始化的最大内存,自动扩存
if (L->length >= MAXSIZE)
{
Newbase = (int*)malloc((MAXSIZE + LISTINCREMENT) * sizeof(int));
}
//定义两个指针变量,p取得elem数组中的i-1个位置,然后将i-1位置的元素往后移一个位置
//即i-1位置的元素到i位置
int* p, * q;
p = &(L->elem[i - 1]);
for (q = &(L->elem[L->length - 1]); q >= p; --q)
{
*(q + 1) = *q; //将原来是q地址的元素放到q+1地址里头
}
//将e赋值到p的位置里
*p = e;
++L->length;
return OK;
}
else { //i不在L的长度内
return OVERFLOW;
}
}
// 打印数据
Status ListPrint(SqList L)
{
if (L.length) {
//从0开始遍历elem数组,输出线性表长度length个元素
for (int i = 0; i < L.length; i++)
{
printf("%d ", L.elem[i]);
}
printf("\n");
return OK;
}
else {
return INFEASIBLE;
}
}
// 查找数据
Status LocateElem(SqList L, ElemType e)
{
if (L.length)
{
int i = 0;
for (i = 0; i < L.length; i++)
{
if (L.elem[i] == e) {
return i;
break;
}
}
return FALSE;
}
else
{
return INFEASIBLE;
}
}
// 获取第i位置的元素内容
Status GetElem(SqList L, int i, ElemType* e)
{
if (i >= 0 && i < L.length) {
*e = L.elem[i];
return TRUE;
}
else {
return FALSE;
return OVERFLOW;
}
}
// 删除数据
Status ListDelete(SqList* L, int i, ElemType* e)
{
if (i >= 0 && i <= L->length) {
*e = L->elem[i - 1];
int* p;
//p在最后一个地址之前,将后面元素赋给前面一个地址,依次往后移
for (p = &(L->elem[i - 1]); p <= &(L->elem[L->length - 1]); ++p)
{
*p = *(p + 1); //将后面的元素都往前挪一个地址
}
L->length = L->length - 1;
return TRUE;
}
else {
return FALSE;
return OVERFLOW;
}
}
// 清空线性表
Status ClearList(SqList* L)
{
L->length = 0;
printf("已经清空线性表,当前长度为0!\n");
return OK;
}
// 销毁已存在的线性表
Status DestroyList(SqList* L)
{
if (L->elem)
{
free(L->elem);
printf("销毁成功,当前线性表元素为0!\n");
return OK;
}
else {
return INFEASIBLE;
}
}
//判断错误问题
void QuestPrint(int Q)
{
if (Q == 0)
{
printf("出错了!");
}
if (Q == -1)
{
printf("线性表为空,或者不存在!");
}
if (Q == -2)
{
printf("不在查询范围内!");
}
}
运行结果:
顺序表的实现相对比较简单,跟数组的操作类似,差不多就只有插入的操作有些许难理解,其他都比较简单。