C語言鏈表案例學(xué)習(xí)之通訊錄的實(shí)現(xiàn)
一、通訊錄需要實(shí)現(xiàn)的功能
1,通訊錄可以存儲(chǔ)編號,聯(lián)系人的姓名,電話號碼和家庭住址。
2,通訊錄最基本的功能是添加聯(lián)系人,用戶可以隨時(shí)添加聯(lián)系人。
3,通訊錄可以展示已經(jīng)添加的所有聯(lián)系人。
4,通訊錄中用戶可以根據(jù)聯(lián)系人的姓名刪除對應(yīng)通訊錄中的信息。
5,通訊錄中姓名可以重復(fù),所以為了刪除準(zhǔn)確的信息,需要實(shí)現(xiàn)按位置刪除的功能。
6,通訊錄中用戶可以根據(jù)聯(lián)系人的姓名找到聯(lián)系人在通訊錄中的信息,因?yàn)槁?lián)系人可以重名,所以如果有重名的聯(lián)系人的時(shí)候就需要返回兩個(gè)或兩個(gè)以上的聯(lián)系人信息到一個(gè)查找表單中。
7,通訊錄中用戶可以根據(jù)通訊錄的位置修改該位置的聯(lián)系人信息,而修改這樣的功能就只需要按照位置來進(jìn)行修改,不需要再按照姓名進(jìn)行修改,因?yàn)榇嬖谥孛那闆r,并且可以根據(jù)返回的表單看到具體的聯(lián)系人位置,所以就不需要再 設(shè)計(jì)像按照名字進(jìn)行修改這樣冗余的功能了。
8,通訊錄可以按照位置插入聯(lián)系人
二、項(xiàng)目目的
制作本項(xiàng)目是為了將所學(xué)到的鏈表的知識進(jìn)行鞏固學(xué)習(xí),做到學(xué)以致用,并通過做這樣的小項(xiàng)目來增強(qiáng)理解開發(fā),算法和C語言的指針,結(jié)構(gòu)體等知識,同時(shí)收獲開發(fā)經(jīng)驗(yàn),項(xiàng)目重點(diǎn)是能夠使用鏈表的知識做出的小項(xiàng)目,所以該項(xiàng)目不會(huì)考慮到實(shí)現(xiàn)數(shù)據(jù)持久化的操作和GUI編程,只是基于DOS的命令行程序。
三、項(xiàng)目開發(fā)
開發(fā)IDE:Visual Studio 2019
IDE注意:
1,在該IDE中不能夠直接使用scanf()函數(shù),因?yàn)樗赡軙?huì)存在一些不安全的因素,所以在該IDE中使用的是scanf_s()函數(shù),但是scanf和scanf_s函數(shù)具有本質(zhì)的區(qū)別,并且scanf_s函數(shù)只能在該IDE中使用,不廣泛,所以還是推薦使用scanf()函數(shù),為了能正常使用scanf()函數(shù),需要將聲明#define _CRT_SECURE_NO_WARNINGS放在項(xiàng)目的最頂部。
2,為了更方便的開發(fā)該項(xiàng)目,所以使用了一些c++的庫函數(shù),所以為了能夠正確運(yùn)行程序,建議將后綴改為.cpp,其實(shí)c++是c的升級版本,解決了c不能面向?qū)ο箝_發(fā)的模式,它的編譯器既可以運(yùn)行.c的程序也可以運(yùn)行.cpp的程序,但是.c的編譯器是不能夠運(yùn)行.cpp的程序的,它并不能識別一些.cpp的源碼。
首先,根據(jù)需求,選擇合適的數(shù)據(jù)結(jié)構(gòu),這里選擇的數(shù)據(jù)結(jié)構(gòu)是鏈表為主體,采用帶有頭結(jié)點(diǎn)的單鏈表的形式,通過傳入指向頭結(jié)點(diǎn)的指針進(jìn)行添加結(jié)點(diǎn),遍歷結(jié)點(diǎn),刪除結(jié)點(diǎn),插入結(jié)點(diǎn)等對結(jié)點(diǎn)的操作,這樣每次對鏈表進(jìn)行操作就只需要傳入指向頭結(jié)點(diǎn)的指針就可以了,可以這樣理解:
當(dāng)程序運(yùn)行在內(nèi)存中的時(shí)候,首先先使用一個(gè)指針指向一個(gè)頭結(jié)點(diǎn)
將該指針傳入到添加結(jié)點(diǎn)的函數(shù)中,在該函數(shù)中通過指針從頭結(jié)點(diǎn)開始遍歷,使用頭插法或尾插法,將生成的結(jié)點(diǎn)插入到頭結(jié)點(diǎn)之后
然后再次傳入指向頭結(jié)點(diǎn)的指針到添加結(jié)點(diǎn)的函數(shù),此時(shí)該鏈表已經(jīng)有兩個(gè)結(jié)點(diǎn),頭結(jié)點(diǎn)和一個(gè)結(jié)點(diǎn),內(nèi)部函數(shù)使用指針進(jìn)行遍歷,然后添加結(jié)點(diǎn),形成頭結(jié)點(diǎn)為起點(diǎn)的后面帶有兩個(gè)結(jié)點(diǎn)的單向鏈表
依次如此....
理解這里最重要的是對于指針的理解
然后我們根據(jù)需要在通訊錄中的內(nèi)容可以存儲(chǔ)編號,聯(lián)系人的姓名,電話號碼和家庭住址定義一個(gè)結(jié)構(gòu)體,如下:
typedef struct Node { Number num;//編號 Name name[23];//聯(lián)系人姓名 Phone phone[33];//聯(lián)系人電話 Addr addr[50];//聯(lián)系人地址 struct Node* next;//next指針 }LNode;
在這里需要考慮到的問題是對于編號的實(shí)現(xiàn),編號可以在程序中由程序自主的實(shí)現(xiàn),也就是在程序中可以定義一個(gè)全局變量number,并且賦初值為0,當(dāng)調(diào)用添加結(jié)點(diǎn)的函數(shù)時(shí),number自增并將number的值的賦給結(jié)構(gòu)體成員num,但是它只能夠不斷的自增,當(dāng)調(diào)用刪除功能時(shí),它的序號就會(huì)變得混亂,比如,現(xiàn)在已經(jīng)添加了5個(gè)聯(lián)系人,編號分別為1,2,3,4,5,如果刪除編號為3的聯(lián)系人的話,那么此時(shí)通訊錄中的編號只剩下1,2,4,5,這樣就會(huì)造成編號無序的情況,就沒有意義也不便于管理操作。
如果編號在添加結(jié)點(diǎn)的時(shí)候由用戶輸入實(shí)現(xiàn)的話,對于用戶來說可能會(huì)增加操作的負(fù)擔(dān),同時(shí)也不便于管理,可能它會(huì)無序甚至是重復(fù)。
所以最后可以解決的方式就是將編號不作為結(jié)構(gòu)體成員的變量而是作為功能函數(shù)體中的一部分,也就是說我們傳入頭結(jié)點(diǎn)之后添加結(jié)點(diǎn)形成的單鏈表,如果要將信息輸出到屏幕上時(shí),可以定義一個(gè)指針指向單鏈表的首元結(jié)點(diǎn)并定義一個(gè)num為1的計(jì)數(shù)器,指針遍歷到的個(gè)數(shù)就是計(jì)數(shù)器上的數(shù)字,這樣如果刪除了某個(gè)結(jié)點(diǎn),它都會(huì)重新遍歷一次,這樣編號就是有序的,也就是說在打印聯(lián)系人名單的函數(shù)中每次傳入頭結(jié)點(diǎn)都需要重新遍歷一下,編號由函數(shù)中的num給出,所以此時(shí)重新修改結(jié)構(gòu)體為:
typedef struct Node { Name name[23];//聯(lián)系人姓名 Phone phone[33];//聯(lián)系人電話 Addr addr[50];//聯(lián)系人地址 struct Node* next;//next指針 }LNode;
然后定義函數(shù)printNode(Node* head)用來打印通訊錄的名單,這里可以更好的理解如何解決編號的問題
void printNode(Node* head) { Node* move; move = head->next; int num = 1; printf("================================通訊錄頁面=============================\n"); while (move) { printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); printf("編號:%d 姓名:%s 電話:%s 住址:%s\n", num, move->name, move->phone, move->addr); printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); move = move->next; num++; } printf("================================通訊錄頁面=============================\n"); }
接下來,我們來定義完整的鏈表實(shí)現(xiàn):
首先,可以定義一個(gè)函數(shù)Node* initList()用來初始化鏈表,也就是創(chuàng)建一個(gè)頭結(jié)點(diǎn)并讓它的指針域指向NULL,定義一個(gè)指針函數(shù),該指針函數(shù)返回一個(gè)指向頭結(jié)點(diǎn)的指針。
Node* initList() { Node* head; head = (Node*)malloc(sizeof(Node)); head->next = NULL; return head; }
然后,定義一個(gè)函數(shù)用來添加聯(lián)系人也就是創(chuàng)建結(jié)點(diǎn)并使用頭插法添加到單向鏈表中,此時(shí)我們需要傳入指向頭結(jié)點(diǎn)的指針。
void addListNode(Node* head) { Node* node; node = (Node*)malloc(sizeof(Node)); printf("請輸入聯(lián)系人信息:\n"); printf("姓名:"); scanf("%s", &node->name); printf("電話號碼:"); scanf("%s", &node->phone); printf("家庭地址:"); scanf("%s", &node->addr); //使用頭插法將結(jié)點(diǎn)鏈接到頭結(jié)點(diǎn)之后 node->next = head->next; head->next = node; if (head->next != NULL) { printf("添加聯(lián)系人成功\n"); } else { printf("添加聯(lián)系人失敗\n"); } }
此時(shí),可以在主方法中進(jìn)行測試,此時(shí)已經(jīng)能夠打印出三個(gè)聯(lián)系人了
int main() { Node* head; head=initList(); addListNode(head); addListNode(head); printNode(head); return 0; }
我們已經(jīng)實(shí)現(xiàn)了通訊錄的存儲(chǔ)要求,添加聯(lián)系人和打印出通訊錄的聯(lián)系人信息了。
此時(shí)我們會(huì)考慮到如果該通訊錄為空的話,我們直接進(jìn)行打印可能會(huì)造成nullptr,所以需要實(shí)現(xiàn)一個(gè)判斷表空的函數(shù),該函數(shù)返回bool值便于調(diào)用判斷布爾類型。
bool isempty(Node* head) { if (head->next == NULL) { return false; } else { return true; } }
此時(shí)在打印聯(lián)系人目錄的函數(shù)中使用該函數(shù)在函數(shù)的最開始進(jìn)行判斷,并在main中進(jìn)行測試
if (!isempty(head)) { printf("檢測到通訊錄為空,請先添加聯(lián)系人再進(jìn)行操作\n"); return; }
接下來需要實(shí)現(xiàn)的是通訊錄的其他功能。
首先實(shí)現(xiàn)用戶可以根據(jù)聯(lián)系人的姓名刪除對應(yīng)通訊錄中的信息,根據(jù)聯(lián)系人的姓名刪除對應(yīng)通訊錄中的信息,我們可以想到,聯(lián)系人的姓名是可以重復(fù)的,所以在刪除時(shí)需要判斷是否有重名的情況,所以可以先實(shí)現(xiàn)第6個(gè)需求,也就是通訊錄中用戶可以根據(jù)聯(lián)系人的姓名找到聯(lián)系人在通訊錄中的信息,因?yàn)槁?lián)系人可以重名,所以如果有重名的聯(lián)系人的時(shí)候就需要返回兩個(gè)或兩個(gè)以上的聯(lián)系人信息到一個(gè)查找表單中。
首先我們知道該功能是通過用戶輸入聯(lián)系人的姓名也就是字符串然后在鏈表中找到相應(yīng)字符串的位置進(jìn)行返回,那么我們就需要實(shí)現(xiàn)一個(gè)字符串匹配的函數(shù),它應(yīng)當(dāng)返回一個(gè)bool類型的值,當(dāng)指針在鏈表中不斷遍歷并取出聯(lián)系人的姓名進(jìn)行比較直到找到這個(gè)聯(lián)系人為止也就是該函數(shù)返回false的時(shí)候循環(huán)結(jié)束,所以可以定義一個(gè)字符匹配函數(shù)bool isBatch(Name n1[], Name n2[]);它的具體實(shí)現(xiàn)如下:
bool isBatch(Name n1[], Name n2[]) { Name* n11, * n22;//定義兩個(gè)char類型的指針 n11 = n1;//讓n11指向字符串n1的首地址 n22 = n2;//讓n22指向字符串n2的首地址 int num1 = 1;//定義長度器用來計(jì)量n1的長度 int num2 = 1;//定義長度器用來計(jì)量n2的長度 //定義兩個(gè)長度器的原因是如果是n1:NUM,n2:NUM2的話,沒有計(jì)數(shù)器的情況下當(dāng)跳出循環(huán)后它依然是返回true的 while (1) { if (*n11 == *n22) { n11++; n22++; num1++; num2++; if (*n11 == '\0' && *n22 == '\0') { break; } } else { return false; } } if (num1 != num2) { return false; } return true; }
然后我們需要返回聯(lián)系人在鏈表中的位置通過再打印查找單的函數(shù)中遍歷打印出信息,聯(lián)系人可能重名,所以位置可能會(huì)返回多個(gè),一個(gè)或者是無聯(lián)系人的情況,因此這個(gè)返回位置的函數(shù)可以想到使用返回一個(gè)int數(shù)組的方式來操作,在C語言中如果想要返回?cái)?shù)組的話需要返回的是指向這個(gè)數(shù)組的指針,也就是說C語言不能夠直接就返回一個(gè)數(shù)組類型的數(shù)據(jù),所以可以定義一個(gè)函數(shù)int* lookupByname(Node* head, Name name[]);傳入頭結(jié)點(diǎn)和輸入的名字,它的具體實(shí)現(xiàn)如下:
int* lookupByname(Node* head, Name name[]) { Node* target;//定義一個(gè)目標(biāo)指針,該目標(biāo)指針是用來獲取每一個(gè)結(jié)點(diǎn)中的name并與輸入的name進(jìn)行比較 target = head->next;//讓其指向第一個(gè)結(jié)點(diǎn) int* summary = NULL;//定義一個(gè)指向返回?cái)?shù)組的指針 summary = (int*)calloc(NUM, sizeof(int));//該指針指向一片大小為NUM的int類型的數(shù)組,該數(shù)組中所有值為0 int loc = 1;//獲取位置從1開始,放在所申請的數(shù)組中 int numd = 0;//數(shù)組的下標(biāo) while (target)//遍歷完整個(gè)鏈表 { if (isBatch(target->name, name))//如果匹配 { summary[numd] = loc;//位置放入數(shù)組 numd++;//只要放入numd就會(huì)加1,所以只要有一個(gè)聯(lián)系人numd是1不是0 } target = target->next; loc++; } if (numd == 0) {//如果最后遍歷完整個(gè)鏈表numd還是0就說明沒有這個(gè)人 loc = -1;//讓位置為-1 summary[0] = loc; } return summary;//返回指向整型數(shù)組的指針 }
最后,我們來實(shí)現(xiàn)遍歷打印出查找單的函數(shù)void printsMenu(Node* head, int* loc);傳入鏈表的頭結(jié)點(diǎn)和指向整型數(shù)組的指針,它的具體實(shí)現(xiàn)如下:
void printsMenu(Node* head, int* loc) { if (!isempty(head)) { printf("檢測到通訊錄為空,請先添加聯(lián)系人再進(jìn)行操作\n"); return; } Node* ptr;//定義一個(gè)指針用來獲取位置并打印信息 ptr = head->next; int num = 1;//定義位置尋找器用來找到指定的位置 printf("================================查找人匯總=============================\n"); for (int i = 0; i < NUM; i++)//遍歷返回的數(shù)組,數(shù)組最大值為NUM其實(shí)不必要遍歷到NUM,找到0的時(shí)候就可以跳出循環(huán) { if (loc[i] == -1) {//如果位置返回的是-1的話就表明無此人 printf(" 未查詢到此人\n"); break; } while (num!=loc[i]) {//指針指到相應(yīng)位置的結(jié)點(diǎn) num++; ptr = ptr->next; } //打印信息 printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); printf("編號:%d 姓名:%s 電話:%s 住址:%s\n", num, ptr->name, ptr->phone, ptr->addr); printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); if (ptr->next == NULL) {//如果指針指到結(jié)尾就結(jié)束方法 return; } } return; }
此時(shí)我們可以來測試一下這個(gè)功能,在main方法中:
int main() { Node* head; Name name[23]="李白"; //添加兩個(gè)李白,一個(gè)李白,沒有李白和一個(gè)叫李白一個(gè)叫李白1進(jìn)行測試 head=initList(); addListNode(head); addListNode(head); addListNode(head); addListNode(head); int* loc; loc=lookupByname(head,name); printsMenu(head, loc) return 0; }
接下來,既然現(xiàn)在已經(jīng)能夠按照姓名查到位置并輸出了,那么實(shí)現(xiàn)用戶可以根據(jù)聯(lián)系人的姓名刪除對應(yīng)通訊錄中的信息,這里可以考慮到姓名重名的話我們可以再設(shè)計(jì)一個(gè)按照位置刪除聯(lián)系人的函數(shù),這樣在按姓名刪除的函數(shù)中需要通過返回的數(shù)組的大小提醒用戶是否有重名的聯(lián)系人,并提供建議如果有則建議結(jié)束函數(shù)然后采用按位置刪除聯(lián)系人的方式解決,沒有就可以直接刪除還有就是沒有此聯(lián)系人的情況,所以定義函數(shù)void deleteByname(Node* head, Name name[]),依然傳入頭結(jié)點(diǎn)和要?jiǎng)h除的名字
void deleteByname(Node* head, Name name[]) { if (!isempty(head)) { printf("檢測到通訊錄為空,請先添加聯(lián)系人再進(jìn)行操作\n"); return; } Node* p, * q;//定義兩個(gè)指針,p指針指向的是頭結(jié)點(diǎn),q指針指向的是首元結(jié)點(diǎn) p = head; q = head->next; //當(dāng)q查詢到名字時(shí)就將q所指結(jié)點(diǎn)進(jìn)行釋放,然后將p所指結(jié)點(diǎn)也就是q所指結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn)連接到q所指結(jié)點(diǎn)的后一個(gè)結(jié)點(diǎn) while (q) { if (q->next == NULL && !isBatch(q->name, name)) {//當(dāng)遍歷完整個(gè)鏈表并且最后一個(gè)結(jié)點(diǎn)的名字與輸入的名字不符合的情況下結(jié)束遍歷 printf("刪除失敗,未找到該聯(lián)系人\n"); return; } if (isBatch(q->name, name))//如果遍歷的過程中找到與輸入的名字相對于的名字時(shí) { int* ptr = lookupByname(head, name);//調(diào)用查找名字位置的函數(shù)用于查找該名稱是否有重名的情況 int li=1;//定義計(jì)數(shù)器記錄有無重名的情況 for (int i = 0; i < NUM; i++) { if (ptr[i] == 0) { break; } li++; } if (li-1 == 1) {//計(jì)數(shù)器為1,也就是說只有一個(gè)名字無重名 printf("沒有重名的聯(lián)系人\n"); } else { printf("有%d個(gè)重名的聯(lián)系人,建議查詢后使用位置刪除\n",li-1); printf("按1選擇繼續(xù)刪除,按2選擇結(jié)束本次刪除:"); int input; scanf("%d", &input); if (input == 1) { goto loop; } else { return; } } break; } p = p->next; q = q->next; } loop: //刪除結(jié)點(diǎn)的過程 p->next = q->next; q->next = NULL; free(q); printf("刪除聯(lián)系人成功\n"); }
然后在main方法寫測試的用例:
int main() { Node* head; Name deletename[23]="李白"; //添加兩個(gè)李白,一個(gè)李白,沒有李白和一個(gè)叫李白一個(gè)叫李白1進(jìn)行測試 head=initList(); addListNode(head); addListNode(head); addListNode(head); addListNode(head); deleteByName(head,deletename); return 0; }
添加兩個(gè)李白的情況:
添加一個(gè)李白的情況:
沒有李白的情況:
接下來,既然能夠按照聯(lián)系人的名稱進(jìn)行刪除了,那么在重名的情況下,還需要一種刪除的方法,也就是按照位置的刪除方法,所以可以定義函數(shù)void deleteByLoc(Node* head, int loc),按照位置刪除聯(lián)系人,它的具體實(shí)現(xiàn)如下:
void deleteByLoc(Node* head, int loc) { if (!isempty(head)) { printf("檢測到通訊錄為空,請先添加聯(lián)系人再進(jìn)行操作\n"); return; } Node* move,*q,*choic; choic = head->next;//choic指向首元結(jié)點(diǎn),用來判斷鏈表中結(jié)點(diǎn)的數(shù)量,避免程序出現(xiàn)nullptr //刪除操作的兩個(gè)指針 q = head; move = head->next; int num = 1;//位置計(jì)數(shù)器,一直自增到對應(yīng)的位置loc int t = 1;//鏈表數(shù)量器,用來遍歷獲取鏈表的結(jié)點(diǎn)數(shù)量 while (choic) {//獲取鏈表的實(shí)際長度 if (choic->next == NULL) { break; } choic = choic->next; t++; } while (num!=loc&&move) {//尋找到要?jiǎng)h除的位置 q = q->next; move = move->next; num++; } if (num >= t) {//如果要?jiǎng)h除的位置比鏈表的長度都長就說明刪除錯(cuò)誤 printf("查詢錯(cuò)誤,已經(jīng)超出已有人數(shù)上限,會(huì)造成程序異常\n"); return; } else { q->next = move->next; move->next = NULL; free(move); printf("刪除成功\n"); } }
再次使用main方法進(jìn)行測試:
int main() { Node* head; int loc=1; head=initList(); addListNode(head); deleteByLoc(head,1); return 0; }
到目前位置,整個(gè)通訊錄的功能已經(jīng)完成了,接下來完成修改聯(lián)系人的信息和插入新的聯(lián)系人的功能,先完成修改聯(lián)系人信息的功能,既然可以查找到重名的聯(lián)系人,所以此時(shí)需要按照位置修改聯(lián)系人信息,所以定義函數(shù)void modifyByName(Node* head, int loc),闖入頭結(jié)點(diǎn)和需要修改的位置,在修改的時(shí)候,我們希望可以展示修改人原先的信息,有些算法的思想和按位刪除聯(lián)系人的思想一致,具體實(shí)現(xiàn)如下:
void modifyByName(Node* head, int loc) { if (!isempty(head)) { printf("檢測到通訊錄為空,請先添加聯(lián)系人再進(jìn)行操作\n"); return; } Node* move, * choic; choic = head->next; move = head->next; int num = 1; int t = 1; while (choic) { choic = choic->next; t++; if (choic->next == NULL) { break; } } while (num != loc && move) { move = move->next; num++; } if (num > t) { printf("位置錯(cuò)誤,已經(jīng)超出已有人數(shù)上限,會(huì)造成程序異常\n"); return; } else { printf("找到聯(lián)系人信息\n"); printf("正在檢測聯(lián)系人信息...........\n"); printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); printf("編號:%d 姓名:%s 電話:%s 住址:%s\n", num, move->name, move->phone, move->addr); printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); printf("按1鍵更改聯(lián)系人信息,按2鍵不更改聯(lián)系人信息并結(jié)束:"); int inputs; scanf("%d", &inputs); if (inputs==1) { int istrue; do { printf("請輸入聯(lián)系人信息:\n"); printf("姓名:"); scanf("%s", &move->name); printf("電話號碼:"); scanf("%s", &move->phone); printf("家庭地址:"); scanf("%s", &move->addr); printf("按1鍵確認(rèn)更改,按2鍵重新更改:"); scanf("%d", &istrue); } while (istrue!=1); printf("修改聯(lián)系人信息成功\n"); } else { printf("ERROR"); return; } } }
同樣在main函數(shù)中進(jìn)行測試
最后完成最后一個(gè)功能,按位置插入聯(lián)系人,這個(gè)函數(shù)的意義不大,但是為了鞏固鏈表的插入而設(shè)計(jì)的,定義一個(gè)函數(shù)void insertNodeByLoc(Node* head, int loc)算法的實(shí)現(xiàn)思路與按位刪除的一致,只是找到后是將新的結(jié)點(diǎn)插入而已,具體實(shí)現(xiàn)如下:
void insertNodeByLoc(Node* head, int loc) { if (!isempty(head)) { printf("檢測到通訊錄為空,請先添加聯(lián)系人再進(jìn)行操作\n"); return; } Node* p, * q,*m; m = head->next; p = head->next; q = head; int num=1; int i=1; while (m) { i++; if (m->next == NULL) { break; } } while (num!=loc&&p) { p = p->next; q = q->next; num++; } if (num >= i) { printf("插入位置錯(cuò)誤,已經(jīng)超出已有數(shù)量上限,會(huì)造成程序異常\n"); return; } else { Node* node; node = (Node*)malloc(sizeof(Node)); printf("請輸入聯(lián)系人信息:\n"); printf("姓名:"); scanf("%s", &node->name); printf("電話號碼:"); scanf("%s", &node->phone); printf("家庭地址:"); scanf("%s", &node->addr); node->next = p->next; p->next = node; printf("插入成功\n"); } }
同樣進(jìn)行測試:
以上就是C語言鏈表案例學(xué)習(xí)之通訊錄的實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于C語言鏈表實(shí)現(xiàn)通訊錄的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++實(shí)現(xiàn)一個(gè)簡易版的事件(Event)的示例代碼
之前在?windows系統(tǒng)中開發(fā)應(yīng)用時(shí),?遇到需要進(jìn)行線程同步的時(shí)候幾乎都是使用的事件內(nèi)核對象?Event。本文為大家整理了C++實(shí)現(xiàn)一個(gè)簡易版的事件(Event)的相關(guān)資料,需要的可以參考一下2022-11-11基于C中一個(gè)行壓縮圖的簡單實(shí)現(xiàn)代碼
首先簡單說一下什么是行壓縮圖,其實(shí)嚴(yán)格意義上應(yīng)該是行壓縮矩陣2013-05-05C++實(shí)現(xiàn)景區(qū)信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)景區(qū)信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01C語言實(shí)現(xiàn)學(xué)生成績管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)學(xué)生成績管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01使用C++實(shí)現(xiàn)給PDF文檔添加文字水印
這篇文章主要為大家詳細(xì)介紹了如何通過第三方國產(chǎn)庫Spire.PDF?for?C++來實(shí)現(xiàn)給PDF文檔添加文字水印,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11深入解析C++中的函數(shù)模板和函數(shù)的默認(rèn)參數(shù)
這篇文章主要介紹了深入解析C++中的函數(shù)模板和函數(shù)的默認(rèn)參數(shù),是C++入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-09-09