线性表
什么是线性表
线性表是一种最简单的线性结构。
什么是线性结构?简单地说,线性结构是一个数据元素的有序(次序)集合。它有四个基本特征:
在数据元素的非空有限集中,
- ①存在惟一的一个被称做”第一个”的数据元素;
- ②存在惟一的一个被称做”最后一个”的数据元素;
- ③除第一个之外,集合中的每个数据元素均只有一个前驱;
- ④除最后一个之外,集合中的每个数据元素均只有一个后继。
这里的”有序”仅指在数据元素之间存在一个”领先”或”落后”的次序关系,而非指数据元素”值”的大小可比性。比较典型的线性结构:线性表、栈、队列、串等。
2.1 线性表的类型定义
2.1.1 线性表的定义
线性表(linear_list)是n个数据元素的有限序列,记作(a1, a2, …, ai, …, an)。
线性表的数学模型(形式定义):
含有n个数据元素的线性表是一个数据结构
LinearList=(A, R)
其中:
A={ai| ai∈ElemType , 1≤i≤n, n≥0}
R={r}
r={<ai, ai+1> | 1≤i≤n -1}
说明:
①线性表的数据元素可以是各种类型(整、实、记录类型等)
typedef int ElemType;
typedef char ElemType;
等;
②同一线性表中的数据元素必须具有相同的特性,属同一类型;
③关系r是一个有序偶对的集合,即对于非空的线性表(a1, a2, …, ai-1, ai, ai+1, …, an), ai-1 领先于ai,表示了数据元素之间的相邻关系,称ai-1是ai的直接前驱,ai是ai-1的直接后继;
④序列中数据元素的个数 n 定义为线性表的表长,n=0 时的线性表被称为空表;
⑤称i为数据元素在线性表中的位序。
2.1.2 线性表的抽象数据类型
ADT List {
Data:
一个线性表L定义为L=(a1,a2,…,an),当L=( )时定义为一个空表。
Operation:
InitList(& L) //初始化线性表L,即把它设置为一个空表
DestroyList(&L) //销毁顺序线性表L
ClearList (& L) //将L重置为空表
ListEmpty(L) //判断L是否为空,若空则返回true,否则返回false
ListLength (L) //返回L中数据元素的个数
GetElem(L, pos) //返回线性表第pos个数据元素的值
LocateElem(&L,e,compare());
//返回L中第1个与e满足关系compare()的数据元素的位序。若这样的数据元素不存在,则返回0
ListInsert (&L, pos, e)
//在L的pos位置插入e,线性表L长度加1
ListDelete (&L, pos)
//删除L的第pos个数据元素
ListTraverse(L, visit( ))
//遍历线性表L,依次对L的每个数据元素调用函数visit( )
CreateList(&L, n, visit ())
//创建有n个元素的线性表
}
2.1.3 操作举例
例:假设利用两个线性表La和Lb分别表示两个集合A和B,求一个新的集合A=A∪B。
算法:
①取得Lb中的1个元素;
②在La中查找这个元素;
③若不存在:插入La中;若存在,取Lb中下一个元素,重复 ①、②、③,直到取完Lb的每个元素。
demo:
void UnionList(SqList &la,SqList lb)
{
int Lb_len=ListLength(lb);
ElemType e;
for (int i=1;i<=Lb_len;++i)
{
e=GetElem(lb,i);
if (!LocateElem(la, e, equal))
ListInsert(la,la.length+1,e);
}
}
int equal(ElemType e1,ElemType e2)
{
if (e1.id==e2.id)
return 1;
return 0;
}
算法的时间复杂度为O(La.size×Lb.size)。
2.2 线性表的顺序表示和实现
2.2.1 线性表的顺序表示
线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素,使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。 采用顺序存储结构的线性表通常称为顺序表。
表示为:A=(a1,a2,… ,ai,ai+1,… ,an )
第i个元素的地址
假设线性表中有n个元素,每个元素占k个单元,第一个元素的地址为loc(a1),则可以通过如下公式计算出第i个元素的地址loc(ai):
loc(ai) =loc(a1)+(i-1)×k
其中loc(a1)称为基地址。
用C++语言描述的顺序表类型如下所示:sqlist.h
#include <iostream>
#include <cstdlib>
using namespace std;
const int LIST_INIT_SIZE=100; //线性表存储空间的初始分配量
const int LISTINCREMENT=10; //线性表存储空间的分配增量
struct Node //定义结点(数据元素)的类型
{
int id;
int age;
};
typedef Node ElemType; //声明结点的类型名为ElemType
struct SqList //定义线性表的存储结构
{
ElemType* elem;
int length;
int listsize;
};
bool InitList(SqList &L);
void DestroyList(SqList &L);
void ClearList(SqList &L);
bool ListEmpty(SqList L);
int ListLength(SqList L);
ElemType& GetElem(SqList L, int pos);
int LocateElem(SqList& L, ElemType e,int (*compare)(ElemType, ElemType));
bool ListInsert(SqList& L, int pos, ElemType e);
bool ListDelete(SqList& L, int pos);
void ListTraverse(SqList L, void (*visit)(ElemType&));
bool CreateList(SqList& L, int n, void (*visit)(ElemType&));
2.2.2 线性表顺序存储结构上的基本运算sqlist.cpp
⑴ 初始化线性表
bool InitList(SqList &L)
{
L.elem=new ElemType[LIST_INIT_SIZE];
if (!L.elem)
{
cerr<<"Memory allocation failure!"<<endl;
exit(1);
}
L.length=0;
L.listsize=LIST_INIT_SIZE;
return true;
}
⑵ 销毁顺序线性表L
void DestroyList(SqList &L)
{
delete[ ] L.elem;
L.elem=NULL;
L.length=0;
L.listsize=0;
}
⑶ 删除线性表的所有元素,使之成为空表在顺序存储方式下实现此操作只要将线性表的长度置0即可。
void ClearList(SqList &L)
{
L.length=0;
}
⑷ 检查线性表是否为空
bool ListEmpty(SqList L)
{
return L.length==0;
}
⑸ 获取表元素的个数
int ListLength(SqList L)
{
return L.length;
}
⑹ 得到线性表中指定序号的元素
ElemType& GetElem(SqList L, int pos)
{
if (pos<1||pos>L.length)
{
cerr<<"pos is out range!"<<endl;
exit(1);
}
return L.elem[pos-1];
}
⑺ 查找线性表中满足给定条件的元素的位序
int LocateElem(SqList& L, ElemType e, int (*compare)(ElemType, ElemType))
{
for (int i=0; i<L.length; ++i)
if (compare(L.elem[i],e)==1)
return i+1;
return 0;
}
如要查找与e的相等(某对应成员的值相等)的元素,则compare()的实参可定义如下:
int equal(ElemType e1, ElemType e2)
{
if (e1.id==e2.id)
return 1;
return 0;
}
算法的时间复杂度:
时间耗费主要在比较元素的次数上,而次数取决于待查找元素的位置。
最好情况:
compare(L.elem[0], e)==1 O(1)
最坏情况:
compare(L.elem[n-1], e)==1 O(n)
⑻ 向线性表中的指定位置插入一个元素
原表:a1,a2,…,ai-1,ai,ai+1,…,an
插入后的表:a1,a2,…,ai-1,b,ai,ai+1,…,an
①ai-1和ai逻辑关系发生变化;
②因逻辑相邻的数据元素物理位置上也相邻,因此,除非i=n+1,否则必须将原表中位置n, n-1,…, i上的结点,依次后移到位置n+1,n,…,i+1上,空出第i个位置,在该位置上插入新结点b。
bool ListInsert(SqList& L, int pos, ElemType e)
{
if (pos<1||pos>L.length+1)
{
cerr<<"pos is out range!"<<endl;
return false;
}
if(L.length>=L.listsize) //当前存储空间已满,增加分配
{
ElemType *newbase=new ElemType[L.listsize+LISTINCREMENT];
if (!newbase)
{
cerr<<"Memory allocation failure!"<<endl;
exit(1);
}
for(int i=0; i<L.length; ++i)
newbase[i]=L.elem[i];
delete[] L.elem;
L.elem=newbase;
}
for (int i=L.length-1; i>=pos-1; --i)
L.elem[i+1]=L.elem[i];
L.elem[pos-1]=e;
++L.length;
return true;
}
⑼ 从线性表中删除指定位置的元素
原表: a1,a2,…,ai-1,ai,ai+1,…,an
删除后的表:a1,a2,…,ai-1,ai +1,ai+2,… ,an
①ai-1 和 ai+1逻辑关系发生变化
②需移动元素:
i=n 只删 an即可
1≤i≤n -1 将ai+1 ~an前移
demo:
bool ListDelete(SqList& L, int pos)
{
if (pos<1||pos>L.length)
{
cerr<<"pos is out range!"<<endl;
return false;
}
for (int i=pos-1; i<=L.length-2; ++i)
L.elem[i]=L.elem[i+1];
--L.length;
return true;
}
⑽ 遍历线性表:遍历一个线性表就是从线性表的第一个元素起,按照元素之间的逻辑顺序,依次访问每一个元素,并且每个元素只被访问一次,直到访问完所有元素为止。
void ListTraverse(SqList L, void (*visit)(ElemType&))
{
for (int i=0; i<L.length; ++i)
visit(L.elem[i]);
cout<<endl;
}
如要依次输出每个元素的值,visit()的实参可定义如下:
void Print(ElemType& e)
{
cout<<"id:"<<e.id<<" age:"<<e.age<<endl;
}
⑾ 创建有n个元素的线性表
bool CreateList(SqList& L, int n, void (*visit)(ElemType&))
{
if (n>L.listsize)
{
delete[] L.elem;
L.elem=new ElemType[n+LISTINCREMENT];
if (!L.elem)
return false;
}
for (int i=0; i<n; ++i)
{
visit(L.elem[i]);
++L.length;
}
return true;
}
2.2.3 顺序表应用例
例:已知线性表La和Lb中的数据元素按值非递减有序排列,现要将La和Lb归并为一个新的线性表Lc,且Lc中的数据元素仍按值非递减排列。
#include "sqlist.h"
SqList MergeList_Sq (SqList La, SqList Lb);
void Print(ElemType& e)
{
cout<<"id:"<<e.id<<" age:"<<e.age<<endl;
}
int compare (ElemType e1,ElemType e2)
{
if (e1<=e2)
return 1;
return 0;
}
SqList MergeList_Sq(SqList La, SqList Lb)
{
SqList Lc;
InitList(Lc);
int i, j, k, La_len, Lb_len;
ElemType ai, bj;
i=j=1, k=0;
La_len=ListLength(La);
Lb_len=ListLength(Lb);
while ((i<=La_len)&&(j<=Lb_len))
{
ai=GetElem(La, i);
bj=GetElem(Lb, j);
if (compare(ai, bj)==1)
{
ListInsert(Lc, ++k, ai);
++i;
}
else
{
ListInsert(Lc, ++k, bj);
++j;
}
}
while (i<=La_len)
{
ai=GetElem(La, i++);
ListInsert(Lc, ++k, ai);
}
while (j<=Lb_len)
{
bj=GetElem(Lb, j++);
ListInsert(Lc, ++k, bj);
}
return Lc;
}
int main()
{
SqList la,lb;
ElemType a1,a2,a3,b1,b2,b3,b4;
a1.id=1,a1.age=18;
a2.id=3,a2.age=20;
a3.id=6,a3.age=16;
b1.id=2,b1.age=32;
b2.id=4,b2.age=12;
b3.id=5,b3.age=22;
b4.id=7,b4.age=15;
InitList(la);
InitList(lb);
ListInsert(la,1,a1);
ListInsert(la,2,a2);
ListInsert(la,3,a3);
ListInsert(lb,1,b1);
ListInsert(lb,2,b2);
ListInsert(lb,3,b3);
ListInsert(lb,4,b4);
ListTraverse(la,Print);
ListTraverse(lb,Print);
SqList lc=MergeList_Sq(la,lb);
ListTraverse(lc,Print);
}