單鏈表
單鏈表的定義就不說了, 很簡單, 請自行百度; 那麼從今天的主題<單鏈表的應用>入手; 利用單鏈表實現電話本的模擬程序:定義單鏈表的數據類型,將頭插法和尾插法、插入、逆置、刪除、查找、修改、計數、輸出等操作都定義成子函數的形式,最後在主函數中調用它( 兄弟們調用就不弄了, 自行測試, 增加動手能力 )
整個程序最主要的步驟還是插入, 查找, 刪除操作, 其他操作都可以圍繞這三個操作來進行, 其實單鏈表無論是什麼操作都可以總結爲指針的操作, 無非是指針的指向, 通過指針的指向就能完成很多步驟, 但是在單鏈表中由於指針是單向的, 不像雙鏈表那樣可以雙向操作, 所以操作起來有點不太方便(其實還是很簡單的, 只是說了嚇唬人的)
此處做註釋: 鏈表的head節點數據域不存數據, head所在的位置是-1, 鏈表中元素是從0號開始
那麼我就先從insert開始:
insert操作
void insert(int index,LNode *L1,LNode *L2);
insert就是在特定的位置(index)把L2節點連接到L1上面, 所以就有如下實現方式:
void insert(int index,LNode *L1,LNode *L2){
for(int i=0;(i<index)&&(L1!=NULL)&&(L2!=NULL);i++,L1=L1->next);
L2->next=L1->next;L1->next=L2;
}
L1與L2需要判斷是否爲空(避免引用非法指針), 上面的for操作主要是將指針L1移動到需要插入的位置的前一個位置, 首先將L2的next指針存入原本L1的後一個位置的地址, 然後將L1的next指針存入L2的地址, 這兩步操作不能顛倒, 顛倒, 原本鏈表中L1後面部分的鏈表結構將會丟失; (請細細理解)
在創建鏈表的過程中我們可以選擇頭插法或者尾插法進行創建鏈表;
所謂頭插法簡單的來說就是每次插入操作總是把插入節點的位置定位0號, 頭插法的意義就在於我們可以很容易完成逆置的操作, 頭插法代碼如下:
void insertHead(LNode *L1,LNode *L2){
insert(0,L1,L2);
}
頭插法就是在0號位置把L2插入到L1的後面, 如果將頭插法應用到創建鏈表, 那麼此處的L1便是head
尾插法就是每次在尾部插入節點, 插入的位置總是末尾; 代碼如下:
void insertTail(LNode *L1,LNode *L2,int t){
insert(t,L1,L2);
}
此處L1, L2的含義與頭插法中的含義一致, t表示末尾的位置, 末尾需要傳入參數來確定, 待會在main函數中測試再進行說明, 兩個插入的區別就在於位置的變化, 由兩種插入方面所形成的鏈表順序是相反的
逆置操作
上面提到逆置操作需要用到頭插法, 因爲每次節點傳入的insertHead中, 我總是把節點插入到0號位置, 也就是head後面, 一個正序的鏈表, 我把它的節點按順序不斷拆下來, 同時將每個拆下來的節點連接到head的0號位上面, 而不是又按照原有的順序進行順序連接(表面上看好像連和沒連一樣, 這裏你需要畫圖好好思考), 舉個例子: head 0 1 2 3的順序表, 0拆下連接到head上面, 1拆下連接到head上面, 此時0就連接到1號上面, 順序爲head 1 0, 同理, 2拆下連接到head上面, 1又連接到2上面, 順序爲head 2 1 0 ,最後整個過程重複, 得到的順序就是head 3 2 1 0, 然後就可以做到把一個正序的鏈表變爲逆序的
話不多說, 上代碼:
void convert(LNode *L){//此處逆置是對整個鏈表逆置, L傳入的是head
LNode* p=L->next;
L->next=NULL;
LNode* q;
int t_size=size;
while(t_size>0){
q=p->next;
p->next=NULL;
insert(0,L,p);
p=q;t_size--;
}
}
LNode* p=L->next;L->next=NULL;LNode* q;是將head分離, while內部的操作主要是通過不斷迭代的方式, 做到將第一個節點與第二個節點進行分離, 以達到將在0號位插入的效果
刪除, 查找, 修改
這三步操作是圍繞查找來, 由於單鏈表結構的特點, 所以只能做到順序查找(可採用平衡二叉搜索樹, 實現鏈表的O(logn)的複雜度)
上代碼:
LNode *find(LNode *L,char *e){
while(L!=NULL){//避免引用非法指針
if(strcmp(L->data.name,e)==0)
return L;
L=L->next;
}
return head;
}
傳入head節點, 以及所要查找的元素e(使用指針, 其效果與char e[]一致), 不斷比較, 成功則返回所指向的節點, 失敗則返回head
刪除: 刪除我做了修改, 讓它具有兩個功能–刪除特定元素, 刪除整個鏈表(其實還是爲了偷懶, 少寫代碼)
void remove(LNode *L,LNode *LTarget_value){
while(L->next!=NULL){
if((L->next==LTarget_value)&&(LTarget_value!=NULL)){//用於刪除特定元素
LNode* p=L->next;L->next=p->next;//將所要刪除的節點進行分離操作
size--;//刪除一個人就需要總人數減一次
delete p;return;//若刪除p立馬退出
}else if(LTarget_value==NULL){//用於刪除整個鏈表
LNode* p=L->next;L->next=p->next;
delete p;//分離一個節點進行一次刪除,直到刪除到只剩下head節點才停止
}
if(LTarget_value!=NULL) L=L->next;//只有在刪除特定元素的時候才進行L的移動
}
if(LTarget_value==NULL) delete L;//只有在刪除整個鏈表才刪除head
}
修改操作: 修改可針對特定元素, 此處只做簡單說明
上代碼:
void revise(LNode *L,char *e,char *Ae,int i){
LNode *p=find(L,e);
if(p->data.name==NULL && p->data.num=NULL )
return;//表示沒有找到要修改的元素,p現在是head,head裏面沒有存數據, 直接跳出程序
if(i==1) strcpy(p->data.name,Ae);
else if(i==2) strcpy(p->data.num,Ae);
}
L表示head, e表示要查找的目標元素, Ae表示替換e, i表示選擇替換name, 還是替換num
輸出
輸出就很簡單了, 不做敘述
void display(LNode *L){
while(L!=NULL){
printf("%s %s\n",L->data.name,L->data.num);
L=L->next;
}
}
此次測試所用的結構體
struct DataType{
char name[10];
char num[12];
};
struct LNode{
LNode *next;
DataType data;
};
兩個全局變量
LNode *head=new LNode();//head節點
int size=0;//用作計數, 計算存入多少聯繫人
main函數 的測試代碼
int main(){
int t_size;
int sum=0,i;
char na[10],nu[12];
printf("輸入需要創建的電話本大小以及1.頭插OR2.尾插\n");
scanf("%d%d",&t_size,&i);
while(sum<t_size){
printf("輸入名字,號碼\n");fflush(stdin);
scanf("%s%s",na,nu);
LNode *s=new LNode();s->next=NULL;
strcpy(s->data.name,na);
strcpy(s->data.num,nu);
size++; //每輸入一次,size進行一次增加, 只有在輸入纔有人數的增加
if(i==1){//輸入時選擇頭插法,還是尾插法
insertHead(head,s);
}else if(i==2){
insertTail(head,s,sum);
}++sum;
}
display(head);
// printf("輸入要查找的元素\n");fflush(stdin);
// scanf("%s",na);
// printf("%s\n",find(head,na)->data.name);
// printf("刪除鏈表中元素\n");fflush(stdin);
// scanf("%s",na);
// if(i==1){
// puts("輸入要刪除的元素");scanf("%s",na);
// remove(head,find(head,na));
// }
// display(head);
// puts("若號碼以及名字都要修改那麼你可選擇直接刪除");
// puts("需要修改的元素,修改後的值,1.修改名字,2.修改號碼");
// scanf("%s%s%d",na,nu,&i);
// revise(head,na,nu,i);
puts("輸入在幾號位置進行插入操作,插入元素爲,0號是首元素");
scanf("%d",&i);
printf("輸入名字,號碼\n");fflush(stdin);
scanf("%s%s",na,nu);
LNode *s=new LNode();s->next=NULL;
strcpy(s->data.name,na);
strcpy(s->data.num,nu);
insert(i,head,s);
// convert(head);
display(head);
remove(head,NULL);
return 0;
}
代碼進行拼湊就可使用, 可能存在異常, 但是主要還是闡述思想, 上面註釋部分爲測試每個功能的代碼, 最後還需做修改, 寫的不好, 還有很多地方需要改進, 就不做修改了, 界面做的很簡陋, 整個程序最核心的還是insert, find, remove, 理解頭插法與尾插法的區別, 有問題還請在下方留言