五、【线性表】线性表的链式表示和实现
線性表的鏈?zhǔn)奖硎竞蛯?shí)現(xiàn)
上節(jié)提到,由于順序表的特點(diǎn)是邏輯關(guān)系上相鄰的兩個(gè)元素在物理位置上也相鄰,因此可以隨機(jī)存取表中任一元素。然而,這也導(dǎo)致了順序表在執(zhí)行插入或刪除操作時(shí),需要移動(dòng)大量元素。本節(jié)來(lái)討論線性表的另一種存儲(chǔ)方式——鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)。
1 線性表的鏈?zhǔn)奖硎?/h2>
1.1 單鏈表的定義 Single Linked List
線性表的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)的特點(diǎn)是用一組任意的存儲(chǔ)單元存儲(chǔ)線性表的數(shù)據(jù)元素,這些存儲(chǔ)單元可以連續(xù)也可以不連續(xù)。因此,為了表示每個(gè)數(shù)據(jù)元素與其后繼的邏輯關(guān)系,除了存儲(chǔ)其信息本身外,還需要存儲(chǔ)一個(gè)指示其后繼的信息(即其后繼的存儲(chǔ)地址)。這兩部分信息共同構(gòu)成一個(gè)數(shù)據(jù)元素的存儲(chǔ)映像,稱為結(jié)點(diǎn)(Node)。其中,存儲(chǔ)數(shù)據(jù)元素信息的域稱為數(shù)據(jù)域;存儲(chǔ)后繼存儲(chǔ)位置的域稱為指針域,指針域中存儲(chǔ)的信息稱為指針或鏈。
因?yàn)榇祟愭湵淼拿總€(gè)結(jié)點(diǎn)中只包含一個(gè)指針域,故又稱為單鏈表或線性鏈表。
1.2 單鏈表的特點(diǎn)
換句話說(shuō),單鏈表中的數(shù)據(jù)元素之間的邏輯關(guān)系是由結(jié)點(diǎn)中的指針指示的,所以邏輯上相鄰的兩個(gè)元素不需要在物理地址上也相鄰,這種存儲(chǔ)結(jié)構(gòu)為非順序映像或鏈?zhǔn)接诚瘛?/p>
在單鏈表中,只要知道前一個(gè)元素,通過(guò)指針就可以找到后一個(gè)元素。但是鏈表中的第一個(gè)數(shù)據(jù)元素就不是很好表達(dá),因此我們需要引入“頭結(jié)點(diǎn)”(如下圖中的 head)。頭結(jié)點(diǎn)的作用是指向表中的第一個(gè)元素結(jié)點(diǎn),頭結(jié)點(diǎn)的數(shù)據(jù)域中可以不存儲(chǔ)任何信息,也可以存儲(chǔ)表長(zhǎng)等描述線性表的附加信息。若線性表為空表,則頭結(jié)點(diǎn)的指針域?yàn)榭铡?br />
頭指針和頭結(jié)點(diǎn)
上圖中的單鏈表在第一個(gè)數(shù)據(jù)結(jié)點(diǎn)(即保存 a1a_1a1? 的結(jié)點(diǎn))之前,還有一個(gè)和普通結(jié)點(diǎn)結(jié)構(gòu)一致的頭結(jié)點(diǎn),包含數(shù)據(jù)域和指針域。還有一些鏈表沒(méi)有完整的頭結(jié)點(diǎn),只有一個(gè)指針指向第一個(gè)數(shù)據(jù)結(jié)點(diǎn),這個(gè)指針被稱為頭指針(可以看成是只包含指針域的頭結(jié)點(diǎn))。
這兩種表示方法沒(méi)有區(qū)別,不管有沒(méi)有頭結(jié)點(diǎn),頭指針都始終指向鏈表的第一個(gè)結(jié)點(diǎn)。但是為了操作方便,一般都加上頭結(jié)點(diǎn)。
總結(jié)
- 單鏈表不需要大量連續(xù)的存儲(chǔ)單元,可以更好地利用非連續(xù)內(nèi)存。
- 同時(shí),由于單鏈表除了存儲(chǔ)數(shù)據(jù)元素本身,還存儲(chǔ)了指向下一個(gè)元素的指針,因此也需要浪費(fèi)更多的存儲(chǔ)空間。
- 由于單鏈表的各元素離散地分布在存儲(chǔ)空間中,所以只能順序存取。如果要獲取第 nnn 個(gè)元素,必須先遍歷前 n?1n-1n?1 個(gè)元素,因此單鏈表的查找操作效率很低。
2 單鏈表的實(shí)現(xiàn)
2.1 結(jié)點(diǎn)的定義
要實(shí)現(xiàn)鏈表,首先要定義結(jié)點(diǎn),按照定義,每一個(gè)結(jié)點(diǎn)包含存儲(chǔ)數(shù)據(jù)的數(shù)據(jù)域和存儲(chǔ)后繼位置的指針域:
/********** 單鏈表的定義 **********/ typedef int elemType; typedef struct LNode{elemType data; // 數(shù)據(jù)域struct LNode *next; // 指針域 } LNode, *LinkedList;其中我們給 LNode 定義了一個(gè)別名 LinkedList,這個(gè)類型別名是一個(gè)指針類型。實(shí)際上使用 LNode 和 LinkedList 會(huì)得到一樣的效果,但是使用合適的別名可以讓我們更好地理解代碼。當(dāng)我們要使用的變量的功能側(cè)重于標(biāo)識(shí)位置時(shí),就聲明這個(gè)對(duì)象為 LinkedList ;當(dāng)我們要使用的變量是結(jié)點(diǎn)時(shí),就聲明這個(gè)對(duì)象為 LNode 。例如:
// 頭指針,尾指針和臨時(shí)指針 LinkedList head, tail, tmp; // 新結(jié)點(diǎn)n LNode *n;2.2 主要操作的實(shí)現(xiàn)
2.2.1 頭插法建立單鏈表
單鏈表的生成可以有兩種方式,從頭部插入和從尾部插入。其中頭插法從空表開始,每生成一個(gè)新結(jié)點(diǎn),就插入當(dāng)前鏈表的表頭,即頭結(jié)點(diǎn)之后。
/** Function: 頭插法建立單鏈表* ----------------------------* 從空表開始生成新結(jié)點(diǎn),并將讀取到的數(shù)據(jù)存放到新結(jié)點(diǎn)的數(shù)據(jù)域中,* 然后將新結(jié)點(diǎn)插入到頭結(jié)點(diǎn)之后。*/ LinkedList headInsert(LinkedList &L){ LNode *n; // 先聲明一個(gè)結(jié)點(diǎn)指針n,用來(lái)指向?qū)?lái)生成的新結(jié)點(diǎn)elemType e; // 元素e,用來(lái)接收輸入的元素?cái)?shù)據(jù)L = new LNode; // 創(chuàng)建頭結(jié)點(diǎn)L->next = NULL; // 初始為空鏈表scanf("%d", &e); while(e!=-1){n = new LNode; // 創(chuàng)建新結(jié)點(diǎn)n->data = e; n->next = L->next; // 將新結(jié)點(diǎn)插入頭結(jié)點(diǎn)之后L->next = n; scanf("%d", &e);}return L; // 返回頭結(jié)點(diǎn) }時(shí)間復(fù)雜度分析
假設(shè)采用頭插法建立單鏈表,一共插入 nnn 個(gè)結(jié)點(diǎn)。對(duì)于單次插入操作來(lái)說(shuō),插入位置固定,因此操作時(shí)間復(fù)雜度和鏈表總長(zhǎng)無(wú)關(guān),時(shí)間復(fù)雜度為 O(1)O(1)O(1) 。總共插入 nnn 個(gè)結(jié)點(diǎn),則總的時(shí)間復(fù)雜度為 O(n)O(n)O(n) 。
2.2.2 尾插法建立單鏈表
頭插法每一次都是在頭結(jié)點(diǎn)之后插入新結(jié)點(diǎn),因此會(huì)導(dǎo)致元素的輸入順序和在鏈表中的存儲(chǔ)順序相反,有些不符合邏輯,尾插法可解決這一問(wèn)題。尾插法是從空表開始,每一次將新結(jié)點(diǎn)插入到表尾。但是不同于頭結(jié)點(diǎn),沒(méi)有一個(gè)特殊的指針指向表尾,所以我們需要建立一個(gè)尾指針并讓它始終指向表尾。
/** Function: 尾插法建立單鏈表* ----------------------------* 從空表開始生成新結(jié)點(diǎn),并將讀取到的數(shù)據(jù)存放到新結(jié)點(diǎn)的數(shù)據(jù)域中,* 然后將新結(jié)點(diǎn)插入到鏈表的表尾。*/ LinkedList tailInsert(LinkedList &L){LNode *n; // 聲明新的結(jié)點(diǎn)nLinkedList t; // 聲明尾指針telemType e;L = new LNode; // 創(chuàng)建頭結(jié)點(diǎn)L->next = NULL;t = L; // 將指針t指向表尾,此時(shí)鏈表為空,表尾就是頭結(jié)點(diǎn)scanf("%d", &e);while (e!=-1){n = new LNode; // 創(chuàng)建新結(jié)點(diǎn)n->data = e;t->next = n; // 將新結(jié)點(diǎn)n加入鏈表t = n; // 將尾指針指向新結(jié)點(diǎn)nscanf("%d", &e);}t->next = NULL; // 將新的鏈表的尾結(jié)點(diǎn)指針置空return L; }時(shí)間復(fù)雜度分析
由于我們加入了尾指針,每一次插入操作只需要將新結(jié)點(diǎn)插入尾結(jié)點(diǎn)之后,單次插入操作的時(shí)間復(fù)雜度依然和鏈表總長(zhǎng)度無(wú)關(guān),時(shí)間復(fù)雜度為 O(1)O(1)O(1) 。假設(shè)總共插入 nnn 個(gè)結(jié)點(diǎn),那么總的時(shí)間復(fù)雜度為 O(n)O(n)O(n) 。
2.2.3 按位查找結(jié)點(diǎn)操作
在單鏈表中查找第 iii 個(gè)結(jié)點(diǎn),若找到則返回該結(jié)點(diǎn),否則返回 NULL 。
/** Function: 按位查找結(jié)點(diǎn)* ----------------------------* 在單鏈表中從第一個(gè)結(jié)點(diǎn)出發(fā),直到找到第i個(gè)結(jié)點(diǎn)為止,* 否則返回最后一個(gè)結(jié)點(diǎn)指針域NULL。*/LNode *getElem(LinkedList L, int i){if (i<0){ // 檢查序號(hào)值是否合法return NULL;}LinkedList tmp=L; // 創(chuàng)建臨時(shí)指針,并指向頭結(jié)點(diǎn)for (int j=0;j<i;j++){ if (!tmp->next){ // 如果臨時(shí)指針的指針域?yàn)榭?#xff0c; 則代表return NULL; // 臨時(shí)指針已位于表尾且仍未查到,返回NULL}tmp = tmp->next; }return tmp; // 如果for循環(huán)正常退出,// 則代表找到,返回臨時(shí)指針 }時(shí)間復(fù)雜度分析
基本操作為判斷結(jié)點(diǎn)是否為目標(biāo)結(jié)點(diǎn)。
- 最優(yōu)情況:目標(biāo)結(jié)點(diǎn)在表頭(即序號(hào)值 i=1i=1i=1),只需要判斷 1 次,因此時(shí)間復(fù)雜度為 O(1)O(1)O(1) 。
- 最差情況:目標(biāo)結(jié)點(diǎn)在表尾(即序號(hào)值 i=ni = ni=n),共需要判斷 nnn 次,時(shí)間復(fù)雜度為 O(n)O(n)O(n) 。
- 平均情況:令 pip_ipi? 為目標(biāo)元素在第 iii 個(gè)位置上的概率,假設(shè)目標(biāo)元素出現(xiàn)在任意一個(gè)位置上的概率是相等的,那么 pi=1(n)p_i = \frac{1}{(n)}pi?=(n)1?。在長(zhǎng)度為 nnn 的鏈表中查找一個(gè)元素時(shí),所需移動(dòng)元素的平均次數(shù)為
∑i=1npi?i=n+12\sum_{i=1}^{n} p_i*i = \frac{n+1}{2} i=1∑n?pi??i=2n+1?
所以平均時(shí)間復(fù)雜度為 O(n)O(n)O(n) 。
2.2.4 按值查找結(jié)點(diǎn)操作
在單鏈表中查找第一個(gè)數(shù)據(jù)域等于給定值 eee 的結(jié)點(diǎn)并返回,若未找到則返回 NULL 。
/** Function: 按值查找結(jié)點(diǎn)* ----------------------------* 在單鏈表中從第一個(gè)結(jié)點(diǎn)出發(fā),直到找到某個(gè)結(jié)點(diǎn)的數(shù)據(jù)域* 等于目標(biāo)值為止,否則返回返回NULL。*/LNode *locateElem(LinkedList L, elemType e, int &count){LinkedList tmp=L->next; // 創(chuàng)建臨時(shí)指針,指向頭結(jié)點(diǎn)指針域count=1; // 記錄查找次數(shù)while (tmp!=NULL && tmp->data!=e){ // 如果臨時(shí)指針不為空且還未找到目標(biāo)值,tmp = tmp->next; // 則繼續(xù)while循環(huán)count++;}return tmp; }時(shí)間復(fù)雜度分析
和按位查找一致。
2.2.5 插入結(jié)點(diǎn)操作
在指定位置 iii 插入新結(jié)點(diǎn),如果 iii 的值不合法則返回 NULL 。
插入結(jié)點(diǎn)的操作可分為幾步:
因?yàn)榈?1 步和第 2 步恰好是按位查找結(jié)點(diǎn)操作所做的事,所以可以借用按位查找來(lái)實(shí)現(xiàn)插入操作。
/** Function: 插入操作 * ----------------------------* 在指定位置i插入結(jié)點(diǎn)。* 借用getElem()方法可以將整個(gè)過(guò)程簡(jiǎn)化為三步:* 1. tmp = getElem(L, i-1);* 2. n->next = tmp->next;* 3. tmp->next = n;*/LNode *listInsert(LinkedList &L, elemType e, int i){LinkedList tmp = getElem(L, i-1); // 首先使用按位查找檢測(cè)第i-1個(gè)結(jié)點(diǎn)是否存在,if (tmp==NULL){ // 即檢查插入位置的前驅(qū)結(jié)點(diǎn)是否存在return NULL;}LNode *n = new LNode; // 創(chuàng)建新結(jié)點(diǎn)n->data = e; n->next = tmp->next; // 將新結(jié)點(diǎn)插入第i-1個(gè)結(jié)點(diǎn)之后tmp->next = n;return tmp; }時(shí)間復(fù)雜度分析
根據(jù)前面分析,插入操作共分三步:
結(jié)合按位查找可簡(jiǎn)化為:
因?yàn)榘次徊檎也僮鞯臅r(shí)間復(fù)雜度為 O(n)O(n)O(n) ,單次插入操作的復(fù)雜度為 O(1)O(1)O(1) ,所以總的時(shí)間復(fù)雜度為 O(max{O(n),O(1)})=O(n)O(max\{O(n), O(1)\}) = O(n)O(max{O(n),O(1)})=O(n) 。
2.2.5 刪除結(jié)點(diǎn)操作
刪除單鏈表的第 iii 個(gè)結(jié)點(diǎn),如果 iii 的值不合法則返回 NULL 。分析同插入操作一致,重點(diǎn)是找到刪除結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)。
/** Function: 刪除操作* ----------------------------* 刪除在指定位置i的結(jié)點(diǎn)。*/elemType listDelete(LinkedList &L, int i){LinkedList tmp = getElem(L, i-1); // 檢查刪除位置的前驅(qū)結(jié)點(diǎn)是否存在if (tmp==NULL || tmp->next==NULL){ // tmp不存在或者tmp就是最后一個(gè)結(jié)點(diǎn),return -1; // 則不需要執(zhí)行刪除操作,直接返回}LinkedList q = tmp->next; // 指針q指向待刪除結(jié)點(diǎn)elemType e = q->data; // e用來(lái)保存刪除結(jié)點(diǎn)的值tmp->next = q->next; // 斷開q其他結(jié)點(diǎn)在鏈表中連接關(guān)系delete q; // 釋放結(jié)點(diǎn)的存儲(chǔ)空間return e; }時(shí)間復(fù)雜度分析
與插入操作一致。
3 其他常用操作
3.1 反轉(zhuǎn)鏈表操作
將鏈表所有元素的邏輯關(guān)系反轉(zhuǎn)(即前驅(qū)變?yōu)楹罄^)。
3.1.1 遍歷法實(shí)現(xiàn)反轉(zhuǎn)操作
基本思路是遍歷原鏈表,將每一個(gè)遍歷的元素指針?lè)崔D(zhuǎn)。但在過(guò)程中可能會(huì)出現(xiàn)鏈表斷裂的問(wèn)題,如下圖所示:
當(dāng)?shù)诙€(gè)結(jié)點(diǎn)指向改變時(shí),鏈表在第二、三結(jié)點(diǎn)處斷裂,無(wú)法再獲取第三、四結(jié)點(diǎn)。因此,我們需要用一個(gè)變量來(lái)保存當(dāng)前結(jié)點(diǎn)的后繼結(jié)點(diǎn)。一般需要三個(gè)指針,一個(gè)指向當(dāng)前結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn),定義為 pre ;一個(gè)指向當(dāng)前結(jié)點(diǎn),定義為 cur ;一個(gè)指向當(dāng)前結(jié)點(diǎn)的后繼結(jié)點(diǎn),定義為 next 。
時(shí)間復(fù)雜度分析
整個(gè)反轉(zhuǎn)過(guò)程可看成是遍歷 nnn 個(gè)元素以及依次反轉(zhuǎn)單個(gè)元素。反轉(zhuǎn)單個(gè)元素的時(shí)間復(fù)雜度為 O(1)O(1)O(1),遍歷的時(shí)間復(fù)雜度為 O(n)O(n)O(n) ,因此總的時(shí)間復(fù)雜度為 O(n)O(n)O(n) 。
3.1.2 遞歸法實(shí)現(xiàn)反轉(zhuǎn)操作
待補(bǔ)充。
3.2 合并有序鏈表操作
將有兩個(gè)有序鏈表合并為一個(gè)有序鏈表。
基本思路是分別用兩個(gè)指針遍歷兩個(gè)鏈表的元素,比較指針?biāo)赶蚪Y(jié)點(diǎn)的元素大小,將較小的先加入合并鏈表。
/** Function: 合并鏈表操作* ----------------------------* 將兩個(gè)鏈表的元素按大小關(guān)系*/LinkedList listMerge(LinkedList L1, LinkedList L2){LinkedList LM = new LNode; // 新的合并鏈表的頭結(jié)點(diǎn)LM->next = NULL;LinkedList tmp = LM;L1 = L1->next;L2 = L2->next;// 如果L1和L2都為空,那么直接返回空的LMif (L1==NULL && L2==NULL){return LM;}// 如果L1,L2其一不為空,或是都不為空,則直接開始比較while (true){// 當(dāng)一方?jīng)]有剩余結(jié)點(diǎn)時(shí),退出循環(huán)if (L1==NULL || L2==NULL){break;}// 比較兩個(gè)鏈表中的元素大小,較小的先加入合并鏈表if (L1->data <= L2->data){tmp->next = L1;tmp = tmp->next;L1 = L1->next;} else {tmp->next = L2;tmp = tmp->next;L2 = L2->next;}}// 當(dāng)一方已經(jīng)完成遍歷時(shí),將另一方的剩余結(jié)點(diǎn)全部加入合并鏈表if (L1==NULL){tmp->next = L2;} else {tmp->next = L1;}return LM; }上述實(shí)現(xiàn)方法會(huì)破壞原鏈表,在確定不會(huì)再使用原鏈表后才可使用。例如:
// L1 = 1 3; // L2 = 2 4;LinkedList LM = listMerge(L1, L2); listPrint(L1); listPrint(L2); listPrint(LM);輸出: L1: 1 2 3 4 L2: 2 3 4 LM: 1 2 3 4時(shí)間復(fù)雜度分析
合并有序鏈表的基本操作是兩個(gè)鏈表內(nèi)元素的比較,假設(shè)鏈表的長(zhǎng)度分別為 LaLaLa 和 LbLbLb ,那么總的比較次數(shù)最多為 La+Lb?1La +Lb -1La+Lb?1 。例如:a={1,3,5},b={2,4,6}a = \{1, 3, 5\}, b = \{2, 4, 6\}a={1,3,5},b={2,4,6} ,總的比較次數(shù)為 5 。
因此最差時(shí)間復(fù)雜度為 O(La+Lb)O(La + Lb)O(La+Lb) 。
相關(guān)章節(jié)
第一節(jié) 【緒論】數(shù)據(jù)結(jié)構(gòu)的基本概念
第二節(jié) 【緒論】算法和算法評(píng)價(jià)
第三節(jié) 【線性表】線性表概述
第四節(jié) 【線性表】線性表的順序表示和實(shí)現(xiàn)
第五節(jié) 【線性表】線性表的鏈?zhǔn)奖硎竞蛯?shí)現(xiàn)
第六節(jié) 【線性表】雙向鏈表、循環(huán)鏈表和靜態(tài)鏈表
第七節(jié) 【棧和隊(duì)列】棧
第八節(jié) 【棧和隊(duì)列】棧的應(yīng)用
第九節(jié) 【棧和隊(duì)列】棧和遞歸
第十節(jié) 【棧和隊(duì)列】隊(duì)列
附錄
單鏈表實(shí)現(xiàn)的完整代碼
/** File name: LinkedList.h* -----------------------* Using struct to implement LinkedList*/#ifndef _SINGLE_LINKED_LIST_h_ #define _SINGLE_LINKED_LIST_h_ #include <iostream> #include <stdio.h> #include <stdlib.h> using namespace std;/********** 單鏈表的定義 **********/ typedef int elemType; typedef struct LNode{elemType data; // 數(shù)據(jù)域struct LNode *next; // 指針域 } LNode, *LinkedList;/********** 基本操作的實(shí)現(xiàn) **********/ /** Function: 頭插法建立單鏈表* ----------------------------* 從空表開始生成新結(jié)點(diǎn),并將讀取到的數(shù)據(jù)存放到新結(jié)點(diǎn)的數(shù)據(jù)域中,* 然后將新結(jié)點(diǎn)插入到頭結(jié)點(diǎn)之后。*/ LinkedList headInsert(LinkedList &L){ // L是一個(gè)指針LNode *n; // 先聲明一個(gè)結(jié)點(diǎn)指針n,用來(lái)指向?qū)?lái)生成的新結(jié)點(diǎn)elemType e; // 元素e,用來(lái)接收輸入的元素?cái)?shù)據(jù)L = new LNode; // 創(chuàng)建頭結(jié)點(diǎn)L->next = NULL; // 初始為空鏈表scanf("%d", &e); while(e!=-1){n = new LNode; // 創(chuàng)建新結(jié)點(diǎn)n->data = e; n->next = L->next; // 將新結(jié)點(diǎn)插入頭結(jié)點(diǎn)之后L->next = n;scanf("%d", &e);}return L; // 返回頭結(jié)點(diǎn) }/** Function: 尾插法建立單鏈表* ----------------------------* 從空表開始生成新結(jié)點(diǎn),并將讀取到的數(shù)據(jù)存放到新結(jié)點(diǎn)的數(shù)據(jù)域中,* 然后將新結(jié)點(diǎn)插入到鏈表的表尾。*/ LinkedList tailInsert(LinkedList &L){LNode *n; // 聲明新的結(jié)點(diǎn)nLinkedList t; // 聲明尾指針telemType e;L = new LNode; // 創(chuàng)建頭結(jié)點(diǎn)L->next = NULL;t = L; // 將指針t指向表尾,此時(shí)鏈表為空,表尾就是頭結(jié)點(diǎn)scanf("%d", &e);while (e!=-1){n = new LNode; // 創(chuàng)建新結(jié)點(diǎn)n->data = e;t->next = n; // 將新結(jié)點(diǎn)n加入鏈表t = n; // 將尾結(jié)點(diǎn)指向新結(jié)點(diǎn)nscanf("%d", &e);}t->next = NULL; // 將新的鏈表的尾結(jié)點(diǎn)指針置空return L; }/** Function: 判空操作* ----------------------------* 判斷鏈表是否為空。*/bool listEmpty(LinkedList L){return !L->next; // 如果只有頭結(jié)點(diǎn),則鏈表為空 }/** Function: 求表長(zhǎng)操作* ----------------------------* 計(jì)算單鏈表中結(jié)點(diǎn)的個(gè)數(shù),不包括頭結(jié)點(diǎn)。*/int listLength(LinkedList L){int count=0; // 用來(lái)計(jì)算結(jié)點(diǎn)個(gè)數(shù)LinkedList tmp=L;while (tmp->next!=NULL){tmp = tmp->next;count++;}return count; }/** Function: 按位查找結(jié)點(diǎn)* ----------------------------* 在單鏈表中從第一個(gè)結(jié)點(diǎn)出發(fā),直到找到第i個(gè)結(jié)點(diǎn)為止,* 否則返回最后一個(gè)結(jié)點(diǎn)指針域NULL。*/LNode *getElem(LinkedList L, int i){if (i<0){ // 檢查序號(hào)值是否合法return NULL;}LinkedList tmp=L; // 創(chuàng)建臨時(shí)指針,并指向頭結(jié)點(diǎn)for (int j=0;j<i;j++){ if (!tmp->next){ // 如果臨時(shí)指針的指針域?yàn)榭?#xff0c; 則代表return NULL; // 臨時(shí)指針已位于表尾且仍未查到,返回NULL}tmp = tmp->next; }return tmp; // 如果for循環(huán)正常退出,// 則代表找到,返回臨時(shí)指針 } /** Function: 按值查找結(jié)點(diǎn)* ----------------------------* 在單鏈表中從第一個(gè)結(jié)點(diǎn)出發(fā),直到找到某個(gè)結(jié)點(diǎn)的數(shù)據(jù)域* 等于目標(biāo)值為止,否則返回返回NULL。*/LNode *locateElem(LinkedList L, elemType e, int &count){LinkedList tmp=L->next; // 創(chuàng)建臨時(shí)指針,指向頭結(jié)點(diǎn)指針域count=1; // 記錄查找次數(shù)while (tmp!=NULL && tmp->data!=e){ // 如果臨時(shí)指針不為空且還未找到目標(biāo)值,tmp = tmp->next; // 則繼續(xù)while循環(huán)count++;}return tmp; }/** Function: 插入操作 * ----------------------------* 在指定位置i插入結(jié)點(diǎn)。* 借用getElem()方法可以將整個(gè)過(guò)程簡(jiǎn)化為三步:* 1. tmp = getElem(L, i-1);* 2. n->next = tmp->next;* 3. tmp->next = n;*/LNode *listInsert(LinkedList &L, elemType e, int i){LinkedList tmp = getElem(L, i-1); // 首先使用按位查找檢測(cè)第i-1個(gè)結(jié)點(diǎn)是否存在,if (tmp==NULL){ // 即檢查插入位置的前驅(qū)結(jié)點(diǎn)是否存在return NULL;}LNode *n = new LNode; // 創(chuàng)建新結(jié)點(diǎn)n->data = e; n->next = tmp->next; // 將新結(jié)點(diǎn)插入第i-1個(gè)結(jié)點(diǎn)之后tmp->next = n;return tmp; }/** Function: 刪除操作* ----------------------------* 刪除在指定位置i的結(jié)點(diǎn)。*/elemType listDelete(LinkedList &L, int i){LinkedList tmp = getElem(L, i-1); // 檢查刪除位置的前驅(qū)結(jié)點(diǎn)是否存在if (tmp==NULL || tmp->next==NULL){ // tmp不存在或者tmp就是最后一個(gè)結(jié)點(diǎn),return -1; // 則不需要執(zhí)行刪除操作,直接返回}LinkedList q = tmp->next; // 指針q指向待刪除結(jié)點(diǎn)elemType e = q->data; // e用來(lái)保存刪除結(jié)點(diǎn)的值tmp->next = q->next; // 斷開q其他結(jié)點(diǎn)在鏈表中連接關(guān)系delete q; // 釋放結(jié)點(diǎn)的存儲(chǔ)空間return e; }/** Function: 輸出操作* ----------------------------* 按順序從頭到尾輸出單鏈表的元素*/void listPrint(LinkedList L){LinkedList tmp=L;while (tmp->next!=NULL){tmp = tmp->next;printf("%d ", tmp->data);}printf("\n"); }#endif // _SINGLE_LINKED_LIST_h單鏈表檢測(cè)程序
/** File name: LinkedList.cpp* -----------------------* Using struct to implement LinkedList*/#include "LinkedList.h"int main() {LinkedList L; // 頭結(jié)點(diǎn)int n, i, res;elemType e;LinkedList node;char helpInfo[] ="*****************************\n""Linked list check: \n""\t1-Create linked list by head insertion\n""\t2-Create linked list by tail insertion\n""\t3-Insert element\n""\t4-Delete element\n""\t5-Print\n""\t6-Empty check\n""\t7-Get Length\n""\t8-Search by value\n""\t9-Search by location\n""\t10-Quit\n""*****************************\n";while (n != 10) {printf(helpInfo);scanf("%d", &n);switch (n) {case 1:printf("Head insertion:\n");printf("Enter -1 to quit.\n");headInsert(L);break;case 2:printf("Tail insertion:\n");printf("Enter -1 to quit.\n");tailInsert(L);break;case 3:printf("Please enter the location: \n");scanf("%d", &i);printf("Please enter the element: \n");scanf("%d", &e);listInsert(L, e, i);break;case 4:printf("Please enter the location: \n");scanf("%d", &i);res = listDelete(L, i);if (res == -1) {printf("Didn't find the target node.\n");} else {printf("Target node in location %d with value %d is ""deleted.\n",i, res);}break;case 5:printf("List: ");listPrint(L);break;case 6:if (listEmpty(L)) {printf("This list is empty.\n");} else {printf("This list is not empty.\n");}break;case 7:printf("Length of list is: %d\n", listLength(L));break;case 8:printf("Please enter the target value: ");scanf("%d", &e);node = locateElem(L, e, i);if (!node) {printf("Didn't find the target node.\n");} else {printf("The index of target is: %d\n", i);}break;case 9:printf("Please enter the target index: ");scanf("%d", &i);node = getElem(L, i);if (!node) {printf("Didn't find the target node.\n");} else {printf("The value of target is %d\n", node->data);}break;}}return 0; }總結(jié)
以上是生活随笔為你收集整理的五、【线性表】线性表的链式表示和实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 四、【线性表】线性表的顺序表示和实现
- 下一篇: 七、【栈和队列】栈