一、線性表的概念和定義
1、線性表的邏輯結構特點
線性表是一種最簡單的線性結構,是一個數據元素的有序(次序)集。
集合中必存在唯一的一個“第一元素”;
集合中必存在唯一的一個“最後元素”;
除最後元素之外,每個元素均有唯一的後繼;
除第一元素之外,每個元素均有唯一的前驅;
2、線性表的基本術語
線性表可描述爲n(n≥0)個具有相同特性的數據元素組成的有限序列。
常表示爲:L={a1 ,…, ai-1, ai, ai+1, …, an};ai必須具有相同特性,即屬於同一數據對象。
ai-1是ai的直接前驅元素,ai+1是ai的直接後繼元素;
數據元素ai在線性表中有確定的位置i,i稱爲位序;
線性表中數據元素的個數n稱爲線性表的長度,n=0時,線性表稱爲空表。
3、ADT定義
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 } {設線性表爲(a1,a2,...,ai,...,an), 稱i爲ai在線性表中的位序。}
}
基本操作:
結構初始化操作
結構銷燬操作
引用型操作
加工型操作
}ADT List
GetElem(L,i,&e)
初始條件:
線性表L已存在,且1<=i<=LengthList(L).
操作結果:
用e返回L中第i個元素的值。
LocateElem(L,e,compare())
初始條件:
線性表L已存在,e爲給定值,compare()是元素判定函數。
操作結果:
返回L中第一個與e滿足關係compare()的元素的位序。若這樣的元素不存在,則返回值爲0.
ListTraverse(L,visit())
初始條件:
線性表L已存在,visit()爲某個訪問函數
操作結果:
依次對L的每個元素調用函數visit().一旦visit()失敗,則操作失敗。
PutElem(&L,i,&e)
初始條件:
線性表L已存在,且1<=i<=LengthList(L).
操作結果:
L中第i個元素賦值同e的值.
ListInsert(&L,i,e)
初始條件:
線性表L已存在,且1<=i<=LengthList(L)+1
操作結果:
在L的第i個元素之前插入新的元素e,L的長度增1.
ListDelete(&L,i,&e)
初始條件:
線性表L已存在且非空,1<=i<=LengthList(L).
操作結果:
刪除L的第i個元素,並用e返回其值,L的長度減1.
二、線性表的順序存儲實現——順序表
1.順序存儲(映像)方式
以x的存儲位置和y的存儲位置之間某種關係表示邏輯關係<x,y>.最簡單的一種順序映像方法是:令y的存儲位置和x的存儲位置相鄰。
2.順序表的C語言描述
#define List_INIT_SIZE 100 //線性表存儲空間的初始分配容量
typedef struct
{
ElemType *elem; //存放線性表的數組空間基地址
int length; //當前長度(實際元素個數)
int listsize; //當前分配的容量
}SqlList;//稱 順序表
順序表的基本形態
定義:sqList L;
空表
L.length==0 可插入不可刪除
表滿
L.length>=L.listsize 可刪除不可插入(空間再分配)
表不空不滿
0<L.lenth<L.listsize 可刪除可插入
3、線性表的基本操作在順序表中的實現
初始化:
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
int Locate(SqList L,ElemType e)
{ //在順序表L中查詢第一個等於e的數據元素,若存在,則返回它的位序,否則返回0
i=1; //i的初值爲第1元素的位序
for(i<=L.length;i++)
if(L.elem[i-1]==e)
return i;
return 0;
}//Locate
Status search_i(SqList L,int i,ElemType &e)
{//在順序表L中查找第i個元素,若存在用e返回其值並返回OK,否則返回ERROR
if(i>0&&i<=L.length)
{
e=L.elem[i-1];
return OK;
}
else
return ERROR;
}
線性表的插入操作(ListInsert(&L,i,e))的實現:
分析:
插入元素時,線性表的邏輯結構發生了什麼變化?
(a1,...,ai-1,ai,...,an)改變爲(a1,...,ai-1,e,ai,...,an)
需考慮的問題(步驟)
1.當前表是否已滿?
2.輸入(插入位置)是否有效?
3.插入元素
Status ListInsert(SqList &L,int i,ElemType e)
{//在順序表L的第i個元素之前插入新的元素e,i的合法範圍爲1<=i<=L.length+1
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;//表容量更新
}
for(j=L.length;j>=i;j--)
L.elem[j]=L.elem[j-1];//插入位置及之後的元素右移
L.elem[i-1]=e;//插入e
++L.length;//表長+1
return OK;
}//ListInsert_Sq
線性表操作
ListDelete(&L,i,&e)的實現:
分析:
刪除元素時,線性表的邏輯結構發生了什麼變化?
(a1,a2,...,ai,ai+1,...,an)改變爲(a1,a2,...,ai-1,ai+1,...an)
<ai-1,ai>,<ai,ai+1>——><ai-1,ai+1>
存儲空間(順序表)的變化:表的長度減少
Status ListDelete_Sq(SqList &L,int i,ElemType &e)
{//在順序表L中刪除第i個元素,以引用參數e返回其值,若刪除成功返回ok;否則返回ERROR
if(i<1||i>L.length)
return ERROR;//i的值不合法
e=L.elem[i-1];
for(j=i;j<L.length;j++)
L.elem[j-1]=L.elem[j];//元素前移
--L.length;
return OK;
}//ListDelete_Sq
順序表操作的效率分析
時間效率分析:
算法時間主要耗費在移動元素的操作上,而移動元素的個數取決於插入或刪除元素的位置。
以插入爲例分析
若插入在尾元素之後,則根本無需移動(特別快);
若插入在首元素之前,則表中元素全部要後移(特別慢);
Status Del_k(SqList&L,int i,int k)
{//在順序表L中刪除自第i個開始的k個元素,操作成功返回ok,否則返回error;
if(i>L.length||k>L.length-i+1)
return ERROR;
for(j=i+k-1;j<=L.length-1;j++)
L.elem[j-k]=L.elem[j];//元素前移完成刪除
L.Length-=k;//順序表長度減k
return OK;
}
小結——順序表的特點:
優點:可以隨機存取表中任一元素,方便快捷;
缺點:在插入或刪除某一元素時,需要移動大量元素;需要預先確定數據元素的最大個數。
解決問題的思路:改用另一種線性存儲方式:鏈式存儲結構。