在前面線性表順序存儲(chǔ)的優(yōu)缺點(diǎn)里面我們談到了線性表的一些不足译红,最大的缺點(diǎn)就是插入和刪除時(shí)需要移動(dòng)大量元素,在線性表比較大的時(shí)候采用順序存儲(chǔ)很是不方便站宗,效率低秧骑。
效率低的原因
當(dāng)插入和刪除時(shí),就要移動(dòng)大量元素蠕蚜,仔細(xì)分析后尚洽,發(fā)現(xiàn)原因就在于相鄰兩元素的存儲(chǔ)位置也具有鄰居關(guān)系。它們編號(hào)是1, 2, 3, ……, n,它們?cè)趦?nèi)存中的位置也是挨著的靶累,中間沒有空隙腺毫,當(dāng)然就無(wú)法快速介入,而刪除后挣柬,當(dāng)中就會(huì)留出空隙潮酒,自然需要彌補(bǔ)。問題就出在這里邪蛔。
解決的思路
哪有空位就放到哪里急黎,而讓每個(gè)元素知道它下一個(gè)元素的位置在哪里,這樣,我們可以在第一個(gè)元素時(shí)叁熔,就知道第二個(gè)元素的位置(內(nèi)存地址)委乌, 而找到它;在第二個(gè)元素時(shí)荣回,再找到第三個(gè)元素的位置(內(nèi)存地址)這樣所有的元素我們就都可以通過遍歷而找到遭贸。
線性表的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)的特點(diǎn)
線性表的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)的特點(diǎn)是用一組任意的存儲(chǔ)單元存儲(chǔ)線性表的數(shù)據(jù)元素,這組存儲(chǔ)單元可以是連續(xù)的心软,也可以是不連續(xù)的壕吹。這就意味著,這些數(shù)據(jù)元素可以存在內(nèi)存未被占用的任意位置删铃,比如下圖:
鏈表的一些基本概念
為了表示每個(gè)數(shù)據(jù)元素ai與其直接后繼數(shù)據(jù)元素ai+1之間的邏輯關(guān)系耳贬,對(duì)數(shù)據(jù)元素ai來說,除了存儲(chǔ)其本身的信息之外猎唁,還需存儲(chǔ)一個(gè)指示其直接后繼的信息(即直接后繼的存儲(chǔ)位置)咒劲。
數(shù)據(jù)域:我們把存儲(chǔ)數(shù)據(jù)元素信息的域稱為數(shù)據(jù)域。
指針域:存儲(chǔ)直接后繼位置的域稱為指針域诫隅。
指針/鏈:指針域中存儲(chǔ)的信息稱做指針或鏈腐魂。
結(jié)點(diǎn)(Node):數(shù)據(jù)域與指針域這兩部分信息組成數(shù)據(jù)元素ai的存儲(chǔ)映像,稱為結(jié)點(diǎn)(Node)
n個(gè)結(jié)點(diǎn)(ai的存儲(chǔ)映像)鏈結(jié)成一個(gè)鏈表逐纬,即為線性表(ai, a2…蛔屹,an)的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu),因?yàn)榇随湵淼拿總€(gè)結(jié)點(diǎn)中只包含一個(gè)指針域豁生。因?yàn)橹羔樣蛑写鎯?chǔ)的信息稱做鏈兔毒,而且這個(gè)鏈每個(gè)結(jié)點(diǎn)都只有一個(gè),所以叫做單鏈表甸箱。
單鏈表的頭指針育叁、頭結(jié)點(diǎn)與首元結(jié)點(diǎn)
單鏈表也是一種線性表,所以總得有個(gè)頭有個(gè)尾芍殖。鏈表中第一個(gè)結(jié)點(diǎn)的存儲(chǔ)位置叫做頭指針擂红,那么整個(gè)鏈表的存取就必須是從頭指針開始進(jìn)行了。之后的每一個(gè)結(jié)點(diǎn)围小,其實(shí)就是上一個(gè)的后繼指針指向的位置。
這里有個(gè)地方要注意树碱,就是對(duì)頭指針概念的理解肯适,這個(gè)很重要〕砂瘢“鏈表中第一個(gè)結(jié)點(diǎn)的存儲(chǔ)位置叫做頭指針”框舔,如果鏈表有頭結(jié)點(diǎn),那么頭指針就是指向頭結(jié)點(diǎn)數(shù)據(jù)域的指針。
- 頭結(jié)點(diǎn)是為了操作的統(tǒng)一與方便而設(shè)立的刘绣,放在第一個(gè)元素結(jié)點(diǎn)之前樱溉,其數(shù)據(jù)域一般無(wú)意義(當(dāng)然有些情況下也可存放鏈表的長(zhǎng)度、用做監(jiān)視哨等等)纬凤。
- 有了頭結(jié)點(diǎn)后福贞,對(duì)在第一個(gè)元素結(jié)點(diǎn)前插入結(jié)點(diǎn)和刪除第一個(gè)結(jié)點(diǎn),其操作與對(duì)其它結(jié)點(diǎn)的操作統(tǒng)一了停士。
- 首元結(jié)點(diǎn)也就是第一個(gè)元素的結(jié)點(diǎn)挖帘,它是頭結(jié)點(diǎn)后邊的第一個(gè)結(jié)點(diǎn)。
- 頭結(jié)點(diǎn)不是鏈表所必需的恋技。
- 在線性表的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)中拇舀,頭指針是指鏈表指向第一個(gè)結(jié)點(diǎn)的指針,若鏈表有頭結(jié)點(diǎn)蜻底,則頭指針就是指向鏈表頭結(jié)點(diǎn)的指針骄崩。
- 頭指針具有標(biāo)識(shí)作用,故常用頭指針冠以鏈表的名字薄辅。
-
無(wú)論鏈表是否為空要拂,頭指針均不為空。頭指針是鏈表的必要元素长搀。
單鏈表也可以沒有頭結(jié)點(diǎn)宇弛。如果沒有頭結(jié)點(diǎn)的話,那么單鏈表就會(huì)變成這樣:
image.png
單鏈表的結(jié)構(gòu)體定義與聲明
定義一個(gè)結(jié)構(gòu)體來描述單鏈表的結(jié)點(diǎn)
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList; /* 定義LinkList */
從這個(gè)結(jié)構(gòu)定義中源请,我們知道枪芒,結(jié)點(diǎn)由存放數(shù)據(jù)元素的數(shù)據(jù)域存放后繼結(jié)點(diǎn)地址的指針域組成。
假設(shè)p是指向線性表第i個(gè)元素的指針谁尸,則該結(jié)點(diǎn)ai的數(shù)據(jù)域我們可以用p->data來表示舅踪,p->data的值是一個(gè)數(shù)據(jù)元素,結(jié)點(diǎn)ai的指針域可以用 p->next來表示良蛮,p->next的值是一個(gè)指針抽碌。p->next指向誰(shuí)呢?當(dāng)然是指向第i+1個(gè)元素决瞳,即指向ai+1的指針货徙。
- 關(guān)于結(jié)構(gòu)體 struct Node *next; 這么一句代碼,不可以寫成 int *next 皮胡,next是指向下一個(gè)Node痴颊,所以其類型必須是Node。int *next 只能指向int屡贺,而不能指向Node蠢棱。所以必須定義為 Node 類型锌杀,但是 Node 是結(jié)構(gòu)體,所以前面還得加上個(gè) struct泻仙。
關(guān)于C語(yǔ)言的struct
typedef為C語(yǔ)言的關(guān)鍵字糕再,作用是為一種數(shù)據(jù)類型定義一個(gè)新名字。這里的數(shù)據(jù)類型包括內(nèi)部數(shù)據(jù)類型(int,char等)和自定義的數(shù)據(jù)類型(struct等)玉转。
在編程中使用typedef目的一般有兩個(gè)突想,一個(gè)是給變量一個(gè)易記且意義明確的新名字,另一個(gè)是簡(jiǎn)化一些比較復(fù)雜的類型聲明冤吨。
結(jié)構(gòu)體中的 typedef struct Node 的意思就是蒿柳,為自定義的數(shù)據(jù)類型定義一個(gè)新名字 Node。第二句就是聲明自定義數(shù)據(jù)類型 Node 了漩蟆。
單鏈表的初始化
首先我們?cè)趍ain函數(shù)中 LinkList L; 這么定義了一個(gè)鏈表垒探。因?yàn)樾枰獙?duì)鏈表本身進(jìn)行操作,那么假設(shè)函數(shù)定義為 InitList()怠李,那么需要地址傳遞圾叼,寫成 i=InitList(&L);。
第一步就是:創(chuàng)建一個(gè)頭結(jié)點(diǎn)捺癞,并且讓頭指針指向這個(gè)頭結(jié)點(diǎn)夷蚊。
其實(shí)頭指針也有了。參數(shù) *L 傳入的 L其實(shí)就是鏈表的首地址髓介,也就是頭指針惕鼓。接下來也就是在內(nèi)存開辟一個(gè)區(qū)域來作為頭結(jié)點(diǎn)。
*L=(LinkList) malloc (sizeof(Node)); //創(chuàng)建一個(gè)結(jié)點(diǎn)
此處返回給L的是一個(gè)指針唐础,并且賦給了頭指針箱歧。L其實(shí)就是頭結(jié)點(diǎn),L則為頭指針一膨。
接下來還得給鏈表的首元結(jié)點(diǎn)賦值為 NULL呀邢,因?yàn)?L是頭結(jié)點(diǎn),那么 (L)->next就是首元結(jié)點(diǎn)豹绪,所以 (L)->next=NULL; 這么寫就能讓首元結(jié)點(diǎn)的指針域?yàn)榭占厶省K院瘮?shù)設(shè)計(jì)完畢了。
/* 初始化順序線性表 */
Status InitList(LinkList *L)
{
*L=(LinkList)malloc(sizeof(Node)); /* 產(chǎn)生頭結(jié)點(diǎn),并使L指向此頭結(jié)點(diǎn) */
if(!(*L)) /* 存儲(chǔ)分配失敗 */
{
return ERROR;
}
(*L)->next=NULL; /* 指針域?yàn)榭?*/
return OK;
}
*L=(LinkList)malloc(sizeof(Node)); 這一句代碼里面瞒津,L是個(gè)指針蝉衣,指向頭結(jié)點(diǎn)。L就是指頭結(jié)點(diǎn)巷蚪。if(!(L)) 就是頭結(jié)點(diǎn)分配失敗买乃。
單鏈表的插入與遍歷操作
假設(shè)存儲(chǔ)元素e的結(jié)點(diǎn)為s,要實(shí)現(xiàn)結(jié)點(diǎn)p钓辆、p->next和s之間邏輯關(guān)系的變化,只需將結(jié)點(diǎn)s插入到結(jié)點(diǎn)p和p->next之間即可。
先把結(jié)點(diǎn)s的指針next指向ai+1前联,即 s->next = p->next. 然后再把a(bǔ)i的指針next指向s功戚,即 p->next = s. 用代碼描述則為:
s->next = p->next;
p->next = s;
也就是說讓p的后繼結(jié)點(diǎn)變成s的后繼結(jié)點(diǎn),而不再是p的后繼似嗤,相當(dāng)于砍斷了p與其后繼結(jié)點(diǎn)的關(guān)聯(lián)啸臀。然后再把結(jié)點(diǎn)s變成 p的后繼結(jié)點(diǎn),s也就變成了 p->next烁落。
如果先p->next=s;再s->next=p->next;會(huì)怎么樣乘粒?此時(shí)第一句會(huì)使得將p->next給覆蓋成s的地址了。那么s->next=p->next伤塌,其實(shí)就等于s->next=s灯萍,這樣真正的擁有ai+1數(shù)據(jù)元素的結(jié)點(diǎn)就沒了上級(jí)。這樣的插入操作就是失敗的
單鏈表第i個(gè)數(shù)據(jù)插入結(jié)點(diǎn)的算法思路:
- 聲明一結(jié)點(diǎn)p指向鏈表第一個(gè)結(jié)點(diǎn)每聪,初始化j從1開始旦棉;
- 當(dāng)j < i時(shí),就遍歷鏈表药薯,讓p的指針向后移動(dòng)绑洛,不斷指向下一結(jié)點(diǎn),j累加1;
- 若到鏈表末尾p為空童本,則說明第i個(gè)元素不存在真屯;
- 否則査找成功,在系統(tǒng)中生成一個(gè)空結(jié)點(diǎn)s;
- 將數(shù)據(jù)元素e賦值給s->data;
- 單鏈表的插入標(biāo)準(zhǔn)語(yǔ)句s->next=p->next; p->next=s;
- 返回成功穷娱。
函數(shù)設(shè)計(jì)如下:
/* 初始條件:順序線性表L已存在,1≤i≤ListLength(L)绑蔫, */
/* 操作結(jié)果:在L中第i個(gè)位置之前插入新的數(shù)據(jù)元素e,L的長(zhǎng)度加1 */
Status ListInsert(LinkList *L,int i,ElemType e)
{
int j;
LinkList p,s;
p = *L; /* 聲明一個(gè)結(jié)點(diǎn) p鄙煤,指向頭結(jié)點(diǎn) */
j = 1;
while (p && j < i) /* 尋找第i個(gè)結(jié)點(diǎn) */
{
p = p->next;
++j;
}
if (!p || j > i)
return ERROR; /* 第i個(gè)元素不存在 */
s = (LinkList)malloc(sizeof(Node)); /* 生成新結(jié)點(diǎn)(C語(yǔ)言標(biāo)準(zhǔn)函數(shù)) */
s->data = e;
s->next = p->next; /* 將p的后繼結(jié)點(diǎn)賦值給s的后繼 */
p->next = s; /* 將s賦值給p的后繼 */
return OK;
}
單鏈表的刪除某個(gè)元素的操作
單鏈表刪除第i個(gè)數(shù)據(jù)結(jié)點(diǎn)的算法思路:
- 聲明一結(jié)點(diǎn)p指向鏈表第一個(gè)結(jié)點(diǎn)晾匠,初始化j從1開始;
- 當(dāng)j < i時(shí),就遍歷鏈表梯刚,讓p的指針向后移動(dòng)凉馆,不斷指向下一個(gè)結(jié)點(diǎn),j累加 1亡资;
- 若到鏈表末尾p為空澜共,則說明第i個(gè)元素不存在;
- 否則査找成功锥腻,將欲刪除的結(jié)點(diǎn)p->next賦值給q;
- 單鏈表的刪除標(biāo)準(zhǔn)語(yǔ)句p->next=q->next;
- 將q結(jié)點(diǎn)中的數(shù)據(jù)賦值給e,作為返回嗦董;
- 釋放q結(jié)點(diǎn);
- 返回成功瘦黑。
設(shè)計(jì)的函數(shù)如下:
/* 初始條件:順序線性表L已存在京革,1≤i≤ListLength(L) */
/* 操作結(jié)果:刪除L的第i個(gè)數(shù)據(jù)元素奇唤,并用e返回其值,L的長(zhǎng)度減1 */
Status ListDelete(LinkList *L,int i,ElemType *e)
{
int j;
LinkList p,q;
p = *L; // 聲明一結(jié)點(diǎn)p指向鏈表第一個(gè)結(jié)點(diǎn)
j = 1;
while (p->next && j < i) /* 遍歷尋找第i個(gè)元素 */
{
p = p->next;
++j;
}
if (!(p->next) || j > i)
return ERROR; /* 第i個(gè)元素不存在 */
q = p->next;
p->next = q->next; /* 將q的后繼賦值給p的后繼 */
*e = q->data; /* 將q結(jié)點(diǎn)中的數(shù)據(jù)給e */
free(q); /* 讓系統(tǒng)回收此結(jié)點(diǎn)匹摇,釋放內(nèi)存 */
return OK;
}
分析一下剛才我們的單鏈表插入和刪除算法咬扇,我們發(fā)現(xiàn),它們其實(shí)都是由兩部分組成:第一部分就是遍歷査找第i個(gè)元素廊勃;第二部分就是插入和刪除元素懈贺。
從整個(gè)算法來說,我們很容易推導(dǎo)出:它們的時(shí)間復(fù)雜度都是0(n)坡垫。如果在我們不知道第i個(gè)元素的指針位置梭灿,單鏈表數(shù)據(jù)結(jié)構(gòu)在插入和刪除操作上,與線性表的順序存儲(chǔ)結(jié)構(gòu)是沒有太大優(yōu)勢(shì)的冰悠。但如果我們希望從第i個(gè)位置堡妒,插入10個(gè)元素,對(duì)于順序存儲(chǔ)結(jié)構(gòu)意味著屿脐,每一次插入都需要移動(dòng)n-i個(gè)元素涕蚤,每次都是0(n〕。而單鏈表的诵,我們只需要在第一次時(shí)万栅,找到第i個(gè)位置的指針,此時(shí)為0(n),接下來只是簡(jiǎn)單地通過賦值移動(dòng)指針而已西疤,時(shí)間復(fù)雜度都是0(1)烦粒。顯然,對(duì)于插入或刪除數(shù)據(jù)越頻繁的操作代赁,單鏈表的效率優(yōu)勢(shì)就越是明顯扰她。
獲取單鏈表中的指定位置的元素
在單鏈表中,由于第i個(gè)元素到底在哪是沒辦法一開始就知道芭碍,必須得從頭開始找徒役。我們可以先設(shè)計(jì)一下單鏈表實(shí)現(xiàn)獲取第i個(gè)元素的數(shù)據(jù)的操作GetElem()函數(shù)。
聲明一個(gè)結(jié)點(diǎn)p指向鏈表第一個(gè)結(jié)點(diǎn)窖壕,初始化j從1開始忧勿;
當(dāng)j < i時(shí),就遍歷鏈表瞻讽,讓p的指針向后移動(dòng)鸳吸,不斷指向下一結(jié)點(diǎn),j累加1;
若到鏈表末尾P為空速勇,則說明第i個(gè)元素不存在晌砾;
-
否則査找成功,返回結(jié)點(diǎn)p的數(shù)據(jù)烦磁。
函數(shù)設(shè)計(jì)如下:/* 初始條件:順序線性表L已存在养匈,1≤i≤ListLength(L) */ /* 操作結(jié)果:用e返回L中第i個(gè)數(shù)據(jù)元素的值 */ Status GetElem(LinkList L,int i,ElemType *e) { int j; LinkList p; /* 聲明一結(jié)點(diǎn)p */ p = L->next; /* 讓p指向鏈表L的第一個(gè)結(jié)點(diǎn) */ j = 1; /* j為計(jì)數(shù)器 */ while (p && j < i) /* p不為空或者計(jì)數(shù)器j還沒有等于i時(shí)哼勇,循環(huán)繼續(xù) */ { p = p->next; /* 讓p指向下一個(gè)結(jié)點(diǎn) */ ++j; } if ( !p || j>i ) return ERROR; /* 第i個(gè)元素不存在 */ *e = p->data; /* 取第i個(gè)元素的數(shù)據(jù) */ return OK; }
說白了,就是從頭開始找呕乎,直到第i個(gè)元素為止猴蹂。由于這個(gè)算法的時(shí)間復(fù)雜度取決于i的位置,當(dāng)i=l時(shí)楣嘁,則不需遍歷,第一個(gè)就取出數(shù)據(jù)了珍逸,而當(dāng)i=n時(shí)則遍歷n-1次才可以逐虚。因此最壞情況的時(shí)間復(fù)雜度是0(n)。
用while的條件控制谆膳,當(dāng)循環(huán)執(zhí)行完畢之后就可以鎖定目標(biāo)結(jié)點(diǎn)p叭爱。 這里的主要核心思想就是“工作指針后移”,就是先聲明一個(gè)結(jié)點(diǎn)漱病,這個(gè)結(jié)點(diǎn)與鏈表的首元結(jié)點(diǎn)一致买雾,循環(huán)遍歷下去。
查找某數(shù)在單鏈表中的位置
這里也同樣需要用到“工作指針”杨帽。首先聲明一個(gè)工作指針漓穿,并讓它指向鏈表的首元結(jié)點(diǎn) LinkList p=L->next。參數(shù) L 其實(shí)就是頭指針注盈,L->next 就是頭結(jié)點(diǎn)晃危。然后用工作指針遍歷鏈表,當(dāng) p->data 與傳入的查找數(shù) e 相等老客,返回其位置即可僚饭。
方法體設(shè)計(jì)如下:
/* 初始條件:順序線性表L已存在 */
/* 操作結(jié)果:返回L中第1個(gè)與e滿足關(guān)系的數(shù)據(jù)元素的位序。 */
/* 若這樣的數(shù)據(jù)元素不存在胧砰,則返回值為0 */
int LocateElem(LinkList L,ElemType e)
{
int i=0;
LinkList p=L->next;
while(p)
{
i++;
if(p->data==e) /* 找到這樣的數(shù)據(jù)元素 */
return i;
p=p->next;
}
return 0;
}
用頭插法實(shí)現(xiàn)單鏈表整表創(chuàng)建
單鏈表整表創(chuàng)建的算法思路:
- 聲明一結(jié)點(diǎn)p和計(jì)數(shù)器變量i鳍鸵;
- 初始化一空鏈表L;
- 讓L的頭結(jié)點(diǎn)的指針指向NULL,即建立一個(gè)帶頭結(jié)點(diǎn)的單鏈表尉间;
- 循環(huán):
- 生成一新結(jié)點(diǎn)賦值給p;
- 隨機(jī)生成一數(shù)字賦值給P的數(shù)據(jù)域p->data;
- 將p插入到頭結(jié)點(diǎn)與前一新結(jié)點(diǎn)之間偿乖。
頭插法創(chuàng)建鏈表的函數(shù):
/* 隨機(jī)產(chǎn)生n個(gè)元素的值,建立帶表頭結(jié)點(diǎn)的單鏈線性表L(頭插法) */
void CreateListHead(LinkList *L, int n)
{
LinkList p;
int i;
srand(time(0)); /* 初始化隨機(jī)數(shù)種子 */
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL; /* 先建立一個(gè)帶頭結(jié)點(diǎn)的單鏈表 */
for (i=0; i < n; i++)
{
p = (LinkList)malloc(sizeof(Node)); /* 生成新結(jié)點(diǎn) */
p->data = rand()%100+1; /* 隨機(jī)生成100以內(nèi)的數(shù)字 */
p->next = (*L)->next;
(*L)->next = p; /* 插入到表頭 */
}
}
用尾插法實(shí)現(xiàn)單鏈表整表創(chuàng)建
我們把每次新結(jié)點(diǎn)都插在終端結(jié)點(diǎn)的后面乌妒,這種算法稱之為尾插法汹想。
/* 隨機(jī)產(chǎn)生n個(gè)元素的值,建立帶表頭結(jié)點(diǎn)的單鏈線性表L(尾插法) */
void CreateListTail(LinkList *L, int n)
{
LinkList p,r;
int i;
srand(time(0)); /* 初始化隨機(jī)數(shù)種子 */
*L = (LinkList)malloc(sizeof(Node)); /* L為整個(gè)線性表 */
r=*L; /* r為指向尾部的結(jié)點(diǎn) */
for (i=0; i < n; i++)
{
p = (Node *)malloc(sizeof(Node)); /* 生成新結(jié)點(diǎn) */
p->data = rand()%100+1; /* 隨機(jī)生成100以內(nèi)的數(shù)字 */
r->next=p; /* 將表尾終端結(jié)點(diǎn)的指針指向新結(jié)點(diǎn) */
r = p; /* 將當(dāng)前的新結(jié)點(diǎn)定義為表尾終端結(jié)點(diǎn) */
}
r->next = NULL; /* 表示當(dāng)前鏈表結(jié)束 */
}
完整的程序如下:
#include "stdio.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存儲(chǔ)空間初始分配量 */
typedef int Status;/* Status是函數(shù)的類型,其值是函數(shù)結(jié)果狀態(tài)代碼撤蚊,如OK等 */
typedef int ElemType;/* ElemType類型根據(jù)實(shí)際情況而定古掏,這里假設(shè)為int */
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
/* 定義LinkList */
typedef struct Node *LinkList;
/* 初始化順序線性表 */
Status InitList(LinkList *L)
{
*L=(LinkList)malloc(sizeof(Node)); /* 產(chǎn)生頭結(jié)點(diǎn),并使L指向此頭結(jié)點(diǎn) */
if(!(*L)) /* 存儲(chǔ)分配失敗 */
{
return ERROR;
}
(*L)->next=NULL; /* 指針域?yàn)榭?*/
return OK;
}
/* 初始條件:順序線性表L已存在。操作結(jié)果:返回L中數(shù)據(jù)元素個(gè)數(shù) */
int ListLength(LinkList L)
{
int i=0;
LinkList p=L->next; /* p指向第一個(gè)結(jié)點(diǎn) */
while(p)
{
i++;
p=p->next;
}
return i;
}
/* 初始條件:順序線性表L已存在 */
/* 操作結(jié)果:依次對(duì)L的每個(gè)數(shù)據(jù)元素輸出 */
Status ListTraverse(LinkList L)
{
LinkList p=L->next;
while(p)
{
visit(p->data);
p=p->next;
}
printf("\n");
return OK;
}
Status visit(ElemType c)
{
printf("-> %d ",c);
return OK;
}
/* 初始條件:順序線性表L已存在侦啸,1≤i≤ListLength(L) */
/* 操作結(jié)果:用e返回L中第i個(gè)數(shù)據(jù)元素的值 */
Status GetElem(LinkList L,int i,ElemType *e)
{
int j;
LinkList p; /* 聲明一結(jié)點(diǎn)p */
p = L->next; /* 讓p指向鏈表L的第一個(gè)結(jié)點(diǎn) */
j = 1; /* j為計(jì)數(shù)器 */
while (p && j < i) /* p不為空或者計(jì)數(shù)器j還沒有等于i時(shí)槽唾,循環(huán)繼續(xù) */
{
p = p->next; /* 讓p指向下一個(gè)結(jié)點(diǎn) */
++j;
}
if ( !p || j>i )
return ERROR; /* 第i個(gè)元素不存在 */
*e = p->data; /* 取第i個(gè)元素的數(shù)據(jù) */
return OK;
}
/* 初始條件:順序線性表L已存在 */
/* 操作結(jié)果:返回L中第1個(gè)與e滿足關(guān)系的數(shù)據(jù)元素的位序丧枪。 */
/* 若這樣的數(shù)據(jù)元素不存在,則返回值為0 */
int LocateElem(LinkList L,ElemType e)
{
int i=0;
LinkList p=L->next;
while(p)
{
i++;
if(p->data==e) /* 找到這樣的數(shù)據(jù)元素 */
return i;
p=p->next;
}
return 0;
}
/* 隨機(jī)產(chǎn)生n個(gè)元素的值庞萍,建立帶表頭結(jié)點(diǎn)的單鏈線性表L(頭插法) */
void CreateListHead(LinkList *L, int n)
{
LinkList p;
int i;
srand(time(0)); /* 初始化隨機(jī)數(shù)種子 */
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL; /* 先建立一個(gè)帶頭結(jié)點(diǎn)的單鏈表 */
for (i=0; i < n; i++)
{
p = (LinkList)malloc(sizeof(Node)); /* 生成新結(jié)點(diǎn) */
p->data = rand()%100+1; /* 隨機(jī)生成100以內(nèi)的數(shù)字 */
p->next = (*L)->next;
(*L)->next = p; /* 插入到表頭 */
}
}
/* 隨機(jī)產(chǎn)生n個(gè)元素的值拧烦,建立帶表頭結(jié)點(diǎn)的單鏈線性表L(尾插法) */
void CreateListTail(LinkList *L, int n)
{
LinkList p,r;
int i;
srand(time(0)); /* 初始化隨機(jī)數(shù)種子 */
*L = (LinkList)malloc(sizeof(Node)); /* L為整個(gè)線性表 */
r=*L; /* r為指向尾部的結(jié)點(diǎn) */
for (i=0; i < n; i++)
{
p = (Node *)malloc(sizeof(Node)); /* 生成新結(jié)點(diǎn) */
p->data = rand()%100+1; /* 隨機(jī)生成100以內(nèi)的數(shù)字 */
r->next=p; /* 將表尾終端結(jié)點(diǎn)的指針指向新結(jié)點(diǎn) */
r = p; /* 將當(dāng)前的新結(jié)點(diǎn)定義為表尾終端結(jié)點(diǎn) */
}
r->next = NULL; /* 表示當(dāng)前鏈表結(jié)束 */
}
/* 初始條件:順序線性表L已存在,1≤i≤ListLength(L), */
/* 操作結(jié)果:在L中第i個(gè)位置之前插入新的數(shù)據(jù)元素e钝计,L的長(zhǎng)度加1 */
Status ListInsert(LinkList *L,int i,ElemType e)
{
int j;
LinkList p,s;
p = *L; /* 聲明一個(gè)結(jié)點(diǎn) p恋博,指向頭結(jié)點(diǎn) */
j = 1;
while (p && j < i) /* 尋找第i個(gè)結(jié)點(diǎn) */
{
p = p->next;
++j;
}
if (!p || j > i)
return ERROR; /* 第i個(gè)元素不存在 */
s = (LinkList)malloc(sizeof(Node)); /* 生成新結(jié)點(diǎn)(C語(yǔ)言標(biāo)準(zhǔn)函數(shù)) */
s->data = e;
s->next = p->next; /* 將p的后繼結(jié)點(diǎn)賦值給s的后繼 */
p->next = s; /* 將s賦值給p的后繼 */
return OK;
}
/* 初始條件:順序線性表L已存在,1≤i≤ListLength(L) */
/* 操作結(jié)果:刪除L的第i個(gè)數(shù)據(jù)元素私恬,并用e返回其值债沮,L的長(zhǎng)度減1 */
Status ListDelete(LinkList *L,int i,ElemType *e)
{
int j;
LinkList p,q;
p = *L;
j = 1;
while (p->next && j < i) /* 遍歷尋找第i個(gè)元素 */
{
p = p->next;
++j;
}
if (!(p->next) || j > i)
return ERROR; /* 第i個(gè)元素不存在 */
q = p->next;
p->next = q->next; /* 將q的后繼賦值給p的后繼 */
*e = q->data; /* 將q結(jié)點(diǎn)中的數(shù)據(jù)給e */
free(q); /* 讓系統(tǒng)回收此結(jié)點(diǎn),釋放內(nèi)存 */
return OK;
}
int main()
{
LinkList L;
Status i;
int j,k,pos,value;
char opp;
ElemType e;
i=InitList(&L);
printf("鏈表L初始化完畢本鸣,ListLength(L) =%d\n",ListLength(L));
printf("\n1.整表創(chuàng)建(頭插法) \n2.整表創(chuàng)建(尾插法) \n3.遍歷操作 \n4.插入操作 \n5.刪除操作 \n6.獲取結(jié)點(diǎn)數(shù)據(jù) \n7.查找某個(gè)數(shù)是否在鏈表中 \n0.退出 \n請(qǐng)選擇你的操作:\n");
while(opp != '0'){
scanf("%c",&opp);
switch(opp){
case '1':
CreateListHead(&L,10);
printf("整體創(chuàng)建L的元素(頭插法):\n");
ListTraverse(L);
printf("\n");
break;
case '2':
CreateListTail(&L,10);
printf("整體創(chuàng)建L的元素(尾插法):\n");
ListTraverse(L);
printf("\n");
break;
case '3':
ListTraverse(L);
printf("\n");
break;
case '4':
printf("要在第幾個(gè)位置插入元素疫衩?");
scanf("%d",&pos);
printf("插入的元素值是多少?");
scanf("%d",&value);
ListInsert(&L,pos,value);
ListTraverse(L);
printf("\n");
break;
case '5':
printf("要?jiǎng)h除第幾個(gè)元素荣德?");
scanf("%d",&pos);
ListDelete(&L,pos,&e);
printf("刪除第%d個(gè)元素成功闷煤,現(xiàn)在鏈表為:\n", pos);
ListTraverse(L);
printf("\n");
break;
case '6':
printf("你需要獲取第幾個(gè)元素?");
scanf("%d",&pos);
GetElem(L,pos,&e);
printf("第%d個(gè)元素的值為:%d\n", pos, e);
printf("\n");
break;
case '7':
printf("輸入你需要查找的數(shù):");
scanf("%d",&pos);
k=LocateElem(L,pos);
if(k)
printf("第%d個(gè)元素的值為%d\n",k,pos);
else
printf("沒有值為%d的元素\n",pos);
printf("\n");
break;
case '0':
exit(0);
}
}
}