广义线性表和顺序线性表
线性表是一种最简单的数据结构,其是一个数据元素的有序集,其特征如下:
1.存在一个唯一的第一元素;存在一个唯一的最后元素;
2.每个中间的元素均有一个前驱和一个后驱,且同一个线性表之中的元素具有相同的特征。
而线性表又可以分为两种类型,顺序结构和链式结构
首先我们建立其抽象类型的定义:
ADT list {
数据对象:D={ai | ai∈Elemset,i=1, 2,...,n, n≥0}
//n为线性表的表长;n=0时的线性表为空表
数据关系:R1={<ai-1,ai>|ai-1,ai∈D,i=2,.....,n}
//i为ai在线性表中的位序
基本操作: ……
ADT list
基本操作有以下几种形式:
第一类结构性操作:
InitList
(&L )//初始化一个线性表
¶
操作结果:构造一个空的线性表
L
DestroyList
( &L )//删除线性表
¶
初始条件:线性表
L
已存在
操作结果:销毁线性表
L
第二类引用操作:
ListEmpty
( L )//判断线性表是否为空
¶
初始条件:
线性表
L
已存在
¶
操作结果:
若
L
为空表,则返回
TRUE
,否则
FALSE
¨
ListLength
( L )//求线性表的长度
¶
初始条件:
线性表
L
已存在
¶
操作结果:
返回
L
中元素个数
PriorElem
( L,
cur_e
, &
pre_e
)
¶
初始条件:
线性表
L
已存在
¶
操作结果:
若
cur_e
是
L
的元素,但不是第一个,则用
pre_e
返回它的前驱,否则操作失败,
pre_e
无定义
NextElem
( L,
cur_e
, &
next_e
)
¶
初始条件:
线性表
L
已存在
¶
操作结果:
若
cur_e
是
L
的元素,但不是最后一个,则用
next_e
返回它的后继,否则操作失败,
next_e
无定义
ListTraverse
(L, visit( ))
//遍历线性表
¶
初始条件:
线性表
L
已存在
Visit() 为某个访问函数
Visit() 为某个访问函数
¶
操作结果:
依次
对
L
的每个元素调用函数
visit()
。一旦
visit( )
失败,则操作失败
第三类操作:加工型操作
ListInsert
( &L,
i
,e )//插入数据元素
¶
初始条件:
线性表
L
已存在
;
1≤i≤LengthList(L)+1;
1≤i≤LengthList(L)+1;
¶
操作结果:
在
L
的第
i
个元素之前
插入新的元素
e
,
L
的长度增
1
ListDelete
(&L,
i
,&e)
¶
初始条件:
线性表
L
已存在
;
1≤i≤LengthList(L);
1≤i≤LengthList(L);
¶
操作结果:
删除
L
的第
i
个元素,并用
e
返回其值,
L
的长度减
1
理解上面的基本的结构后我们通过使用线性表实现复杂的操作:
例子:集合A和B跟别用两个线性表 LA 和 LB 表示,求一个新的集合A=A∪B。
我们可以设想一下其操作过程,继而设定操作步骤:
1. 初始化参数ListLength(LA),ListLength(LB),获取A和B的线性表的长度。
2.从线性表LB中依次察看每个数据元素,GetElem(LB , i , e),依值和设定的条件在线性表LA中进行查访;
LocateElem(LA, e, equal( ))若不存在,则插入到LA后面ListInsert(LA, n+1, e);
1. 初始化参数ListLength(LA),ListLength(LB),获取A和B的线性表的长度。
2.从线性表LB中依次察看每个数据元素,GetElem(LB , i , e),依值和设定的条件在线性表LA中进行查访;
LocateElem(LA, e, equal( ))若不存在,则插入到LA后面ListInsert(LA, n+1, e);
伪代码可进行如下设计:
void union(List &La, List Lb) {
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, equal( )) )
ListInsert(La, ++La_len, e);
// La中不存在和 e 相同的数据元素,则插入之
}
} // union
巳知线性表LA和线性表LB中的数据元素按值非递减有序排列
要求将LA和LB归并为一个新的线性表LC,且LC中的元素仍按值非递减有序排列,LC=LA + LB
要求将LA和LB归并为一个新的线性表LC,且LC中的元素仍按值非递减有序排列,LC=LA + LB
二,线性表
线性表就是用一组地址连续的存储单元依次存放线性表中的数据元素:以“存储位置相邻”表示有序对<ai-1,ai>
即:LOC(ai) = LOC(ai-1) + C
C是一个数据元素所占存储量。所有数据元素的存储位置均取决于第一个数据元素的存储位置
LOC(ai) = LOC(a1) + (i-1)×C
即:LOC(ai) = LOC(ai-1) + C
C是一个数据元素所占存储量。所有数据元素的存储位置均取决于第一个数据元素的存储位置
LOC(ai) = LOC(a1) + (i-1)×C
typedef struct {
ElemType *elem; // 存储空间基址
int length; // 当前长度
int listsize; // 当前分配的存储容量
// (以sizeof(ElemType)为单位)
} SqList; // 俗称顺序表
首先对线性表进行初始化操作:
Status InitList_Sq( SqList& L ) {
// 构造一个空的线性表
L.elem = (ElemType*) malloc
(LIST_INIT_SIZE sizeof (ElemType));
if (!L.elem) exit(OVERFLOW);
L.length = 0;
L.listsize = LIST_INIT_SIZE;
return OK;
} // InitList_Sq
从表格中移动并插入数据:
Status ListInsert_Sq(SqList &L, int i, ElemType e) {
if (i < 1 || i > L.length+1)
return ERROR; // 步骤1:位置不合法
q = &(L.elem[i-1]); //步骤2:q 指示插入位置
for (p = &(L.elem[L.length-1]); p >= q; --p)
*(p+1) = *p; //步骤3:元素依次后移
*q = e; // 步骤4:插入e
++L.length; // 步骤5:表长加1
return OK;
} // ListInsert_Sq
然而,若假定在线性表中任何一个位置上进行插入的概率都是相等的,则移动元素的期望值为:n/2;
当存出现存储空间已经占用满了的情况,可以进行以下处理,首先按照增量动态开辟内存:
if (L.length >= L.listsize) {
// 当前存储空间已满,增加分配
newbase = (ElemType *)realloc(L.elem,
(L.listsize+LISTINCREMENT)*sizeof (ElemType));
if (!newbase) exit(OVERFLOW);
// 存储分配失败
L.elem = newbase; // 新基址
L.listsize += LISTINCREMENT; // 增加存储容量
}
若我们想删除某个元素时可以进行一下操作,直接将前面的覆盖就可以了,最后释放或者是保留最后一个位置的内存,若假定在线性表中任何一个位置上进行删除的概率都是相等的,则移动元素的期望值为:(n-1)/2:
Status ListDelete_Sq (SqList &L, int i, ElemType &e) {
// 步骤1:位置是否合法
if ((i < 1) || (i > L.length)) return ERROR;
p = &(L.elem[i-1]); //步骤2:初始化指针
e = *p; //步骤3:赋给 e
q = L.elem+L.length-1; // 表尾的位置
for (++p; p <= q; ++p) *(p-1) = *p;
// 步骤4:被删除元素之后的元素左移
--L.length; // 步骤5:表长减1
return OK;
} // ListDelete_Sq