数据结构---线性表
线性表(Linear List)
由n(n>=0)个数据元素(结点)a1,a2,...an组成的有限序列
~其中数据元素的个数为表的长度
~n=0,称为空表
~将非空的线性表记作:(a1,a2,...an)
!表中元素具有相同特性,数据元素的关系是线性关系
其中非空线性表有且仅有一个开始结点a1,没有直接后继
有且只有一个终端结点an,没有直接前趋,其余内部结点有且仅有一个直接前趋和直接后继。
一元多项式的运算:
pn(x)=p0+p1x+p2x2+...+pnxn
线性表p=(p0,p1,p2...pn)每一项的指数i隐含在系数pi的序号里
相当于数组的顺序存储,但是如果是稀疏多项式,因为上面多项式的次数和前面的系数相同也是等于开辟空间的个数,若很多项前面的系数为零,多项式的次数又高,就会造成空间的浪费,由此可以改变元素的设定,对于每一项,可用(系数, 指数)唯一确定。
例如:多项式的加法
-
创建一个新数组c
-
分别从头遍历比较a和b的每一项
指数相同,对应系数相加,其和不为零,则在c中增加一个新项
指数不相同,将指数较小的项复制在c中
-
一个多项式已经遍历完毕,将另一个剩余项以此复制在c中
由于顺序存储结构空间分配不灵活,运算的空间复杂度高,故用链式存储结构
抽象数据类型线性表的类型定义
ATD List{
数据对象:D={ai|ai属于Elemset,(I=1,2,...n,n>=0)}
数据关系:R={<ai-1,ai>ai-1,ai属于D}
基本操作:
lnitList(&L);...
}ATD List
基本操作例如:
Listinsert(&L,i,e)
初始条件:线性表L已存在,且1<=i<=ListLength(L)+1
操作结果:在 L中第1个位置之前插入新的数据元素 e, L的长度加1。
其中&L是引用作为参数来传递数值,添加元素后的表和原来的表是同一个。
(1)抽象数据类型仅是一个模型的定义,并不涉及模型的具体实现,因此这里描述中所涉及的参数不必考虑具体数据类型。在实际应用中,数据元素可能有多种类型,到时可根据具体需要选择使用不同的数据类型。
(2) 上述抽象数据类型中给出的操作只是基本操作,由这些基本操作可以构成其他较复杂的操作。
线性表的存储结构
在计算机内,两种基本的存储结构:顺序存储结构,链式存储结构
线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素, 这种表示也称作线性表的顺序存储结构或顺序映像。通常, 称这种存储结构的线性表为顺序表(Sequential List)。
其特点是,逻辑上相邻的数据元素, 其物理次序也是相邻的。
-
依次存储,地址连续——中间没有空出的存储单元。
知道某个元素的存储位置可以计算其他元素的存储位置,故是一种随机存取的存储结构。
一般来说,线性表的第i个数据元素ai的存储位置为:LOC(ai)=LOC(a1)+l(i-1)*l(每个元素占l个存储单元)
顺序表(元素)1.地址连续2.依次存放3.随机存取4.类型相同和数组(元素)特点相同,因此,通常都用数组来描述数据结构中的顺序存储结构。
线性表长度可变,数组长度不可动态定义,故用一变量表示顺序表的长度属性。
define LIST_INST_SIZE 100 //顺序表可能达到的最大长度
typedef struct{
Elem Type elem[LIST_INIT_SIZE]; //存储空间的基地址
int length; //当前长度
}SqList; //顺序表的结构类型为SqList
其中Elem Type可以是任意类型既可以是基本数据类型,如int、float、char等,也可以是构造数据类型,如struct结构体类型。存储空间的地址可以由数组表示(静态分配)也可以由指针变量表示(动态分配)。
C语言中的动态分配
C++中的动态分配
顺序表中基本操作的实现
-
初始化(参数用引用)
顺序表的初始化操作就是构造一个空的顺序表。
为顺序表L动态分配一个预定义大小的数组空间,使elem指向这段空间的基地址。
将表的当前长度设为0。
2.取值
取值操作是根据指定的位置序号i, 获取顺序表中第i个数据元素的值。
由于顺序存储结构具有随机存取的特点,可以直接通过数组下标定位得到。
注意:逻辑位序和物理位序相差1
3.查找
查找操作是根据指定的元素值e, 查找顺序表中第1个与e相等的元素。若查找成功,则返回该元素在表中的位置序号;若查找失败,则返回0。
当在顺序表中查找一个数据元素时,其时间主要耗费在数据的比较上, 而比较的次数取决于被查元素在线性表中的位置。
在查找时,为确定元素在顺序表中的位置, 需和给定值进行比较的数据元素个数的期望值称为查找算法在查找成功时的平均查找长度 (Average Search Length, ASL)。
相当于求期望,设pi是查找第i个元素的概率,Ci是为找到表中其关键字与给定值相等的第i个记录时,
和给定值已进行过比较的次数。
ASL=P1+2P2+...+nPn,假设每个记录查找的概率相等:Pi=1/n
=(1+2+...+n)/n=n(n+1)/2
=(n+1)/2
-
插入
线性表的插入操作是指在表的第i个位置插入一个新的数据元素 e, 使长度为 n 的线性表变成长度为n+l 的线性表。
需要注意:
判断插入位置i是否合法(i 值的合法范围是1<=i<=n+ I),若不合法 则返回 ERROR。
判断顺序表的存储空间是否已满,若满则返回ERROR
- 插入位置在最后:a[n+1]=e,无需移动(特别快)
- 插入位置在中间:将第n个至第i个位置的元素依次向后移动一个位置,空出第i个位置,a[i]=e
- 插入位置在最前面:将第n个至第1个位置的元素依次向后移动一个位置,a[1]=e,全部后移(特别慢)
Ems为在长度为n的线性表中插入一个元素时所需移动元素次数的期望值 (平均次数)
pi是在第 i个元素之前插入一个元素的概率pi=1/n+1,n-i+1是移动的次数,化简得n/2,平均时间复杂度O(n)
-
删除
线性表的删除操作是指将表的第i个元素删去, 将长度为n的线性表变成长度为n-1 的线性表
- 删除位置在最后:直接删除a[n],length-1,无需移动,特别快
- 删除位置在中间:删除a[i-1],将第i+1个元素至第n个元素依次向前移动一个位置,length-1
- 删除位置在最前面:删除a[0],将第二个元素至第n个元素依次向前移动一个位置,length-1,将表中n-1个元素全部前移,特别慢
需要注意:
判断删除位置i是否合法(i 值的合法范围是1<=i<=n),若不合法 则返回 ERROR。
Edel为在长度为n的线性表中删除一个元素时所需移动元素次数的期望值(平均次数)
设pi是删除第i个元素的概率1/n,删除第i个元素需要移动的次数为n-i,化简为:n-1/2,平均时间复杂度为O(n).
实现代码
#include<iostream>
using namespace std;
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define maxsize 100
typedef int ElemType;
typedef int Status;
typedef struct
{
int elem[maxsize];
int length;
}SqList;
Status InitList(SqList &L)
{
L.elem == new ElemType[maxsize];
if (!L.elem)exit(OVERFLOW);
L.length = 0;
L.elem[0] = 1;
L.elem[1] = 2;
L.elem[2] = 3;
L.elem[3] = 4;
L.elem[4] = 5;
return OK;
}
int LocateElem(SqList L, ElemType e)
{
int i;
for (i = 0; i < L.length; i++)
if (e == L.elem[i])
return i + 1;
return 0;
}
int ListInsert(SqList& L, int i, ElemType e)
{
int j;
if (i<1 || i>L.length + 1)
return ERROR;
for (j = L.length - 1; j >= i - 1; j--)
L.elem[j + 1] = L.elem[j];
L.elem[i - 1] = e;
++L.length;
return OK;
}
int ListDelete(SqList& L, int i)
{
int j;
if (i<1 || i>L.length )
return ERROR;
for(j=i;j<=L.length-1;j++)
L.elem[j -1] = L.elem[j];
--L.length;
return OK;
}
int main()
{
int i;
int a;
SqList L;
InitList(L);
LocateElem(L, 1);
ListInsert(L, 1, 5);
cout << "添加后线性表为:" << L.elem << endl;
ListDelete(L, 3);
cout << "删除后线性表为:" << L.elem << endl;
return 0;
}