單鏈表常見的創(chuàng)建方法有頭插法和尾插法,這里記錄頭插法創(chuàng)建帶頭結(jié)點(diǎn)的單鏈表具體過程:
以C語言為例,
1)首先使用 typedef 關(guān)鍵字定義結(jié)點(diǎn)數(shù)據(jù)類型
1 typedef struct LNode{
2 int var; //數(shù)據(jù)以整型為例
3 struct LNode* next; //需要定義一個(gè)LNode結(jié)構(gòu)體指針即結(jié)點(diǎn)指針來指向后繼
4 }LNode, *LinkList;
4行的 LNode 和 *LinkList 可有可無,有的話后面定義結(jié)點(diǎn)變量和指針變量時(shí)更方便,不必須在LNode前面加 struct 關(guān)鍵字,可直接這樣定義變量,
LNode l1, l2; //定義結(jié)點(diǎn)變量
LinkList p1, p2; //定義指針變量
與上面 typedef 關(guān)鍵字定義的單鏈表數(shù)據(jù)類型一樣的定義方法:
struct LNode{
int var;
struct LNode* next;
}
若用這種方法定義鏈表結(jié)點(diǎn)類型,定義結(jié)點(diǎn)變量和指針變量時(shí)必須在LNode前帶上struct關(guān)鍵字,即:
struct LNode l1, l2; //定義結(jié)點(diǎn)變量
struct LNode *p1, *p2; //定義指針變量
這兩種定義方法效果是一樣的,都是定義了包含 1個(gè)整型變量的數(shù)據(jù)域 和 1個(gè)后繼指針域的單鏈表結(jié)點(diǎn)類型
2)通過函數(shù) 頭插構(gòu)建鏈表,并返回 LinkList 類型表頭指針變量 L,
算法基本思想:帶有頭結(jié)點(diǎn)的單鏈表有兩類結(jié)點(diǎn),頭結(jié)點(diǎn)和元素結(jié)點(diǎn),頭結(jié)點(diǎn)通常不存儲(chǔ)數(shù)據(jù),用L表示,元素結(jié)點(diǎn)存儲(chǔ)數(shù)據(jù),用s表示
2.1 定義頭結(jié)點(diǎn)指針變量L和元素結(jié)點(diǎn)s
LinkList s, L;
2.2 定義了頭結(jié)點(diǎn)之后,內(nèi)存中尚未分配空間,此時(shí)頭結(jié)點(diǎn)并不真實(shí)存在,需使用malloc關(guān)鍵字分配存儲(chǔ)空間后,并使L指針指之,才真正創(chuàng)建了L頭結(jié)點(diǎn)。由于此時(shí)僅有頭結(jié)點(diǎn),無后繼結(jié)點(diǎn),需將其指針域置空
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;

這時(shí)設(shè)置一個(gè)整型變量x,通過不斷輸入其值,來初始化各結(jié)點(diǎn)數(shù)據(jù)域val,判斷x=9999時(shí)為輸入結(jié)束條件,先任意輸入一個(gè)x,然后通過while循環(huán)來判斷 x值決定是否進(jìn)入添加結(jié)點(diǎn)過程
int x;
scanf("%d", &x);
while(x != 9999){ //進(jìn)入添加結(jié)點(diǎn)過程
... //不斷添加結(jié)點(diǎn)過程
}
添加結(jié)點(diǎn)過程算法如下:
1, 初始化一個(gè)s元素結(jié)點(diǎn),先初始化數(shù)據(jù)域var,然后初始化指針域next
s = (LinkList)malloc(sizeof(LNode));
s->var = x;
s->next = L->next;
先初始化數(shù)據(jù)域var,然后初始化指針域next頭插法是這樣插入新結(jié)點(diǎn)的,新的結(jié)點(diǎn)s始終在當(dāng)前的表中第一個(gè)元素結(jié)點(diǎn)之前
,也就是L->next 之前插入,數(shù)據(jù)輸入順序與最終鏈表結(jié)點(diǎn)順序是相反的,
所以在創(chuàng)建了一個(gè)新的元素結(jié)點(diǎn)s后,需要將其指針域置為L->next, 如圖

