1.
线性表的基本概念
数据结构分线性结构和非线性结构。线性的数据结构包括线性表、栈、队列、数组和串。
线性结构特点:在数据元素的非空有限集中
存在唯一的一个被称作“第一个”的数据元素
存在唯一的一个被称作“最后一个”的数据元素
除第一个外,集合中的每个数据元素均只有一个前驱
除最后一个外,集合中的每个数据元素均只有一个后继
2.
线性表的顺序存储结构
顺序表:
定义:用一组地址连续的存储单元存放一个线性表叫~
元素地址计算方法:
LOC(ai)=LOC(a1)+(i-1)*L
LOC(ai+1)=LOC(ai)+L
其中:
L—
一个元素占用的存储单元个数
LOC(ai)—
线性表第i个元素的地址
特点:
实现逻辑上相邻—物理地址相邻
实现随机存取
实现:可用C语言的一维数组实现
3.
线性表的基本操作
(1) InitList-Sq
(L)初始化操作函数。生成一个空的线性表L。
(2) LENGTH
(L) 求表长度的函数。函数值为线性表L中数据元素的个数。
(3) GET
(L, i) 取表中元素的函数。当1≤ i ≤ LENGTH(L)时,函数值为线性表L中第i个数据元素,否则返回一特殊值。i是该数据元素在线性表中的位置序号。
(4) LOCATE
(L, x) 定位函数。给定值x,在线性表L中若存在和x相等的数据元素,则函数返回和x相等的数据元素的位置序号,否则返回0。若线性表中存在一个以上的和x相等的数据元素,则函数返回多个位置序号中的最小值,也就是表中第一个和x相等的元素的位置序号。
(5) Insertlist-Sq(L, b, i)
插入操作。在给定线性表L中第i(1≤ i ≤ LENGTH(L) + 1)个数据元素之前插入一个新的数据元素b, 使原来线性表的长度n变成n + 1。
(6) ListDelete-Sq(L, i)
删除操作。删除在给定线性表L中第i (1≤ i ≤ LENGTH(L))个数据元素,使原来线性表的长度n变成n -1。
(7) EMPTY(L)
判空表函数。若L为空表,则返回布尔值“真”, 否则返回布尔值“假”。
(8) CLEAR(L)表置空操作。不管原来的线性表L是空表还是非空表,操作结果将L表置空。
以上基本操作中,(1),(5),(6),(8) 是加工型操作,其他都是引用型操作
4.
线性表原始定义
define
LIST_INIT_SIZE 100 //
初次分配空间量
define
LISTINCREAENT 10 //
每次分配增量
Typedef struct {
ElemType *elem;
//
数组指针表的基址
int length;
//
当前元素个数
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; // 数据元素个数为0
L.Listsize =LIST_INIT_SIZE;
//
最多元素个数
return OK;
L.Listsize
}
算法实现:
void Insertlist(seqlist *L,ElemenType x,int i)
{
int j ;
if (i <1 ||i> L->length+1) Error(position error);
if(L->length+1 >= L->listsize) Error( overflow);
For ( j = L->length-1 ;j >= i –1; j--)
L->elem[j+1] = L->elem[j] ;
L->elem[ i-1]= x
L->length+ +;
}
当预定义的空间用完后,没有动态再给用户分配空间的功能。下面给出功能强的算法
Status ListInsert-Sq(SqList &L,int i,ElemenType 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;
// 长度增加
1
return OK;
}
现在,分析该算法的时间复杂性。:该插入算法的基本操作是元素后移操作。执行元素后移的次数是
n - i +1。可以看到移动元素的次数不仅和表长有关,而且还与插入元素的位置i有关。当i = n + 1时,无须移动元素,当i = 1时,则元素后移将执行n次,也就是说该算法在最好情况下时间复杂度是O(1),最坏情况下时间复杂度是O(n)。进一步分析算法的平均性能:考虑在长度为n的线性表中插入一个元素,令Eis为移动元素的平均次数,在表中第i个元素前插入一个元素要移动元素的次数为n - i + 1,故
Eis=∑
Pi(n-i+1)
Pi表示在表中第
i个位置前插入一个元素的概率,假设在表中任何有效位置前(1 ≤ i ≤ n + 1 )插入元素的机会是均等的,则
Pi=1/(n+1)
因此
Eis=∑
(n-i+1)/(n+1)=n/2
也就是说,在顺序表上做插入操作,平均要移动表的一半元素。就数量级而言,它是线性阶的,算法的平均时间复杂度为
O(n)。
对后面的各种算法,我们不再详细分析和推导算法的时间复杂度,对有些算法则只给出推导的结果。
故在顺序表中插入或删除一个元素时,平均移动表的一半元素,当
n很大时,效率很低