目錄
前言:
一:?jiǎn)蝹€(gè)節(jié)點(diǎn)的設(shè)計(jì)和主邏輯?
結(jié)點(diǎn)設(shè)計(jì)
主邏輯
二:接口實(shí)現(xiàn)
(1)生成一個(gè)新的結(jié)點(diǎn)
(2)增加信息
(3)打印信息
(4)查找?
(5)刪除信息
(6)修改信息
(7)排序
?插入排序
快速排序
(8)已有數(shù)據(jù)讀取
(9)更新數(shù)據(jù)錄入
三:全部代碼
contact.h(聲明)
contact.c(接口)
test.c(主邏輯)
前言:
本文使用的存儲(chǔ)結(jié)構(gòu)是不帶哨兵位鏈表,還包含了文件操作(讀取和錄入),有類似課程設(shè)計(jì)的同學(xué)可能需要(完整代碼在最后)。
鏈表(知道基礎(chǔ)概念,清楚形參實(shí)參關(guān)系的可以不看):
https://blog.csdn.net/2301_76269963/article/details/129586021?spm=1001.2014.3001.5502
一:?jiǎn)蝹€(gè)節(jié)點(diǎn)的設(shè)計(jì)和主邏輯?
結(jié)點(diǎn)設(shè)計(jì)
需求:一個(gè)人要包含姓名、年齡、性別、電話號(hào)碼、地址這些信息,把這些信息封裝成一個(gè)結(jié)構(gòu)體。
要讓每個(gè)人的數(shù)據(jù)鏈接成一個(gè)整體,節(jié)點(diǎn)除了個(gè)人信息之外還要保存下個(gè)結(jié)點(diǎn)的地址。
主邏輯
(1)每次開(kāi)始都從文件中讀取數(shù)據(jù)
(2)通訊錄的功能包括:①增加數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?②依據(jù)名字刪除數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?③依據(jù)名字查找數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?④依據(jù)名字修改數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?⑤依據(jù)年齡排序數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?⑥打印數(shù)據(jù)
(3)退出后重新錄入數(shù)據(jù)
二:接口實(shí)現(xiàn)
(1)生成一個(gè)新的結(jié)點(diǎn)
//生成一個(gè)新節(jié)點(diǎn) Node* BuyNewNode() { Node* NewNode = (Node*)malloc(sizeof(Node)); if (NewNode == NULL) { printf("malloc error\n"); exit(-1); } NewNode->next = NULL; printf("請(qǐng)輸入名字:"); scanf("%s", NewNode->data.name); printf("請(qǐng)輸入年齡:"); scanf("%d", &NewNode->data.age); printf("請(qǐng)輸入性別:"); scanf("%s", NewNode->data.sex); printf("請(qǐng)輸入電話:"); scanf("%s", NewNode->data.tele); printf("請(qǐng)輸入地址:"); scanf("%s", NewNode->data.addr); return NewNode; }
(2)增加信息
//增加信息,可能要更改頭指針,傳二級(jí)指針修改一級(jí)指針 void AddContact(Node** pphead) { //生成新節(jié)點(diǎn) Node* NewNode = BuyNewNode(); //單獨(dú)處理空的情況 if (*pphead == NULL) { *pphead = NewNode; return; } else { NewNode->next = *pphead; *pphead = NewNode; } }
圖解:
(3)打印信息
//打印信息 void PrintContact(Node* phead) { //注意打印格式,加-表示字符左對(duì)齊,數(shù)字表示最小字符數(shù)(不足補(bǔ)空格) printf("%-20s\t%-5s\t%-5s\t%-20s\t%-20s\n", "名字", "年齡", "性別", "電話", "地址"); while (phead != NULL) { printf("%-20s\t%-5d\t%-5s\t%-20s\t%-20s\n", phead->data.name,phead->data.age, phead->data.sex,phead->data.tele,phead->data.addr); //迭代 phead = phead->next; } }
(4)查找?
注意:后續(xù)的刪除和修改都需要用到查找,為增強(qiáng)代碼復(fù)用性,查找函數(shù)返回結(jié)點(diǎn)的地址,如果未查找到就返回空。
//查找,第二個(gè)參數(shù)為名字字符串首地址 Node* FindContact(Node* phead,char* name) { //遍歷鏈表 while (phead) { //strcmp()函數(shù)用于字符串比較,相同返回0,否則返回非0值 if (strcmp(phead->data.name, name) == 0) { return phead; } phead = phead->next; } //沒(méi)找到返回空 return NULL; }
(5)刪除信息
先查找,找到待刪除結(jié)點(diǎn)地址,如果待刪除結(jié)點(diǎn)為頭節(jié)點(diǎn),需要更新頭指針;
否則就需要找到待刪除結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn),前一個(gè)結(jié)點(diǎn)指針域指向待刪除結(jié)點(diǎn)的下一個(gè)結(jié)點(diǎn),然后釋放待刪除結(jié)點(diǎn)。
//刪除信息,可能要更改頭指針,傳二級(jí)指針修改一級(jí)指針 void DelContact(Node** pphead) { //信息為空不能刪除 assert(*pphead != NULL); //查找 char name[max_name]; printf("請(qǐng)輸入要?jiǎng)h除聯(lián)系人的名字:"); scanf("%s", name); Node* pos = FindContact(*pphead,name); if (pos == NULL) { printf("查找失敗\n"); return; } //如果pos是第一個(gè)節(jié)點(diǎn) if (pos == *pphead) { //找到下一個(gè) Node* NEXT = pos->next; free(pos); *pphead = NEXT; } else { //找到前一個(gè) Node* prev = *pphead; while (prev->next != pos) { prev = prev->next; } prev->next = pos->next; free(pos); } printf("刪除成功\n"); }
圖解:
(6)修改信息
先查找到目標(biāo)結(jié)點(diǎn),然后進(jìn)行修改。
//修改信息 void ModifyContact(Node* phead) { //查找 char name[max_name]; printf("請(qǐng)輸入要修改聯(lián)系人的名字:"); scanf("%s", name); Node* pos = FindContact(phead, name); if (pos == NULL) { printf("查找失敗\n"); return; } else { printf("找到了,進(jìn)行修改\n"); printf("請(qǐng)輸入名字:"); scanf("%s", pos->data.name); printf("請(qǐng)輸入年齡:"); scanf("%d", &pos->data.age); printf("請(qǐng)輸入性別:"); scanf("%s", pos->data.sex); printf("請(qǐng)輸入電話:"); scanf("%s", pos->data.tele); printf("請(qǐng)輸入地址:"); scanf("%s", pos->data.addr); } }
(7)排序
這里給兩種實(shí)現(xiàn)方式,第一種使用插入排序(方便理解),第二種使用快速排序(效率更快,有空間消耗,代碼量更多)。
?插入排序
插入排序的核心思想是:將一個(gè)未排序的元素插入到已排序的子序列中,使得插入后依然保持有序。具體實(shí)現(xiàn)時(shí),我們可以從第二個(gè)元素開(kāi)始,將其與前面已排序序列的元素比較,找到其正確的插入位置,然后插入即可。在插入過(guò)程中,需要將已排序序列中比該元素大的元素后移,為該元素騰出插入位置(看一遍就可以看代碼和圖解)。
//交換數(shù)據(jù) void swap(Node* p1, Node* p2) { struct PepInfo tmp = p1->data; p1->data = p2->data; p2->data = tmp; } //進(jìn)行排序 void sort(Node* phead) { //空不排序 assert(phead != NULL); Node* begin = phead; Node* end = phead->next; while (end) { while (begin != end) { if (begin->data.age > end->data.age) { swap(begin,end); } begin = begin->next; } //更新 end = end->next; begin = phead; } }
圖解:
快速排序
這里就不展開(kāi)講快速排序的思想了,也不建議用快速排序,一般通訊錄這樣的數(shù)據(jù)量用插入排序就足夠了,感興趣的同學(xué)可以看鏈接。
快速排序鏈接:https://blog.csdn.net/2301_76269963/article/details/130473353?spm=1001.2014.3001.5502
先將鏈表中的數(shù)據(jù)拷貝到數(shù)組中,對(duì)數(shù)組進(jìn)行排序,再把數(shù)組數(shù)據(jù)拷貝回鏈表。
//交換數(shù)據(jù) void swap(PepInfo* p1, PepInfo* p2) { PepInfo tmp = *p1; *p1 = *p2; *p2 = tmp; } // 三數(shù)取中 int GetMidIndex(PepInfo* a, int left, int right) { int midi = left + (right - left) / 2; if (a[midi].age > a[right].age) { if (a[midi].age < a[left].age) return midi; else if (a[right].age > a[left].age) return right; else return left; } else //a[right] > a[mid] { if (a[midi].age > a[left].age) return midi; else if (a[left].age < a[right].age) return left; else return right; } } //前后指針?lè)?int partion3(PepInfo* a, int left, int right) { int midi = GetMidIndex(a, left, right); swap(&a[midi], &a[left]); int keyi = left; int prev = left; int cur = left + 1; while (cur <= right) { //++prev和cur相等,無(wú)效交換,不換 if (a[cur].age < a[keyi].age && ++prev != cur) { swap(&a[prev], &a[cur]); } cur++; } swap(&a[keyi], &a[prev]); return prev; } //快速排序 void QuickSort(PepInfo* a, int left, int right) { //如果left>right,區(qū)間不存在 //left==right,只有一個(gè)元素,可以看成是有序的 if (left >= right) { return; } else { //單次排序 int keyi = partion3(a, left, right); //分成左右區(qū)間,排序左右區(qū)間 QuickSort(a, left, keyi - 1); QuickSort(a, keyi + 1, right); } } // 鏈表快速排序函數(shù) void sortList(Node* phead) { // 處理空或單節(jié)點(diǎn)鏈表的情況 if (!phead || !phead->next) { return; } //求鏈表長(zhǎng)度 int len = 0; Node* cur = phead; while (cur) { len++; cur = cur->next; } // 將鏈表轉(zhuǎn)換為數(shù)組 PepInfo* arr = malloc(len * sizeof(PepInfo)); if (arr == NULL) { printf("malloc error\n"); exit(-1); } cur = phead; for (int i = 0; i < len; i++) { arr[i] = cur->data; cur = cur->next; } // 快速排序 QuickSort(arr, 0, len - 1); // 將數(shù)組轉(zhuǎn)換為鏈表 cur = phead; for (int i = 0; i < len; i++) { cur->data = arr[i]; cur = cur->next; } free(arr); }
(8)已有數(shù)據(jù)讀取
①生成一個(gè)新結(jié)點(diǎn),將讀取的數(shù)據(jù)存入新結(jié)點(diǎn)中。
②第一次讀取時(shí)鏈表為空,修改頭指針。
③后面的新結(jié)點(diǎn)鏈接到鏈表的尾部。
(注意:讀模式打開(kāi)文件需要文件夾中有這個(gè)文件,第一次需要手動(dòng)創(chuàng)建一個(gè)data.txt文件)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-454312.html
//已有數(shù)據(jù)讀取,可能要更改頭指針,傳二級(jí)指針修改一級(jí)指針 void StartFileEntry(Node** pphead) { FILE* pf = fopen("data.txt", "rb"); if (pf == NULL) { printf("打開(kāi)文件失敗\n"); exit(-1); } //保存讀取到的數(shù)據(jù) PepInfo* tmp = (PepInfo*)malloc(sizeof(PepInfo)); if (tmp == NULL) { printf("malloc error\n"); exit(-1); } //先讀取一次 fread(tmp, sizeof(PepInfo), 1, pf); //保存尾部節(jié)點(diǎn)地址 Node* tail = NULL; //讀取到結(jié)尾時(shí)feof()返回0,未讀取到結(jié)尾返回非0值 while (!feof(pf)) { //生成新節(jié)點(diǎn) Node* NewNode = (Node*)malloc(sizeof(Node)); if (NewNode == NULL) { printf("malloc error\n"); exit(-1); } NewNode->data = *tmp; NewNode->next = NULL; //第一次頭為空,更新 if (*pphead == NULL) { *pphead = NewNode; tail = *pphead; } //頭不為空,新節(jié)點(diǎn)鏈接到尾后面,尾指針更新 else { tail->next = NewNode; tail = tail->next; } //繼續(xù)讀取 fread(tmp, sizeof(PepInfo), 1, pf); } fclose(pf); }
(9)更新數(shù)據(jù)錄入
鏈表為空,直接返回;否則遍歷鏈表,依次錄入數(shù)據(jù)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-454312.html
//新數(shù)據(jù)錄入 void UpdateFileEntry(Node* phead) { FILE* pf = fopen("data.txt", "wb"); if (phead == NULL) { return; } else { while (phead) { fwrite(&(phead->data), sizeof(PepInfo), 1, pf); phead = phead->next; } } fclose(pf); }
三:全部代碼
contact.h(聲明)
#pragma once #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> //方便后面更改這些信息的最大容量 #define max_name 20 #define max_tele 20 #define max_addr 20 #define max_sex 10 //個(gè)人信息 typedef struct PepInfo { char name[max_name]; int age; char sex[max_sex]; char tele[max_tele]; char addr[max_addr]; }PepInfo; typedef struct Node { //個(gè)人信息 struct PepInfo data; //指針域 struct Node* next; }Node; //生成一個(gè)新節(jié)點(diǎn) Node* BuyNewNode(); //增加信息,可能要更改頭指針,傳二級(jí)指針修改一級(jí)指針 void AddContact(Node** pphead); //打印信息 void PrintContact(Node* phead); //查找 Node* FindContact(Node* phead, char* name); //刪除信息,可能要更改頭指針,傳二級(jí)指針修改一級(jí)指針 void DelContact(Node** pphead); //修改信息 void ModifyContact(Node* phead); //進(jìn)行排序 // 鏈表快速排序函數(shù) void sortList(Node* phead); //插入排序 void sort(Node* phead); //已有數(shù)據(jù)讀取,可能要更改頭指針,傳二級(jí)指針修改一級(jí)指針 void StartFileEntry(Node** pphead); //更新數(shù)據(jù)錄入 void UpdateFileEntry(Node* phead);
contact.c(接口)
#define _CRT_SECURE_NO_WARNINGS 1 #include "contact.h" //生成一個(gè)新節(jié)點(diǎn) Node* BuyNewNode() { Node* NewNode = (Node*)malloc(sizeof(Node)); if (NewNode == NULL) { printf("malloc error\n"); exit(-1); } NewNode->next = NULL; printf("請(qǐng)輸入名字:"); scanf("%s", NewNode->data.name); printf("請(qǐng)輸入年齡:"); scanf("%d", &NewNode->data.age); printf("請(qǐng)輸入性別:"); scanf("%s", NewNode->data.sex); printf("請(qǐng)輸入電話:"); scanf("%s", NewNode->data.tele); printf("請(qǐng)輸入地址:"); scanf("%s", NewNode->data.addr); return NewNode; } //增加信息,可能要更改頭指針,傳二級(jí)指針修改一級(jí)指針 void AddContact(Node** pphead) { //生成新節(jié)點(diǎn) Node* NewNode = BuyNewNode(); //單獨(dú)處理空的情況 if (*pphead == NULL) { *pphead = NewNode; return; } else { NewNode->next = *pphead; *pphead = NewNode; } } //打印信息 void PrintContact(Node* phead) { //注意打印格式,加-表示字符左對(duì)齊,數(shù)字表示最小字符數(shù)(不足補(bǔ)空格) printf("%-20s\t%-5s\t%-5s\t%-20s\t%-20s\n", "名字", "年齡", "性別", "電話", "地址"); while (phead != NULL) { printf("%-20s\t%-5d\t%-5s\t%-20s\t%-20s\n", phead->data.name,phead->data.age, phead->data.sex,phead->data.tele,phead->data.addr); //迭代 phead = phead->next; } } //查找,第二個(gè)參數(shù)為名字字符串首地址 Node* FindContact(Node* phead,char* name) { //遍歷鏈表 while (phead) { //strcmp()函數(shù)用于字符串比較,相同返回0,否則返回非0值 if (strcmp(phead->data.name, name) == 0) { return phead; } phead = phead->next; } //沒(méi)找到返回空 return NULL; } //刪除信息,可能要更改頭指針,傳二級(jí)指針修改一級(jí)指針 void DelContact(Node** pphead) { //斷言,信息為空不能刪除 assert(*pphead != NULL); //查找 char name[max_name]; printf("請(qǐng)輸入要?jiǎng)h除聯(lián)系人的名字:"); scanf("%s", name); Node* pos = FindContact(*pphead,name); if (pos == NULL) { printf("查找失敗\n"); return; } //如果pos是第一個(gè)節(jié)點(diǎn) if (pos == *pphead) { //找到下一個(gè) Node* NEXT = pos->next; free(pos); *pphead = NEXT; } else { //找到前一個(gè) Node* prev = *pphead; while (prev->next != pos) { prev = prev->next; } prev->next = pos->next; free(pos); } printf("刪除成功\n"); } //修改信息 void ModifyContact(Node* phead) { //查找 char name[max_name]; printf("請(qǐng)輸入要修改聯(lián)系人的名字:"); scanf("%s", name); Node* pos = FindContact(phead, name); if (pos == NULL) { printf("查找失敗\n"); return; } else { printf("找到了,進(jìn)行修改\n"); printf("請(qǐng)輸入名字:"); scanf("%s", pos->data.name); printf("請(qǐng)輸入年齡:"); scanf("%d", &pos->data.age); printf("請(qǐng)輸入性別:"); scanf("%s", pos->data.sex); printf("請(qǐng)輸入電話:"); scanf("%s", pos->data.tele); printf("請(qǐng)輸入地址:"); scanf("%s", pos->data.addr); } } 交換數(shù)據(jù) //void swap(PepInfo* p1, PepInfo* p2) //{ // PepInfo tmp = *p1; // *p1 = *p2; // *p2 = tmp; //} // 三數(shù)取中 //int GetMidIndex(PepInfo* a, int left, int right) //{ // int midi = left + (right - left) / 2; // // if (a[midi].age > a[right].age) // { // if (a[midi].age < a[left].age) // return midi; // else if (a[right].age > a[left].age) // return right; // else // return left; // } // else // a[right] > a[mid] // { // if (a[midi].age > a[left].age) // return midi; // else if (a[left].age < a[right].age) // return left; // else // return right; // } //} // 前后指針?lè)?//int partion3(PepInfo* a, int left, int right) //{ // int midi = GetMidIndex(a, left, right); // swap(&a[midi], &a[left]); // // int keyi = left; // int prev = left; // int cur = left + 1; // while (cur <= right) // { // //++prev和cur相等,無(wú)效交換,不換 // if (a[cur].age < a[keyi].age && ++prev != cur) // { // swap(&a[prev], &a[cur]); // } // cur++; // } // swap(&a[keyi], &a[prev]); // return prev; //} // 快速排序 //void QuickSort(PepInfo* a, int left, int right) //{ // //如果left>right,區(qū)間不存在 // //left==right,只有一個(gè)元素,可以看成是有序的 // if (left >= right) // { // return; // } // else // { // //單次排序 // int keyi = partion3(a, left, right); // //分成左右區(qū)間,排序左右區(qū)間 // QuickSort(a, left, keyi - 1); // QuickSort(a, keyi + 1, right); // } //} // 鏈表快速排序函數(shù) //void sortList(Node* phead) //{ // // 處理空或單節(jié)點(diǎn)鏈表的情況 // if (!phead || !phead->next) // { // return; // } // // //求鏈表長(zhǎng)度 // int len = 0; // Node* cur = phead; // while (cur) // { // len++; // cur = cur->next; // } // // // 將鏈表轉(zhuǎn)換為數(shù)組 // PepInfo* arr = malloc(len * sizeof(PepInfo)); // if (arr == NULL) // { // printf("malloc error\n"); // exit(-1); // } // cur = phead; // for (int i = 0; i < len; i++) // { // arr[i] = cur->data; // cur = cur->next; // } // // // 快速排序 // QuickSort(arr, 0, len - 1); // // // 將數(shù)組轉(zhuǎn)換為鏈表 // cur = phead; // for (int i = 0; i < len; i++) // { // cur->data = arr[i]; // cur = cur->next; // } // // free(arr); //} //交換數(shù)據(jù) void swap(Node* p1, Node* p2) { struct PepInfo tmp = p1->data; p1->data = p2->data; p2->data = tmp; } //進(jìn)行排序 void sort(Node* phead) { //空不排序 assert(phead != NULL); Node* begin = phead; Node* end = phead->next; while (end) { while (begin != end) { if (begin->data.age > end->data.age) { swap(begin,end); } begin = begin->next; } end = end->next; begin = phead; } } //已有數(shù)據(jù)讀取,可能要更改頭指針,傳二級(jí)指針修改一級(jí)指針 void StartFileEntry(Node** pphead) { FILE* pf = fopen("data.txt", "rb"); if (pf == NULL) { printf("打開(kāi)文件失敗\n"); exit(-1); } //保存讀取到的數(shù)據(jù) PepInfo* tmp = (PepInfo*)malloc(sizeof(PepInfo)); if (tmp == NULL) { printf("malloc error\n"); exit(-1); } //先讀取一次 fread(tmp, sizeof(PepInfo), 1, pf); //保存尾部節(jié)點(diǎn)地址 Node* tail = NULL; //讀取到結(jié)尾時(shí)feof()返回0,未讀取到結(jié)尾返回非0值 while (!feof(pf)) { //生成新節(jié)點(diǎn) Node* NewNode = (Node*)malloc(sizeof(Node)); if (NewNode == NULL) { printf("malloc error\n"); exit(-1); } NewNode->data = *tmp; NewNode->next = NULL; //第一次頭為空,更新 if (*pphead == NULL) { *pphead = NewNode; tail = *pphead; } //頭不為空,新節(jié)點(diǎn)鏈接到尾后面,尾指針更新 else { tail->next = NewNode; tail = tail->next; } //繼續(xù)讀取 fread(tmp, sizeof(PepInfo), 1, pf); } fclose(pf); } //新數(shù)據(jù)錄入 void UpdateFileEntry(Node* phead) { FILE* pf = fopen("data.txt", "wb"); if (phead == NULL) { return; } else { while (phead) { fwrite(&(phead->data), sizeof(PepInfo), 1, pf); phead = phead->next; } } fclose(pf); }
test.c(主邏輯)
#define _CRT_SECURE_NO_WARNINGS 1 #include "contact.h" void menu() { printf("********************************\n"); printf("***** 1.增加 2.刪除 ******\n"); printf("***** 3.查找 4.修改 ******\n"); printf("***** 5.排序 6.打印 ******\n"); printf("***** 0.退出 ******\n"); printf("********************************\n"); } int main() { Node* head = NULL; int input = 0; //原信息錄入,這里用讀模式打開(kāi),第一次需要手動(dòng)創(chuàng)建文件 StartFileEntry(&head); do { menu(); printf("請(qǐng)選擇:>"); scanf("%d", &input); switch (input) { case 1: AddContact(&head); printf("增加成功\n"); break; case 2: DelContact(&head); break; case 3: { char name[max_name]; printf("請(qǐng)輸入要查找聯(lián)系人的名字:"); scanf("%s", name); Node* pos = FindContact(head, name); if (pos == NULL) { printf("查找失敗\n"); } else { printf("找到了\n"); printf("%-20s\t%-5d\t%-5s\t%-20s\t%-20s\n", pos->data.name, pos->data.age, pos->data.sex, pos->data.tele, pos->data.addr); } break; } case 4: ModifyContact(head); break; case 5: sort(head); printf("排序成功\n"); break; case 6: PrintContact(head); break; case 0: printf("退出\n"); break; default: printf("非法輸入,重新輸入\n"); break; } } while (input); //新信息錄入 UpdateFileEntry(head); return 0; }
到了這里,關(guān)于動(dòng)態(tài)通訊錄實(shí)現(xiàn)(C語(yǔ)言)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!