线性表
线性结构的特点:
在数据元素的非空有限集中
①存在唯一的一个被称作“第一个”的数据元素
②存在唯一的一个被称作“最后一个”的数据元素
③除第一个外,集合中的每个数据元素均只有一个前驱
④除最后一个外,集合中的每个数据元素均只有一个后继
线性表:是由n个数据元素所组成的有限序列。
在稍复杂的线性表中,一个数据元素可以由若干个数据项组成。在这种情况下,常把数据元素称为记录,含有大量记录的线性表又称文件。
线性表中元素的个数n>(n>=0)定义为线性表的长度,n=0时称为空表。在非空表中的每一个元素都有一个确定的位置,如a1是第一个数据元素,an是最后一个数据元素,ai是第i个数据元素,称i为数据元素ai在线性表中的位序。
主要基本操作还包括:(详细信息见P19)
InitList( &L )
DestroyList( &L )
ListEmpty ( L )
ClearList( &L )
ListLength ( L )
GetElem ( L,i,&e )
LocateElem ( L,e,compare() )
PriorElem ( L,cur_e,&pre_e )
NextElem ( L,cur_e,&next_e )
ListInsert ( &L,i,e )
ListDelete ( &L,i,&e )
ListTraverse ( L,visit() )
一些复杂操作,如合并、拆分、复制等等均可以通过上述简单操作实现。
1. 顺序表示:
- 定义:用一组地址连续的存储单元依次存储线性表的数据元素。
- 特点:
实现逻辑上相邻—物理地址相邻
实现随机存取 - 实现:可用C语言的一维数组实现
线性表顺序存储结构表示
# define LIST_INIT_SIZE 100
# define LISTINCREMENT 10
typedef struct {
Elemtype *elem ; // 基址
int length ; // 表长,初始为 0
int listsize ;// 表存储容量
} 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 ;
}
在线性表的顺序存储结构中,由于逻辑上相邻的数据元素在物理位置上也是相邻的,因此,除非i=n+1,否则必须移动元素才能反应这个逻辑关系的变化。
2. 插入
- 定义:线性表的插入是指在第 i(1<=i <= n+1)个元素之前插入一个新的数据元素x,使长度为 n 的线性表
Status ListInsert_Sq ( Sqlist &L ,int i ,ElemType e ) {
if ( i < 1 || i > L.length +1 ) return ERROR ;
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 ;
}
q = & ( L.elem[i-1]) ;
for ( p = & L.elem[L.length-1] ;p >= q ;- - p )
* (p+1) = * p ; // 后移元素
* q = e ; // 插入新元素
++L.length ;
return OK ;
}
算法时间复杂度:
时间主要花在移动元素上,而移动元素的个数取决于插入元素位置。
i=1, 需移动 n 个元素;
i=n+1, 需移动 0 个元素;
需将第i至第n, 共(n-i+1)个元素后移
假设pi是在第 i 个元素之前插入一个新元素的概率
则长度为 n 的线性表中插入一个元素所需移动元素次数的期望值为: Eis = ∑ pi (n – i + 1)
设在任何位置插入元素等概率, pi = 1/n+1
Eis = ∑ (n – i + 1) = n/2;
O(n)
3. 删除
定义:线性表的删除是指将第 i(1<=i <= n)个元素删除,使长度为 n 的线性表变成长度为 n-1 的线性表。
Status ListDelete_Sq ( Sqlist &L ,int i ,ElemType &e ) {
if ( i < 1 || i > L.length ) return ERROR ;
p = & ( L.elem[i-1]) ;
e = * p ; // 取第 i 个元素的值
q =& L.elem[ L.length–1] ;
for ( ++p;p <= q;++p )
* (p - 1) = * p ; // 前移
-- L.length;
return OK ;
}
4. 算法时间复杂度
时间主要花在移动元素上,而移动元素的个数取决于删除元素位置。
i=1, 需移动n - 1个元素;
i=n, 需移动0个元素;
需将第i+1至第n共(n-i)个元素前移
假设qi是删除第 i 个元素的概率
则长度为 n 的线性表中删除一个元素所需移动元素次数的期望值为: Edl = ∑ qi (n – i)
设删除任何位置的元素等概率, qi = 1/n;
Edl = ∑ (n – i) = (n-1)/2
O(n)
已知顺序表A,写一算法将其倒置
void reverse(SqList &A) //顺序表的就地逆置
{
for(i=0, j=A.length-1; i<j; i++, j--)
A.elem[i]<->A.elem[j];
}
线性表的顺序存储结构的优缺点
优点:
- 可随机存取表中任意数据元素
例 L.elem[i-1]表示第 i 个数据元素 - 直接可获取线性表的长度
例 L.length表示线性表长度
缺点:
- 数据元素的插入、删除相对麻烦