(一)鏈表的定義和介紹在這里就不多介紹了,我們現在看文章大概最煩的就是一上來就長篇大論,這樣直接勸退了很多人,所以我們選擇從簡。
頭節點: 是單鏈表的頭,是一個特殊的節點,只有指針域,沒有數據域。
節點:由兩部分構成,第一部分是數據域,存儲的是該節點的內容,第二部分是指針域,用來存儲下一節點的地址,通過改地址可以訪問下一個節點。
單鏈表:由頭節點和若干節點組成的鏈表。
這是一個有三個節點的鏈表。
(二)鏈表的存儲結構
(1)從上篇文章我們得知順序表在計算機中存儲的位置是連續的,就像宿舍樓一層的房間,都是相鄰的,但鏈表不一樣,鏈表不一定是相鄰的,只不過每一個節點都會存儲下一個節點的地址。比如說,小明有小紅的地址,小紅呢有小白的地址,小白又有小蘭的地址,小黑有小明的地址,他們五個是一個班的,所以他們之間就形成了一個關系:
這就是一個單鏈表的結構,單是指只有前驅節點有后繼節點的地址,只有單向性,當然也有雙向鏈表,后面遇到了我們再講。
(2)上面是我們自己抽象化的一個鏈表,在計算機中的存儲結構是下圖:
(三)代碼分析
(1)頭文件
#include <iostream> //基本的輸入輸出 using namespace std; //聲明命名空間 #include<string.h> //字符串頭文件
(2)預處理
#define OK 1 #define ERROR 0 #define ElemType int
(3)創建節點,很明顯,這里要用到結構體,結構體有兩個成員,一個是數據域,一個是指針域。
typedef struct LNode { struct LNode *next; //節點指針域 string data; //節點數據域 }*LinkList,LNode; //一個是結構體指針,一個是結構體名稱
(4)初始化函數,創建一個頭節點,並將指針域置空。
ElemType Init_Linklist(LinkList &L) //初始化鏈表 { L = new LNode; //創建一個頭節點 L->next = NULL; //並將頭節點的指針域置空 return OK; }
(5)輸出鏈表函數,這個函數呢,是將單鏈表中的每個元素按順序輸出,為了美觀,用"->"隔開。
實現步驟:①定義一個節點指針,指向第一個節點。(注意:這里的第一個節點是頭節點之后的第一個。)
②while判斷當前節點是否為空,如果不是空,就輸出該節點內容,然后指針下移,下面的if是最后一個節點不輸出"->",這樣鏈表看起來會更美觀。
void Printf_LNode(LinkList L) //遍歷輸出鏈表內容 { cout<<"鏈表內容:"; LNode *p = L->next; //節點指針,用來遍歷節點 while(p) { cout<<p->data; //輸出節點的數據域 p = p->next; if(p) //判斷下一個節點是否為空,如果是就不輸出"->" { cout<<"->"; } } cout<<endl; }
(6)前插法建立鏈表,即每次在第一個節點的位置插入新節點。咱們先自己想一下這個過程,首先,我們要新建一個節點吧,然后將新節點的指針域指向下一個節點,再把上一個節點的指針域指向自己,不就插入成功了嘛。看一下圖片:
實現步驟:①輸出提醒,並得到你要創建節點的個數n。
②創建一個節點指針,便於后面改指針的操作。
③初始化頭節點,並將指針域置空。
④要創建n個節點,自然需要循環n次,首先開辟一個新節點,並讓p指向新節點,將新節點的指針指向下一個節點,再讓上一個節點指向自己,然后在輸入該節點的內容。
⑤輸出鏈表。
void Create_LinkList_h(LinkList &L) //前插法創建單鏈表 { int n; cout<<"請輸入你要創建的鏈表的結點個數:"; cin>>n; LNode *p; L = new LNode; //初始化頭節點 L->next = NULL; cout<<"將要采用頭插法創建單鏈表,請逆序輸入"<<n<<"個節點內容:"; for(int i = 0;i < n;i++) //循環創建節點 { p = new LNode; p->next = L->next; L->next = p; cin>>p->data; } Printf_LNode(L); //輸出鏈表內容 }
(7)后插法建立鏈表,尾插法是將每個元素插在最后,故稱為后插法。與前插法大致相同,不過比前插法多一個節點指針,首先新建一個節點,然后自己的指針域為空,因為自己已經是最后一個節點了,然后讓上一個節點指向自己,然后插入成功。
實現步驟:①首先得到你要輸入的節點個數n
②初始化頭節點
③定義一個節點指針r,並指向L
④循環n次,每一次都先申請一個節點,輸入節點內容,因為是后插,所以每一個節點的指針域都指向空,然后再讓上一個節點指向自己,然后r后移一個節點。
void Create_LinkList_r(LinkList &L) //尾插法創建鏈表 { int n; //節點個數 cout<<"請輸入你要創建的鏈表的結點個數:"; cin>>n; L = new LNode; //初始化頭節點 L->next = NULL; LNode *r = L; cout<<"將要采用尾插法創建單鏈表,請輸入"<<n<<"個節點內容:"; for(int i = 0;i < n;i++) //循環創建節點 { LNode *p; p = new LNode; cin>>p->data; p->next = NULL; r->next = p; r = p; } Printf_LNode(L); }
(8)取值,通過輸入的位置, 遍歷鏈表,找到對應位置的值,然后賦值即可。
實現步驟:①首先輸入你要取得元素的位置
②定義一個新的節點指針,並指向頭節點的下一個節點,同時呢也定義一個j,用來記錄節點的值。
③用while循環,讓指針連續下移到相應位置,同時判斷j的值是否達到n。
④如果已經移到空節點了,或者說j已經大於n了,就表明越界了。
⑤如果沒有越界,就賦值即可。
ElemType Get_ElemType(LinkList L,string &name) { int n; //元素位置 cout<<"請輸入你要取得元素的位置:"; cin>>n; LNode *p = L->next; //新建節點指針,並指向下一個節點 int j = 1; while(p&&j<n) //指針移到要取得位置 { p = p->next; j++; } if(!p || j>n) //判斷是否越界 { return ERROR; } name = p->data; //取值 }
(9)插入節點,其實也就是先開辟一個節點,然后讓新節點指向下一個節點,再讓新節點的上一個節點指向自己,就插入成功了。
實現步驟:①首先得到你要插入的位置
②定義一個節點指針,指向第一個節點
③指針連續下移,移到n-1的節點。
④判斷是否越界
⑤新建一個節點和一個指向新節點的指針s,然后輸入插入的節點的值。
⑥改指針,將上一個節點的指針賦給新節點,然后讓上一個節點指向自己,就插入成功了。
ElemType Insert_LNode(LinkList &L) //插入節點函數 { int n; //插入得位置 cout<<"請輸入你要插入的位置:"; cin>>n; LNode *p = L->next; int j = 1; while(p&&j<(n - 1)) //新建節點指針,並指向下一個節點 { p = p->next; j++; } if(!p || j > (n - 1)) //判斷是否越界 { return ERROR; } LNode *s; //指向新建節點的指針 s = new LNode; cout<<"請輸入你要插入的元素:"; cin>>s->data; //輸入新節點的數據域內容 s->next = p->next; p->next = s; Printf_LNode(L); //輸出單鏈表 }
(10)刪元素,其實跟之前插元素的實現差不多,都是先移到你要刪除的位置,然后把你想要刪除節點的指針域賦給前一個節點,然后釋放節點即可。
實現步驟:①輸出一邊當前鏈表的內容,然后挑選並得到你要刪除的位置,同時定義一個節點指針q。
②定義一個節點指針指向第一個節點,並定義一個計數變量j。
③指針p連續下移到要刪除的元素的前一個位置,然后在把該節點的指針域(q = p->next),賦給節點指針q,這樣q就指向了我們要刪除的節點,然后讓q指向我們要刪除的節點的下一個節 點,然后釋放q節點即可。
ElemType Del_LNode(LinkList &L) //刪除節點函數 { int n; LNode *q; //首先定義一個節點指針 Printf_LNode(L); cout<<"請輸入你要刪除的位置:"; cin>>n; LNode *p = L->next; int j = 1; while(p&&(j< n - 1)) //指針下移 { p = p->next; ++j; } if(!p || j > (n - 1)) //判斷是否越界 { return ERROR; } q = p->next; p->next = q->next; delete q; Printf_LNode(L); }
(11)菜單函數,只是一些普通的輸出,就不用解釋了吧
void Menu() //菜單函數 { cout<<"<<<<<<<<<<<<<<<菜單>>>>>>>>>>>>>>>"<<endl; cout<<"1.初始化單鏈表"<<endl; cout<<"2.頭插法創建一個單鏈表"<<endl; cout<<"3.尾插法創建一個單鏈表"<<endl; cout<<"4.取單鏈表中的某個元素"<<endl; cout<<"5.在單鏈表某個位置插入元素"<<endl; cout<<"6.在單鏈表刪除某個位置的元素"<<endl; cout<<"7.輸出鏈表內容"<<endl; cout<<"8.菜單"<<endl; cout<<"9.退出系統!"<<endl; cout<<"輸入對應選項,執行對應操作"<<endl; }
(12)主函數:首先創建一個鏈表指針,輸出菜單,然后進入while循環,通過switch輸入不同的選項進入不同的case,然后執行不同的函數,這里為了能夠退出系統(跳出循環)使用了goto語句來結束循環。這里需要特別說明的可能就是case 4和case 5,定義了兩個標志變量,用來記錄程序退出的值,看是否出錯
int main() { LinkList La; //鏈表指針 int select; Menu(); while(1) //循環輸入選項 { cout<<"請輸入選項:"; cin>>select; switch(select) //判斷選項,並執行對應的函數 { case 1: { Init_Linklist(La); cout<<"初始化后單鏈表為空,要想進行操作,請先創建一個單鏈表"<<endl; Create_LinkList_h(La); break; } case 2: { Create_LinkList_h(La); break; } case 3: { Create_LinkList_r(La); break; } case 4: { int flag; //標志變量,用來記錄退出碼,用來判斷是否為正常退出 string name; flag = Get_ElemType(La,name); if(!flag) { cout<<"取出失敗,越界!"<<endl; } cout<<name<<endl; break; } case 5: { int flag; flag = Insert_LNode(La); //標志變量,用來記錄退出碼,用來判斷是否為正常退出 if(!flag) { cout<<"插入失敗,越界!"<<endl; } break; } case 6: { Del_LNode(La); break; } case 7: { Printf_LNode(La); break; } case 8: { Menu(); break; } case 9: { cout<<"多謝使用"<<endl; goto unloop; //使用goto語句,跳轉,使程序結束 } default: { cout<<"輸入有誤!"<<endl; break; } } } unloop:return 0; //跳轉到程序結束語句 }
(四)完整代碼
#include <iostream> //基本的輸入輸出 using namespace std; //聲明命名空間 #include<string.h> //字符串頭文件 #define OK 1 #define ERROR 0 #define ElemType int typedef struct LNode { struct LNode *next; //節點指針域 string data; //節點數據域 }*LinkList,LNode; //一個是結構體指針,一個是結構體名稱 ElemType Init_Linklist(LinkList &L) //初始化鏈表 { L = new LNode; //創建一個頭節點 L->next = NULL; //並將頭節點的指針域置空 return OK; } void Printf_LNode(LinkList L) //遍歷輸出鏈表內容 { cout<<"鏈表內容:"; LNode *p = L->next; //節點指針,用來遍歷節點 while(p) { cout<<p->data; //輸出節點的數據域 p = p->next; if(p) //判斷下一個節點是否為空,如果是就不輸出"->" { cout<<"->"; } } cout<<endl; } void Create_LinkList_h(LinkList &L) //前插法創建單鏈表 { int n; cout<<"請輸入你要創建的鏈表的結點個數:"; cin>>n; LNode *p; L = new LNode; //初始化頭節點 L->next = NULL; cout<<"將要采用頭插法創建單鏈表,請逆序輸入"<<n<<"個節點內容:"; for(int i = 0;i < n;i++) //循環創建節點 { p = new LNode; p->next = L->next; L->next = p; cin>>p->data; } Printf_LNode(L); //輸出鏈表內容 } void Create_LinkList_r(LinkList &L) //尾插法創建鏈表 { int n; //節點個數 cout<<"請輸入你要創建的鏈表的結點個數:"; cin>>n; L = new LNode; //初始化頭節點 L->next = NULL; LNode *r = L; cout<<"將要采用尾插法創建單鏈表,請輸入"<<n<<"個節點內容:"; for(int i = 0;i < n;i++) //循環創建節點 { LNode *p; p = new LNode; cin>>p->data; p->next = NULL; r->next = p; r = p; } Printf_LNode(L); } ElemType Get_ElemType(LinkList L,string &name) { int n; //元素位置 cout<<"請輸入你要取得元素的位置:"; cin>>n; LNode *p = L->next; //新建節點指針,並指向下一個節點 int j = 1; while(p&&j<n) //指針移到要取得位置 { p = p->next; j++; } if(!p || j>n) //判斷是否越界 { return ERROR; } name = p->data; //取值 } ElemType Insert_LNode(LinkList &L) //插入節點函數 { int n; //插入得位置 cout<<"請輸入你要插入的位置:"; cin>>n; LNode *p = L->next; int j = 1; while(p&&j<(n - 1)) //新建節點指針,並指向下一個節點 { p = p->next; j++; } if(!p || j > (n - 1)) //判斷是否越界 { return ERROR; } LNode *s; //指向新建節點的指針 s = new LNode; cout<<"請輸入你要插入的元素:"; cin>>s->data; //輸入新節點的數據域內容 s->next = p->next; p->next = s; Printf_LNode(L); //輸出單鏈表 } ElemType Del_LNode(LinkList &L) //刪除節點函數 { int n; LNode *q; //首先定義一個節點指針 Printf_LNode(L); cout<<"請輸入你要刪除的位置:"; cin>>n; LNode *p = L->next; int j = 1; while(p&&(j< n - 1)) //指針下移 { p = p->next; ++j; } if(!p || j > (n - 1)) //判斷是否越界 { return ERROR; } q = p->next; p->next = q->next; delete q; Printf_LNode(L); } void Menu() //菜單函數 { cout<<"<<<<<<<<<<<<<<<菜單>>>>>>>>>>>>>>>"<<endl; cout<<"1.初始化單鏈表"<<endl; cout<<"2.頭插法創建一個單鏈表"<<endl; cout<<"3.尾插法創建一個單鏈表"<<endl; cout<<"4.取單鏈表中的某個元素"<<endl; cout<<"5.在單鏈表某個位置插入元素"<<endl; cout<<"6.在單鏈表刪除某個位置的元素"<<endl; cout<<"7.輸出鏈表內容"<<endl; cout<<"8.菜單"<<endl; cout<<"9.退出系統!"<<endl; cout<<"輸入對應選項,執行對應操作"<<endl; } int main() { LinkList La; //鏈表指針 int select; Menu(); while(1) //循環輸入選項 { cout<<"請輸入選項:"; cin>>select; switch(select) //判斷選項,並執行對應的函數 { case 1: { Init_Linklist(La); cout<<"初始化后單鏈表為空,要想進行操作,請先創建一個單鏈表"<<endl; Create_LinkList_h(La); break; } case 2: { Create_LinkList_h(La); break; } case 3: { Create_LinkList_r(La); break; } case 4: { int flag; //標志變量,用來記錄退出碼,用來判斷是否為正常退出 string name; flag = Get_ElemType(La,name); if(!flag) { cout<<"取出失敗,越界!"<<endl; } cout<<name<<endl; break; } case 5: { int flag; flag = Insert_LNode(La); //標志變量,用來記錄退出碼,用來判斷是否為正常退出 if(!flag) { cout<<"插入失敗,越界!"<<endl; } break; } case 6: { Del_LNode(La); break; } case 7: { Printf_LNode(La); break; } case 8: { Menu(); break; } case 9: { cout<<"多謝使用"<<endl; goto unloop; //使用goto語句,跳轉,使程序結束 } default: { cout<<"輸入有誤!"<<endl; break; } } } unloop:return 0; //跳轉到程序結束語句 }
(五)運行結果
程序在健壯性方面還有很多不足,各位大神如果有什么好的想法或者發現我的某些錯誤,歡迎指正!最后希望大家給我一個支持,謝謝!