数据结构和算法:(3)3.1线性表的顺序存储结构
-----------------------1、線性表基礎(chǔ)操作------------------------
線性表:(List)由零個或多個數(shù)據(jù)元素組成的有限序列。
- 首先他是一個序列,元素之間是有個先來后到的;
- 若元素存在多個,則第一個元素?zé)o前驅(qū),而最后一個元素?zé)o后繼,其他元素都有且只有一個前驅(qū)和后繼;(前一個元素是后一個元素的直接前驅(qū),后一個元素是前一個元素的直接后繼;)
- 另外,線性表強(qiáng)調(diào)是有限的,事實(shí)上無論計(jì)算機(jī)發(fā)展到多強(qiáng)大,他所處理的元素都是有限的;
- 所以線性表元素的個數(shù)n(n>=0)定義為線性表的長度,當(dāng)n等于0時,稱為空表。
抽象:是指抽取出事物具有的普遍性的本質(zhì)。他要求抽出問題的特征而忽略非本質(zhì)的細(xì)節(jié),是對具體事物的一個概括,抽象是一種思考問題的方式,他隱藏了繁雜的細(xì)節(jié)。
線性表的抽象數(shù)據(jù)類型:(所謂抽象數(shù)據(jù)類型就是把數(shù)據(jù)類型和相關(guān)操作捆綁在一起,這就像我們面向?qū)ο筮@類高級語言的做法一樣,把屬性和方法捆綁在一塊那就是類。)
???????? 我們對已有的數(shù)據(jù)類型進(jìn)行抽象,就有了抽象數(shù)據(jù)類型(Abstract Data Type,ADT)是指一個數(shù)據(jù)類型及定義在該模型上的一組操作。(也可以自定義)
抽象數(shù)據(jù)類型的標(biāo)準(zhǔn)格式:
ADT 抽象數(shù)據(jù)類型名
Data數(shù)據(jù)元素之間邏輯關(guān)系的定義
Operation操作
endADT
線性表的相關(guān)操作:
- 線性表的創(chuàng)建和初始化
- 數(shù)據(jù)的刪除和插入
- 根據(jù)位序得到元素
線性表抽象數(shù)據(jù)類型的定義:
ADT 線性表(List)//他的名字叫做線性表有一個英文名字叫做List
Data//是什么數(shù)據(jù)呢?//下面具體描述數(shù)據(jù)的類型
線性表的數(shù)據(jù)對象集合為,每個元素的類型均為DataType。其中除第一個元素a1外,每一個元素有且只有一個直接前驅(qū)元素,除了最后一個an外,每一個元素有且只有一個直接后繼元素。數(shù)據(jù)元素之間的關(guān)系是一對一的關(guān)系。
操作:
OperationInitList(*L):初始化操作,建立一個空的線性表L。ListEmpty(L):判斷線性表是否為空表,若線性表為空,返回true ;否則返回false。ClearList(*L):將線性表清空。//就是把里邊的數(shù)據(jù)都給清楚掉事實(shí)上呢就是把他們都填0,因?yàn)槲覀冎涝趦?nèi)存中的數(shù)據(jù)不可能是真正的清除,他不能登一聲就沒有了,內(nèi)存中一個位置總要放一個東西,你可以認(rèn)為說0那時候就是一個空氣啥都沒有表示清空的意思。因?yàn)閮?nèi)存上的數(shù)據(jù)只能被覆蓋。GetElem(L,I,*e):將線性表L中的第i個位置元素值返回給e。LocateElem(L,e):在線性表L中查找與給定值e相等的元素,如果查找成功,返回該元素在表中序號表示成功;否則,返回0表示失敗。(注意:我們的數(shù)組是以0下標(biāo)開始的,0就表示第一個元素;但是在線性表中是正常的思維,1就是1,2就是2,0就是沒有的,所以返回0表示失敗)ListInsert(*L,i,e):在線性表L中第i個位置插入新元素e。ListDelete(*L,i,*e):刪除線性表L中第i個位置元素,并用e返回其值。ListLength(L):返回線性表L的元素個數(shù)。
endADT
對于不同的應(yīng)用,線性表的基本操作是不同的,上述操作是最基本的,對于實(shí)際問題中涉及的關(guān)于線性表的更復(fù)雜的操作,完全可以用這些基本操作的組合來實(shí)現(xiàn)。
舉例:比如想實(shí)現(xiàn)A和B的并集U。運(yùn)用到的基本操作:
ListLength(L);//知道線性表的長度。
GetElem(L,i,*e);//獲取元素。
LocateElem(L,e);//定位。
ListInsert(*L,i,e)//插入。
實(shí)現(xiàn)代碼如下:
//La表示A集合,Lb表示b集合
void unionL(List *La,list Lb)//有兩個線性表,為什么一個帶指針,一個不帶呢?
{int La_len,Lb_len,i;ElemType e;//聲明和LaLb相同的元素e。La_len=ListLength(*La);//獲取線性表長度。(因?yàn)槲覀儌魅氲膶?shí)參不同,所以我們的行參用指針和數(shù)據(jù)來表示而已。)Lb_len=ListLength(Lb);//接下來用一個迭代for(i-1;i<=Lb_len;i++)//(從1開始,因?yàn)榫€性表是給人看的所以從1開始(數(shù)據(jù)結(jié)構(gòu)和算法是給人看的),給機(jī)器看的從0開始){GetElem(Lb,i,*e);//獲取元素,(取Lb中第i個元素賦值給e)將線性表Lb第i個位置的元素值返回給e。if(!LocateElem(*La,e))//判斷獲取的元素在不在La里邊//在線性表La中查找與給定值e相等的元素,如果查找成功,返回該元素在表中序號表示成功;否則,返回0表示失敗。(ListInsert(La,++La_len,e);//如果不在呢就插入//在線性表La中第++La_len個位置插入新元素e。)//之后再進(jìn)行第二次迭代}
------------------2、線性表的順序存儲結(jié)構(gòu)----------------
線性表有兩種物理存儲結(jié)構(gòu):順序存儲結(jié)構(gòu)和鏈?zhǔn)酱鎯Y(jié)構(gòu)。
(1)線性表的順序存儲結(jié)構(gòu):指的是用一段地址連續(xù)的存儲單元依次存儲線性表的數(shù)據(jù)元素。(就像我們C語言中的數(shù)組)
| a1 | a2 | a3 | a4 | ... | ai-1 | ai | ai+1 | ... | an |
物理上的存儲方式事實(shí)上就是在內(nèi)存中找個初始地址,然后通過占位的形式,把一定的內(nèi)存空間給占位了,然后把相同數(shù)據(jù)類型的數(shù)據(jù)元素依次放在這塊空地中。
線性表順序存儲的結(jié)構(gòu)代碼:
#define MAXSIZE 20//3定義下邊數(shù)組的最大長度
typedef int ElemType;
Typedef struct
{ElemType data[MAXSIZE];// 2ElemType是一個類型,它可以時int,front等,在我們這里邊上邊我們用typedef定義為整形的int length;//線性表當(dāng)前長度
}SqList;//4通過一個結(jié)構(gòu)這個結(jié)構(gòu)的名字叫做SqList對數(shù)組進(jìn)行封裝,多了一個length長度的變量。
//1這里封裝了一個結(jié)構(gòu),事實(shí)上就是對數(shù)組進(jìn)行了封裝,增加了個當(dāng)前長度的變量
總結(jié):順序存儲結(jié)構(gòu)封裝需要三個屬性
- 順序存儲空間的起始位置,數(shù)組data,它的存儲位置就是就是線性表存儲空間的存儲位置。
- 線性表的最大存儲容量:數(shù)組的長度Maxsize。
- 線性表的當(dāng)前長度:length。
PS:注意數(shù)組的長度與線性表的當(dāng)前長度需要區(qū)分一下:數(shù)組的長度是存放線性表的存儲空間的總長度,一般初始化后不變。而線性表的當(dāng)前長度是線性表中元素的個數(shù),是會變化的。
我們習(xí)慣了數(shù)組從下標(biāo)0開始計(jì)算,但是線性表是從1開始計(jì)算的。假設(shè)ElemType占用的是c個存儲單元(字節(jié))(比如int占用4個字節(jié)),那么線性表中第i+1個數(shù)據(jù)元素和第i個數(shù)據(jù)元素的存儲位置的關(guān)系是(LOC表示獲得存儲位置的函數(shù)):LOC(ai+1)=LOC(ai)+C(表示ai+1這個元素的地址相當(dāng)于ai元素的地址+C(因?yàn)镃是存儲單元的寬度)),所以對于第i個數(shù)據(jù)元素ai的存儲位置可以由a1存儲推算出來:LOC(ai)=LOC(a1)+(i-1)*C。
------實(shí)現(xiàn)GetElem獲取元素的具體操作,即將線性表L中第i個位置元素值返回,就程序而言非常簡單如下,我們只需要把數(shù)組第i-1下標(biāo)的值返回即可。
#define OK 1//這里用define OK表示1
#define ERROR 0
#define TRUE 1
#DEFINE FALSE 0typedef int Status;
//Status 是函數(shù)類型,其值是函數(shù)結(jié)果狀態(tài)代碼,如OK等。
//初始條件:順序線性表L已存在,1<=i<=ListLength(L)
//操作結(jié)構(gòu):用e返回L中第i個數(shù)據(jù)元素的值。Status GetElem(SqList L,int i,ElemType *e)//有三個參數(shù),第一個是線性表L;第二個是他的索引位置;第三個是他要返回的把他存放的再存放的一個值一個地址一個指針。
{if(L.length==0||i<1||i>L.length){return ERROR;//滿足上述情況返回ERROR;}*e=L.data[i-1]; //其他情況下要把i-1這個下標(biāo)的值把他放在e這個指針變量里邊,那就可以了。return OK;//表示GetElem這個結(jié)果正確
}
注意上述返回值類型Status是一個整型,約定返回1代表OK,返回0代表ERROR。
------實(shí)現(xiàn)ListInsert插入具體操作,ListInsert(*L,i,e)即在線性表L中的第i個位置插入新元素e。
插入算法的思路:
- 如果插入位置不合理,拋出異常;
- 如果線性表長度大于等于數(shù)組長度,則拋出異常或動態(tài)增加數(shù)組容量;
- 從最后一個元素開始向前遍歷到第i個位置,分別將他們都向后移動一個位置。
- 將要插入元素填入位置i處;
- 線性表長+1。
實(shí)現(xiàn)代碼如下:
//初始條件:順序線性表L已經(jīng)存在,1<=i<=ListLength(L)。
//操作結(jié)果:在L中第i個位置之前插入新的數(shù)據(jù)元素e,L長度+1。
Status ListInsert(SqList *L,int i,ElemType e)
{int k;if(L>length==MAXSIZE)//順序線性表已經(jīng)滿了,放不進(jìn)來了。{return ERROR;}if(i<1||i>L->length+1)//當(dāng)i不在范圍內(nèi)時{return ERROR;} if(i<=L->length)//若插入數(shù)據(jù)位置不在表尾{//將要插入位置后的數(shù)據(jù)元素向后移動一位for(k=L->length-1;k>=i-1,k--){L->data[k+1]=L->data[k];//就是一個賦值的過程} }L->data[i-1]=e;//將新元素插入,i-1是因?yàn)榫€性表i是從1開始的,數(shù)組是從0開始的。L->length++;return OK;
}
------實(shí)現(xiàn)ListDelete刪除具體操作,?ListDelete(*L,i,*e)即刪除線性表L中第i個位置元素,并用e返回其值。
刪除算法的思路:
- 如果刪除位置不合理,拋出異常;
- 取出刪除元素;(看一下怎么取的)
- 從刪除位置元素開始遍歷到最后一個元素位置,分別將他們都向前移動一個位置;
- 表長-1。
//初始條件:順序線性表L已經(jīng)存在,1<=i<=ListLength(L)。
//操作結(jié)果:刪除L的第i個數(shù)據(jù)元素,并用e返回其刪除的值,L的長度-1。
Status ListDelete(SqList *L,int i,ElemType *e)
{int k;if(L->length==0)//表長是否為空{(diào)return ERROR;//空表什么都沒有,更不用說刪除了。}if(i<1||i>L->length+1)//當(dāng)i不在范圍內(nèi)時{return ERROR;} *e=L->data[i-1];//把他的值放到這個指針e里邊去(因?yàn)樯线叾x的是一個指針變量)//因?yàn)槭堑趇個元素,所以他的數(shù)組里邊的索引就是i-1;if(i<=L->length)//只要i小于他的length,接下來就是移位{//將要刪除位置后的數(shù)據(jù)元素向前移動一位for(k=i;k<L->length-1;k++){L->data[k-1]=L->data[k];//把刪除位置后邊的元素全部都向前移動//就是一個賦值的過程} }L->length--;return OK;
}
?
總結(jié)
以上是生活随笔為你收集整理的数据结构和算法:(3)3.1线性表的顺序存储结构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构和算法:(2)时间复杂度和空间复
- 下一篇: 数据结构和算法:(3)3.2线性表的链式