线性表的定义 :
简而言之 : 0 个 或 多个元素(类型相同)的有限序列( 有顺序 ) , 第一个元素无前驱 , 最后一个元素无后继 , 其他元素 与有唯一的前驱 和 唯一的后继
数学语言定义 : 若将线性表记为 ( a1 , a2 , ..... , ai - 1 , ai , ai+1 , ... , an) , 则表中 ai-1 领先于 ai , ai 领先于 ai+1
称 ai-1 是 ai 的前驱 , ai+1 是 ai 的后继元素 . 当 i = 1 , 2 ... , n - 1 时候 , ai 有且仅有 一个 直接后
继 , 当 i = 2 ,......., n 时候 , ai 有且仅有一个直接的前驱。
线性表的长度 : 线性表的元素的个数 n( n>=0 ) , 当 n = 0 时候 我们称之为空表
线性表的ADT :
注意 : 这只是 抽象 层面 来说 , 也就是说实现方式尽管 有 顺序表 和 连表等实现方式 , ADT仅仅抽取他们的相同
处 且 基本具有的操作 , 具体实现时候 , 可以添加Data 数据 , 可以 扩充 操作 或者添加 辅助方法( 抽取
相同代码 )。 对于实际问题中更复杂的操作 , 可以使用ADT中的基本操作来组合完成。
定义如下:
ADT 线形表 ( List ) Data : 下一层数据类型 + 描述 线性表的数据对象的序列 {a1,a2,a3,......, an} , 每个元素均为DataType , 其中 , 除了元素 a1 外 , 每一个 元素都只有唯一的前驱元素 , 除了an外 , 每个元素都只有唯一的后继元素 。 元素之间的关系是一对一的关 系。 Operation: 操作 + 描述 (规定 : 提前说明 , 我们使用的编号 均从 0 开始 , 操作返回 0 代表成功 , -1 代表失败) InitList(*L) : 初始化操作 , 给L 指针指向的List类型开辟能容纳序列的空间 ListEmpty(L) : 若线性表为空 , 返回true else false ClearList(*L) : 将线性能表清空 GetElem(L , i ,*e) : 将线性表L中的第i个元素通过传出参数e取出 , 返回值 0 代表的 成功 , -1 代表错误 LocateElem(L,e ) : 将线形表L 中查找 第一个匹配到的e , 成功返回 编号位置 , 失败返回-1 ListInsert(*L , i , e) : 在线性表第 i 个元素 的前面 插入一个元素 , 成功返回 0 失败返回 -1 ListDelete(*L , i ,*e ): 删除线性表中的第i个位置的元素 , 并用e返回其值 ListLength(L) : 返回线性表中的元素的个数 : endADT
线形表的顺序存储结构:
ADT -> 物理层面 -> 语言实现( C 语言实现):
指的是使用一段连续的存储单元依次存储线性表的数据元素。
sqList 结构体 定义在头文件当中:
#ifndef __SEQ_LIST_H #define __SEQ_LIST_H #define MAXSIZE 20 /*假设顺序表的容量为20*/ #define TRUE 0 /*我们只使用TRUE 和 FALSE 两个量表示成功和失败就行了 , 但是注意TRUE是0*/ #define FALSE -1 typedef int ElemType; /*假设ElemType 类型为int*/ typedef int BOOL; /*BOOL 表示两个量 : TRUE | FALSE */ /*结构体的声明放在*/ typedef struct { ElemType data[MAXSIZE]; int length; /*扩充了ATD的DATA,其表示指向序列最后一个元素的下一个位置*/ }sqList; /*函数的声明放在这个位置 */ extern void InitList(sqList * L); extern BOOL ListEmpty(sqList L); extern void ClearList(sqList * L); extern int LocateElem(sqList L , ElemType e); extern int ListLength(sqList L); extern BOOL ListInsert(sqList * L , int i , ElemType e); extern BOOL ListDelete(sqList * L , int i , ElemType *e); extern int GetElem(sqList L , int i , ElemType * e); #endif
sqList 操作定义在 .c 文件当中 :
/************************************************* Function: InitList Description: 初始化顺序表的长度为 0 Output: *L : 线性表发生变动 Return: 空 TRUE | 非空FALSE (TRUE :0 FALSE:-1) *************************************************/ void InitList(sqList * L) { L->length = 0; } /************************************************* Function: LisEempty Description: 返回顺序表是否为空 , O(1) 判断 L->length 是否为0即可 Input: L : 线性表结构 Return: 空 TRUE | 非空FALSE (TRUE :0 FALSE:-1) *************************************************/ BOOL ListEmpty(sqList L) { return L.length ? FALSE : TRUE; } /************************************************* Function: ClearList Description: 将顺序表清空 O(1) 将L->length 置为0即可 Output: *L : 线性能表发生变动 Return: void *************************************************/ void ClearList(sqList * L) { L->length = 0; } /************************************************* Function: LocateElem Description: 获取第一次出现指定元素的位置 O(n) 开始遍历判断是否相等 返回下下标位置 Input: L : 顺序表结构 e : 指定的元素 Return: int 具体位置 , 没找到返回-1 *************************************************/ int LocateElem(sqList L , ElemType e) { int i = 0; for(;i < L.length ; ++i) { if(L.data[i] == e) return i; } return -1; } /************************************************* Function: ListLength Description: 获取顺序表的长度 O(1) 直接返回 L.length即可 Input: L 表结构 Return: int 顺序表的长度 *************************************************/ int ListLength(sqList L) { return L.length; } /************************************************* Function: ListInsert Description: 在顺序表的指定位置插入元素 : 时间复杂度O(n) : 时间浪费在移动上面 : 插入位置不合理 , return FALSE 如果 length + 1 > MAXSIZE return FALSE 需要将 i 到 length-1的元素向后移动一个位置 将元素插入指定位置后 , 表的长度 + 1 Input: i : i位置元素的前面进行插入 e : 被插入的元素 Output: *L: 传出参数,表示线性表会被修改 Return: 成功 TRUE | 失败 FALSE *************************************************/ BOOL ListInsert(sqList * L , int i , ElemType e) { int j = 0; if( 0>i || i > L->length || L->length + 1 > MAXSIZE) return FALSE; for(j = i+1 ; j <=L->length ; ++j) L->data[j] = L->data[j-1]; L->data[i] = e; L->length+=1; return TRUE; } /************************************************* Function: ListDelete Description: 删除指定位置的元素 : O(n) 时间也是浪费在移动数据上面 : 如果删除的位置不合适: return FALSE 取出删除的元素 将 i 位置之后的元素都向前移动一个元素: 表的长度需要减去 1 Input: i : 删除元素的位置 : 0<=i<=L->length-1 Output: L : 表会发生变动 e : 输出被删除的元素 Return: 删除成功 TRUE | 失败 FALSE *************************************************/ BOOL ListDelete(sqList * L , int i , ElemType * e) { int j = 0; if(0 == L->length || 0 > i || i >= L->length) return FALSE; *e = L->data[i]; for(j = i+1 ; j <=L->length ; ++j) { L->data[j-1] = L->data[j]; } L->length-=1; return TRUE; } /************************************************* Function: GetElem Description: 获取顺序表中指定位置的元素 , 时间复杂度O(n) , 时间浪费在遍历上面: i位置不合理 return FALSE 取得i位置的元素 Input: L : 顺序表结构 i : 获取元素的位置 , 0<= i <= L.length-1 Output: *e : 输出类型参数 , 获取 i 指向的元素 Return: 成功TRUE | 失败 FALSE *************************************************/ BOOL GetElem(sqList L , int i , ElemType * e) { /* i 是从0 开始计算的*/ if(0 == L.length || i < 0 || i > L.length-1) return FALSE; *e = L.data[i]; return TRUE; }
宗上看出:
主要操作:
插入 , 删除 O(n)
搜寻 , 修改 O(1)
线性能表存储结构的优点和缺点:
- 优点:
- 无需为表中元素之间的逻辑关系增加额外空间(比如连表 , 需要增加一个指针域)
- 可以快速的存取表中 任意元素的位置
- 缺点:
- 插入 和 删除操作需要移动大量元素
- 当线性表的长度变化较大时 , 难以确定存储空间的容量
- 造成存储空间的碎片 [ 可能开辟的空间大小 > 顺序表的大小造成 空间的浪费 ]