不過妳看樓主的問題,好像是不理解鏈表。這不涉及面向對象。
給妳壹些資料,來自譚浩強的C編程。
我能到達/c嗎?m = 9d 78d 513d 9d 437 a 84 f 9 AE 4690 c66c 0161e 43 f 1652 BD 6a 00209d 5843 b 97732d 405017 e2ac 26520775d 0d 20 b 1616d 9484 b 9 ef 00p = 85769 a44c e 9711a 05 f 9 F8 c 2354 & amp;用戶=百度
看,這個代碼會更自然。
10.7用指針處理鏈表
10.7.1鏈條標簽概述
鏈表是壹種常見而重要的數據結構。它是壹種動態分配存儲的結構。眾所周知,當用數組存儲數據時,
必須預先定義固定的長度(即元素的數量)。比如有的班有100個學生,有的班只有30個學生。如果要用同壹個數組連續存儲不同班級的學生數據,必須定義壹個長度為100的數組。如果很難預先確定壹個班級的最大學生人數,那麽必須將數組設置得足夠大。為了存儲任何班級的學生數據,顯然這樣會浪費內存。鏈表沒有這個缺點,按需開放內存單元。圖10.11表示鏈表的最簡單結構(單向鏈表)。鏈表有壹個“head pointer”變量,在圖中用head表示,它存儲壹個地址。
該地址指向壹個元素。鏈表中的每個元素稱為壹個“節點”,每個節點應該包括兩部分:壹部分是用戶實際需要的數據,另壹部分是下壹個節點的地址。可以看到,head指向第壹個元素;第壹個元素指向第二個元素;.....,直到最後壹個不再指向其他元素的元素,稱為“頁腳”,其地址部分放壹個“NULL”(意為“空地址”)。鏈表到此結束。
可以看出,鏈表中的每個元素可能不會連續存儲在存儲器中。要找到壹個元素,首先要找到上壹個元素,然後可以根據它提供的下壹個元素的地址找到下壹個元素。
如果不提供“head ”,就不能訪問整個鏈表。鏈表就像同壹條鏈條,壹環扣壹環,中間不可斷開。舉個通俗的例子,幼兒園老師領著小朋友出去散步,老師牽著第壹個小朋友的手,第壹個小朋友用另壹只手牽著第二個小朋友...這是壹條“鏈子”,最後壹個孩子有壹條。
可以看出,這個鏈表的數據結構必須使用指針變量來實現。
也就是說,壹個節點應該包含壹個指針變量來存儲下壹個節點的地址。
前面介紹了結構變量,它包含幾個成員。這些成員可以是數字類型、字符類型、數組類型或指針類型。這個指針類型可以是指向其他結構類型的數據,也可以指向它所在的結構類型。例如:
結構學生
{ int num
浮動分數;
結構學生*下壹個;
Next是成員名,是指針類型,指向struct student類型的數據(這是next所在的結構類型)。這樣就可以建立壹個鏈表。見圖10.12。
每個節點都屬於structure student類型,其成員next存儲下壹個節點的地址,程序員不需要具體知道地址值。
只需確保將下壹個節點的地址放在前壹個節點的下壹個成員中。
請註意,上面只定義了壹個struct student類型,並沒有實際分配存儲空間。如前所述,鏈表結構動態分配存儲,即在需要時打開壹個節點的存儲單元。如何動態打開和釋放存儲單元?C語言編譯系統的庫函數提供了以下相關函數。
1.malloc(size)在內存的動態存儲區域中分配壹個大小為的連續空間。
這個函數的值(也就是“返回值”)是壹個指針,它的值是分配域的起始地址。如果該函數執行失敗,返回值為0。
2.calloc(n,size)在內存的動態區域存儲中分配n個長度為size的連續空間。該函數返回分配域的初始地址;如果分配不成功,返回0。
3.free(ptr)釋放ptr指向的內存區域。ptr是最後壹次調用ca11或ma11oc函數返回的值。
在上面三個函數中,參數n和size是整數,ptr是字符指針。
請註意,C版本提供的很多malloc和call0c函數都是獲取字符數據的指針。新標準C提供的ma110c和ca11oc函數被定義為void*類型。
有了本節介紹的初步知識,就可以對下面的鏈表進行操作(包括建立鏈表,在鏈表中插入或刪除節點等。).有些概念需要在後期的應用中逐步建立和掌握。
10.7.2建立鏈表
所謂鏈表構建,是指從零開始構建壹個鏈表,即逐個輸入各個節點的數據,建立前後階段的關系。這裏舉例說明如何建立壹個鏈表。
【例10.7】寫壹個函數,用五個學生的數據建立壹個單向鏈表。
先考慮實現這個要求的算法(見圖10。13).
設置三個指針變量:head,p1,p2,都指向結構類型數據。首先用mal1oc函數打開壹個節點,讓p1,p2指向它。
然後從鍵盤讀取壹個學生的數據到pl指向的節點。我們同意學生人數不會為零。如果輸入的學號為0,則表示建立鏈表的過程完成,該節點不應該連接到鏈表。先使head的值為NULL(即等於0),這是鏈表為“空”的情況(即head不指向任何節點,鏈表中沒有節點),然後添加壹個節點使head指向該節點。
如果輸入pl >:當num不等於0,輸入第壹個節點數據(n=1)時,設head=p1。
也就是把p1的值賦給頭部,也就是讓頭部也指向新打開的節點(圖10.14)。p1指向的新打開的節點成為鏈表中的第壹個節點。然後打開另壹個節點讓p1指向它,然後讀入這個節點的數據(見圖65438) num!=0,應該鏈接到第二個節點(n=2),因為n!=1,p1的值賦給p2->;接下來,也就是讓第壹個節點的下壹個成員指向第二個節點(見圖10.15(b))。然後設p2=p1。
即讓p2指向剛建立的節點,如圖10.15(c)。然後打開另壹個節點,讓pl指向它,讀取這個節點的數據(如圖10.16(a))。在第三個周期,因為n=3(n!=1),pl的值賦給p2->;接下來,也就是把第三個節點連接到第二個節點上,使p2=p1,使p2指向最後壹個節點(見圖10.16(b))。
然後打開壹個新節點,讓pl指向它。
輸入該節點的數據(見圖10.17(a))。因為pl->;如果num的值為0,將不再執行循環,並且這個新節點不應該連接到鏈表。此時,NULL將被賦給p2->;接下來,如圖10.17(b)所示。建立鏈表的過程到此結束。pl最後指向的節點沒有鏈接到鏈表中,第三個節點的下壹個成員的值為NULL,不指向任何節點。雖然pl指向新打開的節點,但是從鏈表中找不到。
建立鏈表的功能可以如下:
#定義NULL 0
#define LEN sizeof(結構學生)
結構學生
{ 1 ONG num;
浮動分數;
結構學生*下壹個;
};
int n;
struct student *creat()
/*這個函數返回壹個指向鏈表頭的指針*/
{結構學生*頭;
struct student *p1,* p2
n = 0;
p1=p2=(結構學生*)mal 1oc(LEN);/*創建新模塊*/
scanf("%ld,%f ",& amppl-gt;數字& amppl-gt;分數);
head=NULL:
while(pl-->;num!=0)
{n=n十1;
if(n = = 1)head = pl;
else p2-> next = p 1;
p2 = pl
pl=(結構學生*)malloc(LEN);
scanf("%1d,%f ",& ampp 1->;數字& amppl-gt;分數);
}
P2 one > next = NULL;
返回(頭);
}
請註意:
(1)第壹行是#define命令行,其中NULL代表0,表示“空地址”。第二行是LEN,代表struct student結構類型的數據長度。
Sizeof是用於查找字節數的運算符。
(2)第9行定義了壹個creat函數,它是壹個指針類型,也就是這個函數帶回壹個指針值,指向壹個struct student類型的數據。實際上這個creat函數帶回了壹個鏈(3)malloc(LEN)來開辟壹個LEN長度的內存區域,這個長度已經定義為sizeof(struct student),也就是結構struct student的長度。在壹般系統中,CREAT函數返回壹個鏈(LEN)。
P1、p2是指向struct student類型數據的指針變量,它們引用不同類型的數據,這是不可接受的。所以需要使用強制類型轉換的方法使它們的類型壹致,在malloc (LEN)前加上“(struct student*)”。它的作用是將malloc返回的指針轉換成指向struct student類型數據的指針。註意“*”號不能省略,否則會轉換成struct student類型而不是指針類型。
(4)最後壹行返回後的參數是head(head已經定義為指針變量,指向struct student類型的數據)。因此,函數返回head的值,這是鏈表的頭地址。
(5)n是節點數。
(6)這個算法的思想是:讓pl指向新打開的節點,p2指向鏈表中的最後壹個節點,將pl指向的節點連接在p2指向的節點後面,用“p2->;Next=pl”。
詳細介紹了鏈表的建立過程,
如果讀者清楚建立鏈表的過程,下面介紹的刪除和插入過程就更容易理解了。
10.7.3輸出鏈小麥
依次輸出鏈表中每個節點的數據。這個問題比較好處理。首先要知道鏈表head元素的地址,也就是要知道head的值。然後,我們要設置壹個指針變量p,指向第壹個節點,輸出p指向的節點,然後將p後移壹個節點,然後輸出,直到鏈表的結束節點。
【例10.8】編寫輸出鏈表的函數print。
作廢打印(表頭)
結構學生*頭;
{ struct student * p;
printf(" \n現在,這些% d記錄是:\ n ",n);
p =頭部;
如果(頭!=空)
做
{printf("%ld%5.1f\ ",p-->;num,p-& gt;分數);
P=p one >下壹個;
}while(p!= NULL);
該算法可以用圖10.18來表示。
該過程可以如圖10.19所示。P先指向第壹個節點,第壹個節點輸出後,P移動到圖中虛線位置,指向第二個節點。在程序中,p = p->;next的作用是將p最初指向的節點中next的值賦給p,
而p->;next的值是第二個節點的起始地址。把它賦給P意味著P指向第二個節點。
head的值通過參數傳遞,即把已有鏈表的頭指針傳遞給被調用的函數,節點從打印函數中head指向的第壹個節點開始順序輸出。
10.7.4刪除鏈麥
有壹個鏈表,我想刪除其中壹個節點。如何考慮這個問題的算法,我們先打個比方:
壹群孩子(A,B,C,D,E)手拉著手。如果壹個孩子(C)因為某件事要離隊,但是陣型不變,只要從兩邊松開C的手,B就可以和D手牽手代替,如圖10.20。圖10.20(a)是原隊,如圖665所示。
同樣,從鏈表中刪除壹個節點並不是真的把它從內存中抹掉,而是把它從鏈表中分離出來,也就是改變了鏈接關系。
【例10.9】寫壹個函數刪除指定的節點。
我們使用指定的學號作為刪除節點的符號。例如,輸入89103表示需要刪除學號為89103的節點。解決問題的思路是:從P指向的第壹個節點開始,檢查這個節點中的num值是否等於需要刪除的學號。如果相等,則刪除該節點。如果不是,則將P移回壹個節點,依此類推。往下走,直到妳碰到桌子的盡頭。
可以設置兩個指針變量P1和p2,讓P1先指向第壹個節點(圖10.5438+0 (a))。如果它不是第壹個要刪除的節點,
然後將pl指回下壹個節點(pl->;下壹個被分配給P1)。在此之前,要將pl的值賦給p2,使p2指向剛剛檢查過的節點,如圖10.21(b)。於是,P壹次又壹次地向後移動,直到找到要刪除的節點或者通過檢查完整鏈表找不到要刪除的節點。如果發現壹個節點被刪除,要區分兩種情況:①。下壹個被分配給負責人。見圖10.438+0 (c)。
這時,頭部指向原來的第二個節點。雖然第壹個節點仍然存在,但它已經從鏈表中分離出來,因為鏈表中沒有元素或頭指針指向它。雖然p1還是指向它,還是指向第二個節點,但是還是無濟於事。現在鏈表中的第壹個節點是原來的第二個節點,原來的第壹個節點“丟失”了。②如果不是第壹個被刪除的節點,則為pl 1。Next賦值給p2 > Next,如圖10.21(d). p2->;Next原本指向pl指向的節點(圖中第二個節點),現在指向p2->;接下來改為指向p 1->;下壹個指向的節點
指向的節點。pl不再是鏈表的壹部分。
我們還需要考慮鏈表為空(沒有節點)且鏈表中沒有要刪除的節點的情況。
圖10.22給出了解決這個問題的算法。
刪除節點的函數del如下:
struct student *del(頭,數)
結構學生*頭;
1 ONG num;
{struct student *p1,* p2
if(head = = NULL){ printf(" \ n list NULL!\ n ");轉到結尾;}
p 1 =頭;
whi1e(編號!= pl one > num & amp& amppl-gt;下壹個!A NULL)/*pl沒有指向妳要找的節點,有壹個節點點*/
{ p2 = p 1;pl = pl-->;接下來;}/*向後移動壹個節點*/
if(num = = pl-->;Num) /*找到*/
{ if(n 1 = = head)head = pl-->;接下來;/*如果pl指向頭節點,則將第二個節點地址給head*/
e 1se p2->;Next = pl one > next/*否則,將下壹個節點地址分配給前壹個節點地址* \
printf("delete:%ld\n ",num);
n = n-1;
}
else printf("%ld未找到!\n ",編號);/*找不到節點*/
結束:
返回(頭);
}
該函數的類型是指向struct student類型數據的指針。
它的值是鏈表的頭指針。要刪除的函數參數head和學號num.head的值可以在函數執行期間改變(當刪除第壹個節點時)。
10.7.5鏈表上的插入操作
將節點插入到現有的鏈表中。讓現有鏈表中每個節點的成員項num(學號)按照學號從小到大的順序排列。
使用指針變量p0指向要插入的節點,使用pl指向第壹個節點。見圖10.23(a)。
將p0 a >: Num和pl->;Num,如果P0 >:num & gt;pl-gt;Num,要插入的節點不應該插在pl指向的節點之前。此時,pl將向後移動,p2將指向pl剛剛指向的節點,如圖10.23(b)所示。然後p 1 >;Num和p0 > Num比率。如果還是P0->;如果num大,pl要繼續後移,直到P0->;努努姆。此時,在pl引用的節點之前插入p0引用的節點。但是,如果p1引用了頁腳節點,pl應該不會向後移動。如果P0->;Num大於所有節點的數量,
那麽p0所指示的節點應該被插入到鏈表的末尾。
如果插入位置既不在第壹個節點之前也不在頁腳節點之後,p0的值賦給p2->;接下來,即使p2是>:Next指向要插入的節點,然後將pl的值賦給P0->;接下來,即使妳得到p0 > Next指向pl所指向的變量。見圖10.23(c)。如您所見,在第壹個節點和第二個節點之間插入了壹個新節點。
如果插入位置在第壹個節點之前(即當pl等於head時),
然後將p0賦給head,將p1賦給P0->;下壹個。見圖10.23(d)。如果要在頁腳後插入,p0應該賦給pl->;接下來,NULL被賦給P0->;接下來,請參見圖10.23(e)。
上述算法可以用圖10.24來表示。
【例10。10]插入節點的函數insert如下。
結構學生*插入(頭部,螺柱)
結構學生*頭,*螺柱:
{struct student *p0,*p1,* p2
pl =頭部;/*使pl指向第壹個節點*/
p0 =螺柱;/*p0指向要插入的節點*/
If (head==NULL)/*結果是壹個空表*/
{ head = p0p0i > next = NULL;}/*將p0指向的節點作為第壹個節點*/
其他
{ while((P0 > num & gt;pl-gt;num)& amp;& amp(pl I >下壹個!=NULL))
{ p2 = p 1;p 1 = pl-->;接下來;}/*p2指向pl剛剛指向的節點,p1向後移動壹個節點*/
如果(P0-& gt;numnext = p0/*在p2指向的節點後插入*/
P0—& gt;next = p 1;}
其他
{ pl I > next = p0p0i > next = NULL;}}/*在最後壹個節點後插入*/
n = n+1;/*節點數加上1*/
返回(頭);
}
函數參數是head和stud.stud也是指針變量,要插入的節點的地址從實參發送到stud。語句p0=stud的作用是讓p0指向要插入的節點。
函數類型是指針類型,函數值是鏈表的起始地址頭。
把上面的創建、輸出、刪除、插入等功能組織成壹個程序,用主函數作為主函數。妳可以寫下面的main函數。
主()
{struct學生*頭,stu
1 ONG de 1 _ num;
printf("輸入記錄:\ n ");
head = creat();/*返回頭指針*/
打印(頭);/*輸出所有節點*/
printf(" \ n輸入已刪除的0號:");
scanf("%ld ",& ampde 1 _ μm);/*輸入要刪除的學號*/
head=del(head,de 1 _ num);/*已刪除的標題地址*/
打印(頭);/*輸出所有節點*/
prinif("/put the inserted record:")/*輸入要插入的記錄*/
scanf("%ld,%f ",& ampstu . num & amp;stu . score);
head = insert(head & amp;stu);/*回信地址*/
打印(頭);
}
該程序的運行結果是正確的。
它只刪除壹個節點並插入壹個節點。但是如果要插入另壹個節點,重復程序的最後四行,也就是,* * *插入兩個節點。運行結果是錯誤的。
輸入記錄:(創建鏈表)
89101,90
89103,98
89105,76
0,0
現在,這三個記錄是:
89101 90.0
89103 98.0
89105 76.0
輸入已刪除的號碼:
89103(已刪除)
刪除:89103
現在,這兩個記錄是:
89101 90.0 89105 76.0
輸入插入的記錄:89102
90(插入第壹個節點)
現在,這三個記錄是:
89101 90.0
89102 90.0
89105 76.0
輸入插入的記錄:89104,99/(插入第二個節點)
現在,這4個記錄是:
89101 90.0
89104 99.0
89104 99.0
89104 99.0
...
...
(輸出節點數據89104無終止)
請讀者結合main和insert函數來考察為什麽會產生上述運行結果。
產生以上結果的原因是stu是壹個有固定地址的變量。stu節點第壹次插入鏈表,第二次用來插入第二個節點,第壹個節點的數據就被洗掉了。
事實上,沒有創建兩個節點。讀者此時可以根據insert函數繪制鏈表。為了解決這個問題,當每個節點被插入時,必須創建壹個新的存儲區。我們修改main函數,使其可以刪除多個節點(直到要刪除的學號為0)和插入多個節點(直到要插入的學號為0)。
主要功能如下:
主()
{ struct student * head *,stu
1 ONG de 1 _ num;
printf("輸入記錄:/n ");
head = creat();
打印(頭);
printf("/n輸入刪除的號碼:");
scanf("%1d ",& ampdel _ num);
while (de1_num!=0)
{head=del(head,del _ num);
打印(頭);
printf("輸入刪除的號碼:");
scanf("%ld ",& ampdel _ num);
printf(" \ n輸入插入的記錄:");
stu=(結構學生*)malloc(LEN);
scanf("%1d,%f,",& ampstu I > num & amp;stu I > SCOR);
While (stu I > num!=0)
{head=insert(head,stu):
打印(頭);
prinif("輸入插入的記錄:");
stu=(結構學生*)malloc(LEN);
scanf("%1d,%f,& ampstu I > num & amp;Stu I >分數);
}
}
Sum被定義為指針變量。需要插入時,先用malloc函數打開壹個內存區,強制類型轉換後將其起始地址賦給stu,然後在這個結構變量中輸入每個成員的值。對於不同的插入對象,stu的值是不同的,每次指向壹個新的結構變量。調用插入函數時,參數是head和stu。將建立的鏈表的起始地址傳遞給insert函數的形參,將stu(新打開單元的地址)傳遞給形參stud。函數值在插入後返回鏈表的頭指針(地址)。
操作如下所示:
輸入記錄:
89101,99
89103,87
89105,77
0,0
這三項記錄是。
89101 99.0
89103 87.0
89105 77.0
輸入刪除的號碼:89103
刪除:89103
現在,這兩個記錄是:
89101 99.0
89105 77.0
輸入de 1完整編號:89105
刪除:89105
現在,這些l記錄是:
89101 99.0
輸入de 1完整數字:0
1輸入插入的記錄:89104,87
現在,這兩個記錄是:
89101 99.0
89104 87.0
輸入插入的記錄:89106,65
現在,這三個記錄是:
89101 99.0
89104 87.0
89106 65.0
輸入插入的記錄:0,0
請仔細消化這個節目。
指針的應用非常廣泛,包括單向鏈表、環形鏈表、雙向鏈表,以及隊列、樹、棧、圖等數據結構。
對於這些問題的算法,可以從《數據結構>:& gt這裏不詳細介紹課程。