一.前置知識(shí)
1.數(shù)據(jù)結(jié)構(gòu)
1.1為什么會(huì)存在數(shù)據(jù)結(jié)構(gòu)?
我們常常接觸到諸如生活中的姓名、身份證、網(wǎng)頁內(nèi)的圖片、視頻等各種各樣的信息,這些信息就是我們常說的數(shù)據(jù)。在使用這些數(shù)據(jù)時(shí),我們發(fā)現(xiàn)隨著數(shù)據(jù)的增加,當(dāng)我們要單獨(dú)尋找某一個(gè)數(shù)據(jù)時(shí)就會(huì)非常困難,就像圖書館內(nèi)書籍如果沒有按一定的順序排放,及時(shí)我們知道我們要找的書籍叫什么,我們也無法在浩如煙海的書籍內(nèi)找到它。
同理,程序中如果不對(duì)數(shù)據(jù)進(jìn)行管理,可能會(huì)導(dǎo)致數(shù)據(jù)丟失、操作數(shù)據(jù)困難、野指針等情況。
因此,對(duì)于數(shù)據(jù),我們需要向像圖書館排列圖書一樣,采用以一定的形式將數(shù)據(jù)組織起來,方便日后進(jìn)行調(diào)用。
總結(jié):數(shù)據(jù)結(jié)構(gòu)是計(jì)算機(jī)存儲(chǔ)、組織數(shù)據(jù)的方式。數(shù)據(jù)結(jié)構(gòu)是指相互之間存在?種或多種特定關(guān)系的數(shù)據(jù)元素的集合。數(shù)據(jù)結(jié)構(gòu)反映數(shù)據(jù)的內(nèi)部構(gòu)成,即數(shù)據(jù)由那部分構(gòu)成,以什么方式構(gòu)成,以及數(shù)據(jù)元素之間呈現(xiàn)的結(jié)構(gòu)。
1.2數(shù)據(jù)結(jié)構(gòu)之線性表
(1)線性表的定義
線性表(linear list)是n個(gè)具有相同特性的數(shù)據(jù)元素的有限序列。具有邏輯上線性,物理存儲(chǔ)上不一定連續(xù)的特性。?線性表是?種在實(shí)際中廣泛使用的數(shù)據(jù)結(jié)構(gòu),常見的線性表:順序表、鏈表、棧、隊(duì)列、字符串...
(2)線性表結(jié)構(gòu)特點(diǎn)
?(a)邏輯結(jié)構(gòu):邏輯上是線性結(jié)構(gòu),也就說是連續(xù)的?條直線。
通過線性表進(jìn)行組織管理的數(shù)據(jù)邏輯上呈現(xiàn)由前到后,一對(duì)一的線性關(guān)系,可以通過如C語言中的結(jié)構(gòu)體與指針來實(shí)現(xiàn)該線性邏輯下的一個(gè)一個(gè)數(shù)據(jù)的訪問,就好像這些數(shù)據(jù)都是穿在一條線上的一樣。
(b)物理結(jié)構(gòu):物理結(jié)構(gòu)上并不?定是連續(xù)的(如順序表物理上連續(xù),鏈表不連續(xù))
線性表在物理上存儲(chǔ)時(shí),通常以數(shù)組和鏈?zhǔn)浇Y(jié)構(gòu)的形式存儲(chǔ)。以數(shù)組為例,我們都知道數(shù)組中每個(gè)元素的存儲(chǔ)是在內(nèi)存中開辟一塊練習(xí)的空間進(jìn)行存放,元素的地址是連續(xù)的,這就是物理結(jié)構(gòu)連續(xù)的意思
但是如鏈?zhǔn)浇Y(jié)構(gòu),用來存放不同元素的空間時(shí)用一塊就申請(qǐng)一塊,通過指針來實(shí)現(xiàn)不同空間的串連,不同空間地址并不是連續(xù)的,不具有物理結(jié)構(gòu)上的連續(xù)性。
二.順序表
1.為什么要有順序表
在C語言中數(shù)組其實(shí)就是能算一種基本的數(shù)據(jù)結(jié)構(gòu),數(shù)組有助于我們實(shí)現(xiàn)對(duì)數(shù)組的查找、修改、調(diào)用等多種功能??此乒δ軓?qiáng)大,但其實(shí)遠(yuǎn)遠(yuǎn)不夠。
現(xiàn)在假定數(shù)組有10個(gè)空間,已經(jīng)使用了5個(gè),向數(shù)組中插入數(shù)據(jù)步驟:
求數(shù)組的長(zhǎng)度,求數(shù)組的有效數(shù)據(jù)個(gè)數(shù),向下標(biāo)為數(shù)據(jù)有效個(gè)數(shù)的位置插入數(shù)據(jù)(注意:這里是
否要判斷數(shù)組是否滿了,滿了還能繼續(xù)插入嗎).....
再假設(shè)數(shù)據(jù)量非常龐大,頻繁的獲取數(shù)組有效數(shù)據(jù)個(gè)數(shù)會(huì)還會(huì)影響程序執(zhí)行效率。因此我們需要學(xué)習(xí)其他數(shù)據(jù)結(jié)構(gòu),來提升效率。
而順序表就在數(shù)組的基礎(chǔ)上對(duì)其進(jìn)行了封裝,在其基礎(chǔ)上實(shí)現(xiàn)了常用的增刪改查等功能接口。
2.靜態(tài)順序表的定義
a.特點(diǎn):
底層創(chuàng)建定長(zhǎng)數(shù)組來存儲(chǔ)元素,為對(duì)數(shù)組進(jìn)行增刪查改的操作,我們還需要?jiǎng)?chuàng)建一個(gè)變量來記錄已存放有效的數(shù)據(jù)個(gè)數(shù)。
b.缺點(diǎn):存儲(chǔ)空間過大或過小
由于實(shí)際生活中,我們往往并不能準(zhǔn)確預(yù)計(jì)實(shí)際使用時(shí)需要存放數(shù)據(jù)數(shù)量,而且由于靜態(tài)順序表底層是使用數(shù)組,大小固定,因此往往會(huì)出現(xiàn)空間申請(qǐng)過大或過小的情況,如果空間申請(qǐng)過大,那么會(huì)白白消耗大量的資源,如果空間申請(qǐng)過小,再對(duì)順序表操作,則會(huì)出現(xiàn)數(shù)據(jù)丟失的情況。上述兩種情況都會(huì)給公司造成大量的損失。因此,并不推薦屏幕前的讀者使用靜態(tài)順序表。
3.動(dòng)態(tài)順序表的定義
a.特點(diǎn):
動(dòng)態(tài)順序表定義時(shí),不會(huì)直接創(chuàng)建,大小固定的數(shù)組,而是創(chuàng)建一個(gè)指針來配合之后的realloc來動(dòng)態(tài)申請(qǐng)空間使用。同時(shí)由于動(dòng)態(tài)申請(qǐng)空間大小,空間大小時(shí)變化的,因此除了需要變量來記錄存放的數(shù)據(jù)個(gè)數(shù),我們還需要記錄已申請(qǐng)的空間大小。
b.缺點(diǎn):
1.中間/頭部的插入刪除,時(shí)間復(fù)雜度為O(N)
2. 增容需要申請(qǐng)新空間,拷貝數(shù)據(jù),釋放舊空間。會(huì)有不小的消耗。
3. 增容?般是呈2倍的增長(zhǎng),勢(shì)必會(huì)有?定的空間浪費(fèi)。例如當(dāng)前容量為100,滿了以后增容到200,我們?cè)倮^續(xù)插入了5個(gè)數(shù)據(jù),后面沒有數(shù)據(jù)插入了,那么就浪費(fèi)了95個(gè)數(shù)據(jù)空。
但相對(duì)與靜態(tài)順序表較好,推薦使用。
4.實(shí)現(xiàn)增刪查改功能的封裝
(注:基于動(dòng)態(tài)順序表實(shí)現(xiàn),由于靜態(tài)順序表較為簡(jiǎn)單并且不常使用,筆者便不再過多贅述,后文會(huì)給出基于靜態(tài)順序表現(xiàn)的通訊錄。)
1.順序表初始化
//初始化
void SLInit(SL* ps)
{
assert(ps);
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
由于我們是使用SLInit來初始化數(shù)序表,這就意味著順序表未初始化,如果函數(shù)參數(shù)為結(jié)構(gòu)體這里系統(tǒng)就會(huì)報(bào)錯(cuò),因?yàn)閰?shù)為結(jié)構(gòu)體是傳值調(diào)用,形參是實(shí)參的拷貝,但是實(shí)參有沒有初始化,因此這里便會(huì)報(bào)錯(cuò),assert斷言防止傳入空指針,增強(qiáng)代碼韌性,初始時(shí)未申請(qǐng)空間,未添加數(shù)據(jù)指針arr置為NULL,size,capacity賦值0.
2.順序表的銷毀
//銷毀
void SLDestroy(SL* ps)
{
assert(ps);
free(ps->arr);
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
我們需要將指針arr指向的realloc申請(qǐng)的空間釋放掉,再將野指針arr置為NULL,數(shù)據(jù)清空,size、capacity置為空。
3.申請(qǐng)空間
#define INIT_CAPACITY 4
//申請(qǐng)空間
void SLCheckCapacity(SL* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity = 0 ? INIT_CAPACITY : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc:");
exit(1);
}
else
{
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
}
申請(qǐng)空間前我們需要先判斷通過已存儲(chǔ)的數(shù)據(jù)個(gè)數(shù)與空間個(gè)數(shù)是否相等來判斷空間是否足夠,如果
已開辟空間為0;我們先賦予它一個(gè)初始大小空間,如果不為0,我們?cè)侔疵看?倍大小進(jìn)行擴(kuò)容(2倍空間擴(kuò)容是較好選擇,具體原因設(shè)計(jì)數(shù)學(xué)論證,筆者在此不過多贅述)。申請(qǐng)新空間無誤后,我們用arr來指向這片空間。,并將capacity大小更正。
4.數(shù)據(jù)前插
void SLPushFront(SL* ps, SLDataType data)
{
assert(ps);
SLCheckCapacity(ps);
int i = 0;
for (i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = data;
ps->size++;
}
數(shù)據(jù)前插之前我們通過申請(qǐng)空間函數(shù)完成是否申請(qǐng)、申請(qǐng)一系列操作。之后再將原有數(shù)據(jù)后移一位,完成插入,現(xiàn)有數(shù)據(jù)+1,size更正。
5.數(shù)據(jù)尾插
void SLPushBack(SL* ps, SLDataType data)
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size++] = data;
}
先判斷空間是否足夠,再在最后一個(gè)有效數(shù)據(jù)之后添加數(shù)據(jù),更正size。
6.打印順序表數(shù)據(jù)
//打印數(shù)據(jù)
void SLPrint(SL ps)
{
int i = 0;
for (i = 0; i < ps.size; i++)
{
if (ps.arr[i] >= 'a' && ps.arr[i] <= 'z' || ps.arr[i] >= 'A' && ps.arr[i] <= 'Z')
{
printf("%c ", ps.arr[i]);
}
else
{
printf("%d ", ps.arr[i]);
}
}
printf("\n");
}
筆者這邊假設(shè)數(shù)組內(nèi)存儲(chǔ)的數(shù)據(jù)是字母或者數(shù)字來打印的,其實(shí)數(shù)據(jù)的類型遠(yuǎn)不是只有這兩種,不過大體就是這樣,根據(jù)存儲(chǔ)數(shù)據(jù)類型來循環(huán)打印
7.數(shù)據(jù)頭刪
//前刪
void SLPopFront(SL* ps)
{
assert(ps);
int i = 0;
for (i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
前刪的話不是真的將數(shù)據(jù)從數(shù)組內(nèi)移除,我們通過將頭數(shù)據(jù)之后的數(shù)據(jù)前移,頭數(shù)據(jù)就會(huì)被覆蓋掉,然后記錄數(shù)據(jù)個(gè)數(shù)的size-1,這樣即使原有arr[size-1]位置還留存尾部的數(shù)據(jù),之后我們通過size-1后的size來對(duì)數(shù)組操作時(shí),留存的數(shù)據(jù),我們也訪問不到了,就相當(dāng)于刪除了。
8.數(shù)據(jù)尾刪
//尾刪
void SLPopBack(SL* ps)
{
assert(ps);
ps->size--;
}
我們只需要size-1,那么跟據(jù)size對(duì)數(shù)組訪問時(shí),我們是無法,訪問到最后一個(gè)數(shù)據(jù),也就相當(dāng)于刪除掉了。
9.查找指定數(shù)據(jù)
//查找數(shù)據(jù)
int SLFind(SL ps, SLDataType s)
{
int i = 0;
for (i = 0; i < ps.size; i++)
{
if (s == ps.arr[i])
{
printf("找到了,下標(biāo)位置為%d\n", i);
return i;
}
}
if (i == ps.size)
{
puts("很遺憾,找不到數(shù)據(jù)");
return EOF;
}
}
我們根據(jù)size記錄的數(shù)據(jù)的個(gè)數(shù)循環(huán)訪問數(shù)組進(jìn)行比對(duì)查找。、
10.數(shù)據(jù)指定位置插入
//指定位置插入
void SLInsert(SL* ps, int pos, SLDataType data)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
SLCheckCapacity(ps);
int i = 0;
for (i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = data;
ps->size++;
}
插入數(shù)據(jù)時(shí),我們需先明確插入位置pos,作為控制i的大小的數(shù)據(jù),不能為負(fù)數(shù),必須小于size(否則會(huì)發(fā)生數(shù)組越界訪問)。然后要插入位置及之后的數(shù)據(jù)向后移動(dòng)一位,然后將插入位置賦值為要插入的數(shù)據(jù)。
11.數(shù)據(jù)指定位置刪除
//指定位置刪除
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
int i = 0;
for (i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
插入數(shù)據(jù)時(shí),我們需先明確插入位置pos,作為控制i的大小的數(shù)據(jù),不能為負(fù)數(shù),必須小于size(否則會(huì)發(fā)生數(shù)組越界訪問)。然后要插入位置之后的數(shù)據(jù)向前移動(dòng)一位,覆蓋要?jiǎng)h除的數(shù)據(jù),size--,使得無法再訪問最后一個(gè)多余數(shù)據(jù)。
5.順序表的應(yīng)用:通訊錄
C語言基礎(chǔ)要求:結(jié)構(gòu)體、動(dòng)態(tài)內(nèi)存管理、順序表、文件操作
1.基于動(dòng)態(tài)順序表實(shí)現(xiàn)通訊錄
功能要求:
1)至少能夠存儲(chǔ)100個(gè)?的通訊信息 2)能夠保存用戶信息:名字、性別、年齡、電話、地址等
3)增加聯(lián)系人信息 4)刪除指定聯(lián)系人?5)查找制定聯(lián)系人?6)修改指定聯(lián)系人?7)顯示聯(lián)系人信息(8)加載保存歷史信息
功能實(shí)現(xiàn):
1.單個(gè)聯(lián)系人信息的集中存儲(chǔ):
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置聲明
typedef struct SeqList contact;
//用戶數(shù)據(jù)
typedef struct PersonInfo
{
char name[NAME_MAX];
char gender[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}PeoInfo;
我們通過將與聯(lián)系人相關(guān)的姓名、性別、年齡、電話、地址等屬性集中在一起創(chuàng)建一個(gè)結(jié)構(gòu)體來
集中存儲(chǔ)。為方便修改數(shù)據(jù),我們使用預(yù)處理指令來定義相關(guān)量。同時(shí)為符合我們的主題,我們將struct?SeqList?重新命名為contact。
2.初始化通訊錄
//初始化通訊錄
void InitContact(contact* con)
{
assert(con);
SLInit(con);
}
初始化是通過我們之前封裝的初始化功能完成的。
3.添加通訊錄數(shù)據(jù)
int FindByName(contact* con, char* Name)
{
assert(con);
assert(Name);
int i = 0;
for (i = 0; i < con->size; i++)
{
if (0 == strcmp(con->arr[i].name, Name))
{
return i;
}
}
return EOF;
}
void AddContact(contact* con)
{
assert(con);
PeoInfo peniof ;
puts("請(qǐng)輸入要添加的聯(lián)系人姓名:");
scanf("%s", peniof.name);
int find = FindByName(con, peniof.name);
if (find >= 0)
{
puts("您要添加的聯(lián)系人已存在!");
ShowContact(con);
return;
}
puts("請(qǐng)輸入要添加的聯(lián)系人的性別:");
scanf("%s", peniof.gender);
puts("請(qǐng)輸入要添加的聯(lián)系人的年齡:");
scanf("%d", &peniof.age);
puts("請(qǐng)輸入要添加的聯(lián)系人的電話:");
scanf("%s", peniof.tel);
puts("請(qǐng)輸入要添加的聯(lián)系人的地址:");
scanf("%s", peniof.addr);
SLPushBack(con, peniof);
puts("添加成功!");
}
首先我們先創(chuàng)建存放聯(lián)系人信息的結(jié)構(gòu)體,然后在正式添加數(shù)據(jù)之前我們需要跟據(jù)我們輸入的姓名與順序表中存儲(chǔ)的arr[i].name來比較判斷通訊錄中是否已有該聯(lián)系人信息,避免重復(fù)添加,之后想結(jié)構(gòu)體內(nèi)每個(gè)成員挨個(gè)輸入,最后再用我們封裝的頭插或尾插功能插入數(shù)據(jù)(頭插或尾插的選擇看個(gè)人喜好)。
4.刪除通訊錄數(shù)據(jù)
int FindByName(contact* con, char* Name)
{
assert(con);
assert(Name);
int i = 0;
for (i = 0; i < con->size; i++)
{
if (0 == strcmp(con->arr[i].name, Name))
{
return i;
}
}
return EOF;
}
void DelContact(contact* con)
{
assert(con);
char name[NAME_MAX];
puts("請(qǐng)輸入要?jiǎng)h除的聯(lián)系人的姓名");
scanf("%s",&name);
int find = FindByName(con, name);
if (find < 0)
{
puts("您要添加的聯(lián)系人信息不存在,刪除失敗");
return;
}
SLErase(con, find);
puts("刪除成功!");
}
在正式添加數(shù)據(jù)之前我們需要跟據(jù)我們輸入的姓名與順序表中存儲(chǔ)的arr[i].name來比較判斷通訊錄中是否已有該聯(lián)系人信息,避免空刪,之后根據(jù)名字查找函數(shù),找到要?jiǎng)h除聯(lián)系人信息存儲(chǔ)位置,用封裝的SLErase函數(shù)來刪除
5.展示通訊錄數(shù)據(jù)
int FindByName(contact* con, char* Name)
{
assert(con);
assert(Name);
int i = 0;
for (i = 0; i < con->size; i++)
{
if (0 == strcmp(con->arr[i].name, Name))
{
return i;
}
}
return EOF;
}
void ShowContact(contact* con)
{
assert(con);
if(0 == con->size)
{
puts("通訊錄中未存儲(chǔ)任何信息,無法展示");
return;
}
int i = 0;
for(i = 0;i < con->size;i++)
{
printf("聯(lián)系人%d\n", i + 1);
printf("姓名:%-s\n", con->arr[i].name);
printf("性別:%-s\n", con->arr[i].gender);
printf("年齡:%-d\n", con->arr[i].age);
printf("電話:%-s\n", con->arr[i].tel);
printf("地址:%-s\n", con->arr[i].addr);
}
}
判斷通訊錄是否存放信息,如果有,挨個(gè)循環(huán)打印聯(lián)系人信息
6.查找通訊錄數(shù)據(jù)
void FindContact(contact* con)
{
assert(con);
char name[NAME_MAX];
puts("請(qǐng)輸入要查找的聯(lián)系人的姓名");
scanf("%s", &name);
int find = FindByName(con, name);
if (find < 0)
{
puts("您要查找的聯(lián)系人信息不存在,查找失??!");
return;
}
puts("查找成功!聯(lián)系人信息如下:");
printf("聯(lián)系人%d\n", find+1);
printf("姓名:%-s\n", con->arr[find].name);
printf("性別:%-s\n", con->arr[find].gender);
printf("年齡:%-d\n", con->arr[find].age);
printf("電話:%-s\n", con->arr[find].tel);
printf("地址:%-s\n", con->arr[find].addr);
}
這里筆者是根據(jù)姓名查找對(duì)應(yīng)聯(lián)系人在通訊錄中的位置(讀者可根據(jù)自己喜好設(shè)置),然后根據(jù)位置訪問打印,當(dāng)然出于用戶體驗(yàn)的考量,筆者還根據(jù)位置來顯示聯(lián)系人一二三。
7.修改通訊錄數(shù)據(jù)
void contactmenu()
{
puts("****請(qǐng)選擇您要修改的內(nèi)容****");
puts("*******1.聯(lián)系人姓名*******");
puts("*******2.聯(lián)系人性別*******");
puts("*******3.聯(lián)系人年齡*******");
puts("*******4.聯(lián)系人電話*******");
puts("*******5.聯(lián)系人地址*******");
puts("*******0.退出修改 ********");
}
void ModifyContact(contact* con)
{
assert(con);
char name[NAME_MAX];
puts("請(qǐng)輸入要修改的聯(lián)系人的姓名");
scanf("%s", &name);
int find = FindByName(con, name);
if (find < 0)
{
puts("您要添加的聯(lián)系人信息不存在,修改失敗");
return;
}
int num = 0;
while (1)
{
contactmenu();
scanf("%d", &num);
if (0 == num)
{
puts("修改結(jié)束");
break;
}
puts("修改開始!");
switch (num)
{
case 1:
scanf("%s", con->arr[find].name);
puts("修改成功");
break;
case 2:
scanf("%s", con->arr[find].gender);
puts("修改成功");
break;
case 3:
scanf("%d", &con->arr[find].age);
puts("修改成功");
break;
case 4:
scanf("%s", con->arr[find].tel);
puts("修改成功");
break;
case 5:
scanf("%s", con->arr[find].addr);
puts("修改成功");
break;
default:
puts("輸入錯(cuò)誤請(qǐng)重新輸入");
break;
}
}
}
這里修改筆者根據(jù)姓名查找通訊錄中有無該要修改的聯(lián)系人,然后筆者設(shè)計(jì)了一個(gè)修改界面,通過while循環(huán)與switch選擇結(jié)構(gòu)讓用戶能夠自己選擇修改什么信息,如果不愿意繼續(xù)修改則可以直接退出。
8.保存歷史通訊錄信息
//保存通訊錄
void SaveContact(contact* con)
{
FILE* pf = fopen("contact.txt", "wb");
if(NULL == pf)
{
perror("fopen");
exit(1);
}
int i = 0;
for(i = 0;i < con->size;i++)
{
fwrite(con->arr + i, sizeof(PeoInfo), 1, pf);
}
puts("通訊錄數(shù)據(jù)保存成功!");
fclose(pf);
pf = NULL;
}
考慮存儲(chǔ)聯(lián)系人數(shù)據(jù)可能包含各種形式的數(shù)據(jù),讀取時(shí)要考慮多種對(duì)應(yīng)格式較為麻煩,這里筆者采用二進(jìn)制寫入的的方式將輸入數(shù)據(jù)寫入到文件中保存起來。
9.加載歷史通訊錄信息
void LoadContact(contact* con)
{
assert(con);
FILE* pf = fopen("contact.txt","rb");
if(NULL == pf)
{
perror("fopen:");
exit(1);
}
PeoInfo info;
while(fread(&info,sizeof(PeoInfo),1,pf))
{
SLPushBack(con, info);
}
fclose(pf);
pf = NULL;
puts("通訊錄歷史數(shù)據(jù)加載成功!");
}
對(duì)應(yīng)保存方式,筆者這里采用二進(jìn)制讀取方式,將單個(gè)聯(lián)系人讀取存放到聯(lián)系人結(jié)構(gòu)體中,再將聯(lián)系人信息添加到結(jié)構(gòu)體中。
10.銷毀通訊錄數(shù)據(jù)
void DestroyContact(contact* con)
{
assert(con);
int num = 0;
scanf("%d",&num);
puts("是否保存歷史數(shù)據(jù)?");
puts("0.不是 or 1.是");
if (num)
{
SaveContact(con);
}
SLDestroy(con);
puts("銷毀成功!");
}
這里筆者先讓用戶決定是否保存歷史數(shù)據(jù),在進(jìn)行通過動(dòng)態(tài)順序表封裝的銷毀功能進(jìn)行銷毀。文章來源:http://www.zghlxwxcb.cn/news/detail-858702.html
用戶界面設(shè)計(jì)代碼
void menu()
{
puts("*********請(qǐng)選擇您要進(jìn)行的操作*******");
puts("*******1.加載通訊錄歷史聯(lián)系人信息****");
puts("*******2.添加通訊錄聯(lián)系人信息*******");
puts("*******3.刪除通訊錄聯(lián)系人信息*******");
puts("*******4.展示通訊錄聯(lián)系人信息*******");
puts("*******5.查找通訊錄聯(lián)系人信息*******");
puts("*******6.修改通訊錄聯(lián)系人信息*******");
puts("*************0.退出程序************");
}
int main()
{
int a = 0;
contact con;
InitContact(&con);
while (1)
{
menu();
scanf("%d", &a);
if (0 == a)
{
break;
}
switch (a)
{
case 1:
LoadContact(&con);
break;
case 2:
AddContact(&con);
break;
case 3:
DelContact(&con);
break;
case 4:
ShowContact(&con);
break;
case 5:
FindContact(&con);
break;
case 6:
ModifyContact(&con);
break;
default:
puts("輸入錯(cuò)誤請(qǐng)重新輸入");
break;
}
}
DestroyContact(&con);
return 0;
}
2.靜態(tài)順序表實(shí)現(xiàn)通訊錄
注:與動(dòng)態(tài)順序表實(shí)現(xiàn)的通訊錄相比較,靜態(tài)順序表實(shí)現(xiàn)通訊錄會(huì)出現(xiàn)通許錄滿情況,添加前應(yīng)該考慮是否通訊錄已滿,同時(shí)對(duì)于用來存放結(jié)構(gòu)體的靜態(tài)順序表,由于數(shù)組與結(jié)構(gòu)體特性,原本一些初始化會(huì)不太可行,因此筆者采用memset內(nèi)存函數(shù)來直接全部“一鍵”初始化或者銷毀。
附上源碼:文章來源地址http://www.zghlxwxcb.cn/news/detail-858702.html
//stactic_contact.h
#pragma once
#define NAME_MAX 10
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 10
//前置聲明
typedef struct SeqList contact;
//通訊錄修改菜單
void contactmenu();
//用戶數(shù)據(jù)
typedef struct PersonInfo
{
char name[NAME_MAX];
char gender[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}PeoInfo;
//根據(jù)名字查找
int FindByName(contact* con, char* Name);
//加載通訊錄信息
void LoadContact(contact* con);
//初始化通訊錄
void InitContact(contact* con);
//添加通訊錄數(shù)據(jù)
void AddContact(contact* con);
//刪除通訊錄數(shù)據(jù)
void DelContact(contact* con);
//展示通訊錄數(shù)據(jù)
void ShowContact(contact* con);
//查找通訊錄數(shù)據(jù)
void FindContact(contact* con);
//修改通訊錄數(shù)據(jù)
void ModifyContact(contact* con);
//銷毀通訊錄數(shù)據(jù)
void DestroyContact(contact* con);
//stactic_seqlist.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#define ARR_MAX 10000
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"Static_contact.h"
typedef PeoInfo SLDataType;
typedef struct SeqList
{
SLDataType arr[ARR_MAX];
int size;
}SL;
//初始化和銷毀
void SLInit(SL* ps);
void SLDestroy(SL* ps);
//申請(qǐng)空間
void SLCheckCapacity(SL* ps);
//前插和尾插
void SLPushFront(SL* ps, SLDataType data);
void SLPushBack(SL* ps, SLDataType data);
//打印數(shù)組
void SLPrint(SL ps);
//頭刪和尾刪
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);
//static_seqlist.c
//指定位置插入刪除
void SLInsert(SL* ps, int pos, SLDataType data);
void SLErase(SL* ps, int pos);
#include"Static_seqlist.h"
//初始化
void SLInit(SL* ps)
{
assert(ps);
int i = 0;
memset(ps->arr, 0, ARR_MAX * (sizeof(SLDataType)));
ps->size = 0;
}
//銷毀
void SLDestroy(SL* ps)
{
assert(ps);
memset(ps->arr, 0, ARR_MAX * (sizeof(SLDataType)));
ps->size = 0;
}
//void SLCheckCapacity(SL* ps)
//{
// assert(ps);
// if (ps->size == )
// {
// int newcapacity = ps->capacity == 0 ? INIT_CAPACITY : 2 * ps->capacity;
// SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
// if (NULL == tmp)
// {
// perror("realloc:");
// exit(1);
// }
// else
// {
// ps->arr = tmp;
// ps->capacity = newcapacity;
// }
// }
//}
//前插
void SLPushFront(SL* ps, SLDataType data)
{
assert(ps);
int i = 0;
for (i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = data;
ps->size++;
}
//后插
void SLPushBack(SL* ps, SLDataType data)
{
assert(ps);
ps->arr[ps->size++] = data;
}
//void SLPrint(SL ps)
//{
// int i = 0;
// for (i = 0; i < ps.size; i++)
// {
// printf("%d ", ps.arr[i]);
// }
//
//
// printf("\n");
//}
//前刪
void SLPopFront(SL* ps)
{
assert(ps);
int i = 0;
for (i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
//后刪
void SLPopBack(SL* ps)
{
assert(ps);
ps->size--;
}
//隨機(jī)插入
void SLInsert(SL* ps, int pos, SLDataType data)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
int i = 0;
for (i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = data;
ps->size++;
}
//隨機(jī)刪除
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
int i = 0;
for (i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
//staric_contact.c
#include"Static_seqlist.h"
void menu()
{
puts("*********請(qǐng)選擇您要進(jìn)行的操作*******");
puts("*******1.加載通訊錄歷史聯(lián)系人信息****");
puts("*******2.添加通訊錄聯(lián)系人信息*******");
puts("*******3.刪除通訊錄聯(lián)系人信息*******");
puts("*******4.展示通訊錄聯(lián)系人信息*******");
puts("*******5.查找通訊錄聯(lián)系人信息*******");
puts("*******6.修改通訊錄聯(lián)系人信息*******");
puts("*************0.退出程序************");
}
void contactmenu()
{
puts("****請(qǐng)選擇您要修改的內(nèi)容****");
puts("*******1.聯(lián)系人姓名*******");
puts("*******2.聯(lián)系人性別*******");
puts("*******3.聯(lián)系人年齡*******");
puts("*******4.聯(lián)系人電話*******");
puts("*******5.聯(lián)系人地址*******");
puts("*******0.退出修改 ********");
}
//根據(jù)名字查找
int FindByName(contact* con, char* Name)
{
assert(con);
assert(Name);
int i = 0;
for (i = 0; i < con->size; i++)
{
if (0 == strcmp(con->arr[i].name, Name))
{
return i;
}
}
return EOF;
}
//初始化通訊錄
void InitContact(contact* con)
{
assert(con);
SLInit(con);
}
//加載通訊錄信息
void LoadContact(contact* con)
{
assert(con);
FILE* pf = fopen("contact.txt", "rb");
if (NULL == pf)
{
perror("fopen:");
exit(1);
}
PeoInfo info;
while (fread(&info, sizeof(PeoInfo), 1, pf))
{
SLPushBack(con, info);
}
fclose(pf);
pf = NULL;
puts("通訊錄歷史數(shù)據(jù)加載成功!");
}
//添加通訊錄數(shù)據(jù)
void AddContact(contact* con)
{
assert(con);
PeoInfo peniof;
if((sizeof(PeoInfo) * con->size ) >= ARR_MAX)
{
puts("通訊錄可添加的名額已滿,添加失??!");
return;
}
puts("請(qǐng)輸入要添加的聯(lián)系人姓名:");
scanf("%s", peniof.name);
int find = FindByName(con, peniof.name);
if (find >= 0)
{
puts("您要添加的聯(lián)系人已存在!");
ShowContact(con);
return;
}
puts("請(qǐng)輸入要添加的聯(lián)系人的性別:");
scanf("%s", peniof.gender);
puts("請(qǐng)輸入要添加的聯(lián)系人的年齡:");
scanf("%d", &peniof.age);
puts("請(qǐng)輸入要添加的聯(lián)系人的電話:");
scanf("%s", peniof.tel);
puts("請(qǐng)輸入要添加的聯(lián)系人的地址:");
scanf("%s", peniof.addr);
SLPushBack(con, peniof);
puts("添加成功!");
}
//刪除通訊錄數(shù)據(jù)
void DelContact(contact* con)
{
assert(con);
char name[NAME_MAX];
puts("請(qǐng)輸入要?jiǎng)h除的聯(lián)系人的姓名");
scanf("%s", &name);
int find = FindByName(con, name);
if (find < 0)
{
puts("您要添加的聯(lián)系人信息不存在,刪除失敗");
return;
}
SLErase(con, find);
puts("刪除成功!");
}
//展示通訊錄數(shù)據(jù)
void ShowContact(contact* con)
{
assert(con);
if (0 == con->size)
{
puts("通訊錄中未存儲(chǔ)任何信息,無法展示");
return;
}
int i = 0;
for (i = 0; i < con->size; i++)
{
printf("聯(lián)系人%d\n", i + 1);
printf("姓名:%-s\n", con->arr[i].name);
printf("性別:%-s\n", con->arr[i].gender);
printf("年齡:%-d\n", con->arr[i].age);
printf("電話:%-s\n", con->arr[i].tel);
printf("地址:%-s\n", con->arr[i].addr);
}
}
//查找通訊錄數(shù)據(jù)
void FindContact(contact* con)
{
assert(con);
char name[NAME_MAX];
puts("請(qǐng)輸入要查找的聯(lián)系人的姓名");
scanf("%s", &name);
int find = FindByName(con, name);
if (find < 0)
{
puts("您要查找的聯(lián)系人信息不存在,查找失敗!");
return;
}
puts("查找成功!聯(lián)系人信息如下:");
printf("聯(lián)系人%d\n", find + 1);
printf("姓名:%-s\n", con->arr[find].name);
printf("性別:%-s\n", con->arr[find].gender);
printf("年齡:%-d\n", con->arr[find].age);
printf("電話:%-s\n", con->arr[find].tel);
printf("地址:%-s\n", con->arr[find].addr);
}
//修改通訊錄數(shù)據(jù)
void ModifyContact(contact* con)
{
assert(con);
char name[NAME_MAX];
puts("請(qǐng)輸入要修改的聯(lián)系人的姓名");
scanf("%s", &name);
int find = FindByName(con, name);
if (find < 0)
{
puts("您要添加的聯(lián)系人信息不存在,修改失敗");
return;
}
int num = 0;
while (1)
{
contactmenu();
scanf("%d", &num);
if (0 == num)
{
puts("修改結(jié)束");
break;
}
puts("修改開始!");
switch (num)
{
case 1:
scanf("%s", con->arr[find].name);
puts("修改成功");
break;
case 2:
scanf("%s", con->arr[find].gender);
puts("修改成功");
break;
case 3:
scanf("%d", &con->arr[find].age);
puts("修改成功");
break;
case 4:
scanf("%s", con->arr[find].tel);
puts("修改成功");
break;
case 5:
scanf("%s", con->arr[find].addr);
puts("修改成功");
break;
default:
puts("輸入錯(cuò)誤請(qǐng)重新輸入");
break;
}
}
}
//保存通訊錄
void SaveContact(contact* con)
{
FILE* pf = fopen("contact.txt", "wb");
if (NULL == pf)
{
perror("fopen");
exit(1);
}
int i = 0;
for (i = 0; i < con->size; i++)
{
fwrite(con->arr + 1, sizeof(PeoInfo), 1, pf);
}
puts("通訊錄數(shù)據(jù)保存成功!");
fclose(pf);
pf = NULL;
}
//銷毀通訊錄數(shù)據(jù)
void DestroyContact(contact* con)
{
assert(con);
SaveContact(con);
SLDestroy(con);
puts("銷毀成功!");
}
//static_function.c
#include"Static_seqlist.h"
int main()
{
int a = 0;
contact con;
InitContact(&con);
while (1)
{
menu();
scanf("%d", &a);
if (0 == a)
{
break;
}
switch (a)
{
case 1:
LoadContact(&con);
break;
case 2:
AddContact(&con);
break;
case 3:
DelContact(&con);
break;
case 4:
ShowContact(&con);
break;
case 5:
FindContact(&con);
break;
case 6:
ModifyContact(&con);
break;
default:
puts("選擇錯(cuò)誤,請(qǐng)重新選擇!");
break;
}
}
DestroyContact(&con);
return 0;
}
到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)】:順序表及其通訊錄應(yīng)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!