2, 初始化了一個(gè)元素結(jié)點(diǎn)s后,此時(shí)有元素結(jié)點(diǎn)了,由于頭結(jié)點(diǎn)L始終必須作為首個(gè)元素結(jié)點(diǎn)的前驅(qū),所以需要頭結(jié)點(diǎn)L的指針域,使當(dāng)前元素結(jié)點(diǎn)s成為其后繼
L->next = s;

3, 這時(shí),一個(gè)元素結(jié)點(diǎn)s就正式被插入到表中了,然后要插入第二個(gè)元素結(jié)點(diǎn)了,根據(jù)while循環(huán)的流程,再次判斷x值需要再次輸入一個(gè)x值,所以需要在while循環(huán)內(nèi)最后一行輸入x數(shù)據(jù)
scanf("%d", &x);
4,若輸入的值非9999,則再次進(jìn)入while循環(huán),反復(fù)執(zhí)行上述流程,不斷插入元素結(jié)點(diǎn)擴(kuò)大單鏈表長,這里贅述再添加一個(gè)元素結(jié)點(diǎn)的過程
又初始化了一個(gè)元素結(jié)點(diǎn)s,狀態(tài)如圖:

按照上述算法,頭插法 新的元素結(jié)點(diǎn)s插入時(shí)始終插入在當(dāng)前表中首個(gè)元素結(jié)點(diǎn)F之前,故需要將其后繼指針域置為當(dāng)前表中首個(gè)元素結(jié)點(diǎn)F,即s->next = L->next, 記住L->next始終指向首個(gè)元素結(jié)點(diǎn),結(jié)果如圖:

元素結(jié)點(diǎn)s被“半插入”到表中后,F已經(jīng)不是絕對(duì)意義上的首個(gè)元素結(jié)點(diǎn)了,此時(shí)需要更改頭結(jié)點(diǎn)L的后繼指針域,將其后繼指針域置為被“半插入”表中的新元素結(jié)點(diǎn)s,這樣,新的元素結(jié)點(diǎn)s正式被插入表中,表長+1,如圖

2.3 上述插入過程的函數(shù)完整實(shí)現(xiàn):
LinkList head_Insert(){
int x;
LinkList s, L;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
scanf("%d", &x);
while(x != 9999){
s = (LinkList)malloc(sizeof(LNode));
s->var = x;
s->next = L->next;
L->next = s;
scanf("%d", &x);
}
return L;
}
- 用printList()函數(shù)遍歷打印單鏈表,接收的形參為要打印的單鏈表,從首個(gè)結(jié)點(diǎn)開始打印
void printList(LinkList L){
while(L != NULL){
printf("%d ", L->var);
L = L->next; //向后遍歷
}
}
4)主函數(shù)main() 流程
需要初始化一個(gè)head 指針變量,來接收head_Insert()函數(shù)所返回的已創(chuàng)建的鏈表頭結(jié)點(diǎn)指針值, 然后將head傳入printList()函數(shù)直接調(diào)用打印輸入單鏈表數(shù)據(jù),由于printList()是從首個(gè)結(jié)點(diǎn)開始打印,而頭結(jié)點(diǎn)不存儲(chǔ)數(shù)據(jù),故傳入第一個(gè)元素結(jié)點(diǎn)
int main(){
LinkList head;
head = head_Insert();
printf("the linklist vars are: \n");
printList(head->next); //由于printList()是從首個(gè)結(jié)點(diǎn)開始打印,而頭結(jié)點(diǎn)不存儲(chǔ)數(shù)據(jù),故傳入第一個(gè)元素結(jié)點(diǎn)
return 0;
}
程序最終運(yùn)行結(jié)果如圖,可以看到,頭插法建立的單鏈表數(shù)據(jù)順序與輸入順序相反:
