day22 順序表與鏈表的實現及應用(含字典功能與操作對比)
使用順序表實現查字典功能
- 支持連續查詢單詞,輸入
#quit
退出程序。 - 數據格式示例如下:
a\0 indef art one\r\n
word mean
[---buf--->] [---i--->]
[buf] &Buf[i+1]
使用 fgets
讀取一行內容,通過 strtok
分割單詞與釋義:
Fgets(buf); // 讀取一行文本到 buf
Char* word = NULL; // 定義單詞指針
Char* mean = NULL; // 定義釋義指針
Word = Strtok(buf, " "); // 以空格分割,獲取單詞
Mean = Strtok(NULL, "\r"); // 繼續分割,獲取換行前的釋義
理想運行結果:
成功從dict.txt
中逐行解析出單詞和對應釋義,并存入順序表。用戶輸入單詞后能快速查到釋義,輸入#quit
后正常退出。
seqlist.h
順序表結構定義及操作接口聲明。
#ifndef _SEQLIST_H_
#define _SEQLIST_H_// 數據類型定義:存儲單詞和釋義
typedef struct person {char word[50]; // 單詞字段,最多50字符char mean[512]; // 釋義字段,最多512字符
} DATATYPE;// 順序表結構體
typedef struct list {DATATYPE *head; // 指向動態數組的指針int tlen; // 數組總容量int clen; // 當前有效元素個數
} SeqList;// 函數聲明
SeqList *CreateSeqList(int len); // 創建順序表
int ShowSeqList(SeqList *list); // 顯示全部數據
int InsertTailSeqList(SeqList *list, DATATYPE *data); // 尾部插入
int IsFullSeqList(SeqList *list); // 判斷是否滿
int IsEmptySeqList(SeqList *list); // 判斷是否空
int InsertPosSeqList(SeqList *list, DATATYPE *data, int pos); // 按位置插入
int FindSeqList(SeqList *list, char *name); // 查找指定單詞
int ModifySeqList(SeqList *list, char *old, DATATYPE *newdata); // 修改數據
int ClearSeqList(SeqList *list); // 清空順序表
int DestroySeqList(SeqList *list); // 銷毀順序表
int DeleteSeqList(SeqList *list, char *name); // 刪除指定元素
int GetSizeSeqList(SeqList *list); // 獲取有效元素個數
DATATYPE *GetItemSeqlist(SeqList *list, int pos); // 獲取指定位置元素
int ShowSeqListOne(SeqList *list, int inx); // 顯示單條記錄#endif
seqlist.c
順序表的實現文件,包含所有接口函數的具體實現。
#include "seqlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>/*** 創建順序表* @param len 初始容量* @return 成功返回指針,失敗返回 NULL*/
SeqList *CreateSeqList(int len)
{SeqList *sl = malloc(sizeof(SeqList));if (NULL == sl){perror("CreateSeqList malloc error\n");return NULL;}sl->head = malloc(sizeof(DATATYPE) * len);if (NULL == sl->head){perror("CreateSeqList malloc2 error\n");return NULL;}sl->tlen = len;sl->clen = 0;return sl;
}/*** 尾部插入數據* @param list 目標順序表* @param data 待插入數據* @return 0 成功,1 失敗(已滿)*/
int InsertTailSeqList(SeqList *list, DATATYPE *data)
{if (IsFullSeqList(list)){printf("seqlist is full\n");return 1;}memcpy(&list->head[list->clen], data, sizeof(DATATYPE)); // 安全拷貝list->clen++;return 0;
}/*** 判斷順序表是否已滿* @param list 順序表* @return 1 滿,0 未滿*/
int IsFullSeqList(SeqList *list)
{return list->clen == list->tlen;
}/*** 顯示整個順序表內容* @param list 順序表* @return 0*/
int ShowSeqList(SeqList *list)
{int len = GetSizeSeqList(list);for (int i = 0; i < len; ++i){printf("word:%s mean:%s\n", list->head[i].word, list->head[i].mean);}return 0;
}/*** 獲取順序表有效元素個數* @param list 順序表* @return 元素個數*/
int GetSizeSeqList(SeqList *list)
{return list->clen;
}/*** 判斷順序表是否為空* @param list 順序表* @return 1 空,0 非空*/
int IsEmptySeqList(SeqList *list)
{return 0 == list->clen;
}/*** 在指定位置插入數據* @param list 順序表* @param data 待插入數據* @param pos 插入位置(0-based)* @return 0 成功,1 失敗*/
int InsertPosSeqList(SeqList *list, DATATYPE *data, int pos)
{if (IsFullSeqList(list)){printf("seqlist is full\n");return 1;}int len = GetSizeSeqList(list);if (pos < 0 || pos > len){printf("pos is incorrect\n");return 1;}// 從后往前移動元素,騰出位置for (int i = list->clen; i > pos; i--){memcpy(&list->head[i], &list->head[i - 1], sizeof(DATATYPE));}memcpy(&list->head[pos], data, sizeof(DATATYPE));list->clen++;return 0;
}/*** 查找指定單詞的位置* @param list 順序表* @param name 要查找的單詞* @return 找到返回索引,否則返回 -1*/
int FindSeqList(SeqList *list, char *name)
{int len = GetSizeSeqList(list);for (int i = 0; i < len; i++){if (0 == strcmp(list->head[i].word, name)){return i;}}return -1;
}/*** 獲取指定位置的元素* @param list 順序表* @param pos 位置* @return 指向該位置元素的指針,非法位置返回 NULL*/
DATATYPE* GetItemSeqlist(SeqList* list, int pos)
{int len = GetSizeSeqList(list);if (pos < 0 || pos >= len) // 修正:pos 應 < len{printf("pos is incorrect\n");return NULL;}return &list->head[pos];
}/*** 修改指定單詞的數據* @param list 順序表* @param oldname 原單詞* @param newdata 新數據* @return 0 成功,1 失敗(未找到)*/
int ModifySeqList(SeqList *list, char *oldname, DATATYPE *newdata)
{int ret = FindSeqList(list, oldname);if (-1 == ret){printf("modify failure...\n");return 1;}memcpy(&list->head[ret], newdata, sizeof(DATATYPE));return 0;
}/*** 清空順序表(不清除內存)* @param list 順序表* @return 0*/
int ClearSeqList(SeqList *list)
{list->clen = 0;return 0;
}/*** 銷毀順序表并釋放內存* @param list 順序表* @return 0*/
int DestroySeqList(SeqList *list)
{free(list->head);free(list);return 0;
}/*** 顯示指定位置的一條記錄* @param list 順序表* @param inx 索引* @return 0 成功,1 失敗*/
int ShowSeqListOne(SeqList *list, int inx)
{int len = GetSizeSeqList(list);if (inx < 0 || inx >= len) // 修正:inx 應 < len{printf("pos is incorrect\n");return 1;}printf("word:%s mean:%s\n", list->head[inx].word, list->head[inx].mean);return 0;
}
main.c
主程序:加載字典文件,支持交互式查詢。
#include <stdio.h>
#include <stdlib.h>
#include "seqlist.h"
#include <string.h>int main(int argc, char** argv)
{FILE* fp = fopen("/home/linux/dict.txt", "r");if (NULL == fp){perror("fopen");return 1;}SeqList* sl = CreateSeqList(20000);if (NULL == sl){fprintf(stderr, "CreateSeqList error\n");return 1;}// 從文件逐行讀取并構建字典while (1){DATATYPE data;char *word = NULL;char *mean = NULL;char linebuf[1024] = {0};if (NULL == fgets(linebuf, sizeof(linebuf), fp)){break; // 文件讀取結束}word = strtok(linebuf, " ");mean = strtok(NULL, "\r"); // 去除回車符strcpy(data.word, word);strcpy(data.mean, mean);InsertTailSeqList(sl, &data);}// 交互式查詢while (1){char want_word[50] = {0};printf("input word: ");fgets(want_word, sizeof(want_word), stdin); // 輸入如 "book\n"want_word[strlen(want_word) - 1] = '\0'; // 去掉末尾換行符if (0 == strcmp(want_word, "#quit")){break;}int ret = FindSeqList(sl, want_word);if (-1 == ret){printf("cant find %s\n", want_word);}else{ShowSeqListOne(sl, ret); // 輸出釋義}}fclose(fp);DestroySeqList(sl); // 釋放資源return 0;
}
理想運行結果:
input word: hello word:hello mean:你好 input word: #quit
// strlen()-1 的作用說明:
012345 6
Hello\n\0
1234 567
說明:
fgets
會保留換行符\n
,因此用strlen(want_word)-1
可將其替換為\0
,實現去換行。
線性表的鏈式存儲
- 解決順序表在插入、刪除操作中效率低及靜態容量限制的問題。
- 實現動態存儲,按需分配內存。
特點
- 鏈式存儲使用任意內存空間存儲數據元素,不要求連續。
- 每個節點包含兩部分:
- 數據域:存儲實際數據。
- 指針域:指向下一個節點地址。
- 結點(Node)由數據域和指針域共同構成。
LinkList|
---------
Head Clen| -> 數據域Data|next -> Data|next -> Data|next-> Data|next
Typedf Struct node
{Name;Sex;Age;Score;
}DATATYPE;
->
Struct node
{DATATYPE data;Struct node*next;
};
單鏈表的 C 語言描述
typedef struct person {char name[32];char sex;int age;int score;
} DATATYPE;
typedef struct node {DATATYPE data; // 數據域struct node *next; // 指針域,指向下一個節點
} LinkNode;
typedef struct list {LinkNode *head; // 指向第一個節點int clen; // 當前有效節點數量
} LinkList;
linklist.h
鏈表接口聲明。
#ifndef _LINKLIST_H_
#define _LINKLIST_H_typedef struct person {char name[32];char sex;int age;int score;
} DATATYPE;typedef struct node {DATATYPE data;struct node *next;
} LinkNode;typedef struct list {LinkNode *head;int clen;
} LinkList;// 接口函數聲明
LinkList *CreateLinkList();
int InsertHeadLinkList(LinkList *list, DATATYPE *data);
int InsertTailLinkList(LinkList *list, DATATYPE *data);
int InsertPosLinkList(LinkList *list, DATATYPE *data, int pos);
int ShowLinkList(LinkList *list);
LinkNode *FindLinkList(LinkList *list, char *name);
int DeleteLinkList(LinkList *list, char *name);
int ModifyLinkList(LinkList *list, char *name, DATATYPE *data);
int DestroyLinkList(LinkList *list);
int IsEmptyLinkList(LinkList *list);
int GetSizeLinkList(LinkList *list);#endif // _LINKLIST_H_
linklist.c
鏈表功能實現。
#include "linklist.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>/*** 創建空鏈表* @return 新鏈表指針,失敗返回 NULL*/
LinkList *CreateLinkList()
{LinkList* ll = malloc(sizeof(LinkList));if (NULL == ll){perror("CreateLinkList malloc");return NULL;}ll->head = NULL;ll->clen = 0;return ll;
}/*** 頭插法插入節點* @param list 鏈表* @param data 數據* @return 0 成功,1 失敗*/
int InsertHeadLinkList(LinkList *list, DATATYPE *data)
{LinkNode* newnode = malloc(sizeof(LinkNode));if (NULL == newnode){perror("InsertHeadLinkList malloc");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = NULL;if (!IsEmptyLinkList(list)){newnode->next = list->head; // 新節點指向原頭節點}list->head = newnode; // 更新頭指針list->clen++;return 0;
}/*** 判斷鏈表是否為空* @param list 鏈表* @return 1 空,0 非空*/
int IsEmptyLinkList(LinkList *list)
{return 0 == list->clen;
}/*** 顯示鏈表所有節點* @param list 鏈表* @return 0*/
int ShowLinkList(LinkList *list)
{LinkNode* tmp = list->head;while (tmp){printf("name:%s sex:%c age:%d score:%d\n",tmp->data.name, tmp->data.sex, tmp->data.age, tmp->data.score);tmp = tmp->next;}return 0;
}
/*** 尾插法插入節點* @param list 鏈表* @param data 數據* @return 0 成功,-1 失敗*/
int InsertTailLinkList(LinkList *list, DATATYPE *data)
{if (IsEmptyLinkList(list)){return InsertHeadLinkList(list, data);}else{LinkNode* tmp = list->head;while (tmp->next) // 找到最后一個節點{tmp = tmp->next;}LinkNode* newnode = malloc(sizeof(LinkNode));if (NULL == newnode){perror("InsertTailLinkList malloc");return -1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = NULL;tmp->next = newnode; // 連接新節點}list->clen++;return 0;
}
/*** 按位置插入節點* @param list 鏈表* @param data 數據* @param pos 插入位置(0-based)* @return 0 成功,1 失敗*/
int InsertPosLinkList(LinkList *list, DATATYPE *data, int pos)
{int len = GetSizeLinkList(list);if (pos < 0 || pos > len){fprintf(stderr, "InsertPosLinkList pos error\n");return 1;}if (0 == pos){return InsertHeadLinkList(list, data);}else if (pos == len){return InsertTailLinkList(list, data);}else{LinkNode* tmp = list->head;int off = pos - 1;while (off--){tmp = tmp->next;}LinkNode* newnode = malloc(sizeof(LinkNode));if (NULL == newnode){perror("InsertPosLinkList malloc ");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = tmp->next;tmp->next = newnode;}list->clen++;return 0;
}int GetSizeLinkList(LinkList *list)
{return list->clen;
}
/*** 查找指定姓名的節點* @param list 鏈表* @param name 姓名* @return 找到返回節點指針,否則 NULL*/
LinkNode *FindLinkList(LinkList *list, char *name)
{LinkNode *tmp = list->head;while (tmp){if (0 == strcmp(tmp->data.name, name)){return tmp;}tmp = tmp->next;}return NULL;
}
/*** 刪除指定姓名的節點* @param list 鏈表* @param name 姓名* @return 0 成功,1 失敗*/
int DeleteLinkList(LinkList *list, char *name)
{if (IsEmptyLinkList(list)){fprintf(stderr, "DeleteLinkList empty list\n");return 1;}LinkNode *tmp = list->head;if (0 == strcmp(tmp->data.name, name)){list->head = list->head->next;free(tmp);list->clen--;}else{while (tmp->next){if (0 == strcmp(tmp->next->data.name, name)){LinkNode* del = tmp->next;tmp->next = tmp->next->next;free(del);list->clen--;break;}tmp = tmp->next;}}return 0;
}/*** 修改指定節點數據* @param list 鏈表* @param name 原名字* @param data 新數據* @return 0 成功,1 失敗*/
int ModifyLinkList(LinkList *list, char *name, DATATYPE *data)
{LinkNode *tmp = FindLinkList(list, name);if (NULL == tmp){printf("modify error\n");return 1;}memcpy(&tmp->data, data, sizeof(DATATYPE));return 0;
}/*** 銷毀整個鏈表* @param list 鏈表* @return 0*/
int DestroyLinkList(LinkList *list)
{LinkNode* tmp = list->head;while (tmp){list->head = list->head->next;free(tmp);tmp = list->head;}free(list);return 0;
}
main.c
測試鏈表功能。
#include <stdio.h>
#include "linklist.h"int main(int argc, char **argv)
{LinkList* ll = CreateLinkList();DATATYPE data[] ={{"zhangsan", 'm', 20, 80},{"lisi", 'f', 23, 84},{"wangmaizi", 'f', 32, 90},{"guanerge", 'm', 50, 91},{"liubei", 'm', 51, 92},};InsertHeadLinkList(ll, &data[0]);InsertHeadLinkList(ll, &data[1]);InsertHeadLinkList(ll, &data[2]);ShowLinkList(ll);printf("----------insert tail----------\n");InsertTailLinkList(ll, &data[3]);ShowLinkList(ll);printf("----------insert pos----------\n");InsertPosLinkList(ll, &data[4], 1);ShowLinkList(ll);char want_name[] = "zhangsan";LinkNode *tmp = FindLinkList(ll, want_name);if (NULL == tmp){printf("can't find %s\n", want_name);}else{printf("name:%s score:%d\n", tmp->data.name, tmp->data.score);}printf("----------del----------\n");DeleteLinkList(ll, "zhangsan");ShowLinkList(ll);printf("----------modify----------\n");ModifyLinkList(ll, "wangmaizi", &data[4]);ShowLinkList(ll);DestroyLinkList(ll);return 0;
}
理想運行結果:
- 正確顯示插入、刪除、修改后的鏈表狀態。
- 查詢與修改功能正常。
- 內存無泄漏(可用
valgrind
驗證)。
順序表和鏈表優缺點對比
對比維度 | 順序表 | 鏈表 |
---|---|---|
存儲方式 | 連續內存空間 | 不連續,通過指針連接 |
時間性能 - 查找 | O(1)(支持隨機訪問) | O(n)(需遍歷) |
時間性能 - 插入/刪除 | O(n)(需移動元素) | O(1)(已知位置時) |
空間性能 | 需預分配,可能浪費或溢出 | 動態分配,靈活,但每個節點多指針開銷 |
優點 | 訪問快,緩存友好 | 插刪高效,動態擴展 |
缺點 | 擴展困難,插入刪除慢 | 無法隨機訪問,額外指針占用內存 |
gdb 調試
常用命令:
gdb all
(gdb) p data // 打印變量 data
(gdb) p *data // 打印 data 指向的內容
(gdb) p list->clen // 打印 list 的 clen 字段
(gdb) n // 單步執行(不進入函數)
(gdb) s // 單步執行(進入函數)
(gdb) p newnode // 打印 newnode 指針
(gdb) p *newnode // 打印 newnode 指向的節點
(gdb) q // 退出 gdb
valgrind 內存泄露檢測
valgrind ./all
檢測程序是否存在內存泄漏、非法訪問等問題。
回顧
- 單向鏈表:內存空間不連續,通過指針鏈接。
- 優勢:
- 插入、刪除時間復雜度 O(1)(已知位置)
- 動態存儲,無需預分配
- 劣勢:
- 不支持隨機訪問,查找為 O(n)
- 結點結構:數據域 + 指針域(順序表僅數據域)
- 手撕代碼重點:頭插、尾插、按位插、刪除、查找、修改
作業
鏈表的逆序
本部分實現單向鏈表的逆序操作,通過調整節點指針方向,將鏈表從頭到尾的連接順序反轉。
接口聲明(linklist.h)
LinkNode* ReverseLinkList(LinkList *list);
功能:將指定鏈表進行逆序,并返回新的頭節點。
參數:list
- 指向鏈表結構的指針。
返回值:逆序后的頭節點指針;若鏈表為空則返回NULL
。
實現代碼(linklist.c)
LinkNode* ReverseLinkList(LinkList *list)
{// 若鏈表為空或頭節點為空,無法逆序,直接返回 NULLif(NULL == list || NULL == list->head){return NULL;}LinkNode *before = NULL; // 當前節點的前一個節點(初始為 NULL)LinkNode *current = list->head; // 當前處理的節點LinkNode *next; // 臨時保存當前節點的下一個節點// 遍歷鏈表,逐個反轉指針while(NULL != current){next = current->next; // 保存下一個節點current->next = before; // 將當前節點指向前一個節點(反轉指針)before = current; // 更新前一個節點為當前節點current = next; // 移動到下一個節點}// 循環結束后,before 指向原鏈表最后一個節點,即新鏈表的頭節點list->head = before; // 更新鏈表頭指針return before; // 返回新的頭節點
}
? 代碼邏輯說明:使用三指針法(
before
,current
,next
)實現原地反轉,時間復雜度 O(n),空間復雜度 O(1)。
🧪 理想運行結果:原鏈表順序被完全反轉,list->head
指向原尾節點,遍歷輸出順序與原始插入順序相反。
測試代碼(main.c)
#include <stdio.h>
#include "linklist.h"int main(int argc, char **argv)
{// 創建一個空鏈表LinkList *ll = CreateLinkList();// 定義測試數據數組,包含5個人員信息DATATYPE data[] = {{"zhangsan", 'm', 20, 80},{"lisi", 'f', 23, 84},{"wangmaizi", 'f', 32, 90},{"guanerge", 'm', 50, 91},{"liubei", 'm', 51, 92},};// 使用頭插法依次插入數據(后插入的在鏈表前端)InsertHeadLinkList(ll, &data[0]);InsertHeadLinkList(ll, &data[1]);InsertHeadLinkList(ll, &data[2]);InsertHeadLinkList(ll, &data[3]);InsertHeadLinkList(ll, &data[4]);// 輸出頭插后的鏈表(順序應為:liubei → guanerge → wangmaizi → lisi → zhangsan)printf("----------------insert head----------------\n");ShowLinkList(ll);// 執行鏈表逆序操作ReverseLinkList(ll);// 輸出逆序后的鏈表(順序應為:zhangsan → lisi → wangmaizi → guanerge → liubei)printf("----------------reverse----------------\n");ShowLinkList(ll);// 釋放鏈表內存DestroyLinkList(ll);return 0;
}
? 預期輸出示例:
----------------insert head----------------
name: liubei, gender: m, age: 51, score: 92
name: guanerge, gender: m, age: 50, score: 91
name: wangmaizi, gender: f, age: 32, score: 90
name: lisi, gender: f, age: 23, score: 84
name: zhangsan, gender: m, age: 20, score: 80
----------------reverse----------------
name: zhangsan, gender: m, age: 20, score: 80
name: lisi, gender: f, age: 23, score: 84
name: wangmaizi, gender: f, age: 32, score: 90
name: guanerge, gender: m, age: 50, score: 91
name: liubei, gender: m, age: 51, score: 92
💡 說明:由于采用頭插法,原始插入順序為 zhangsan → liubei,但鏈表中實際順序為反向。逆序后恢復為插入順序。
使用鏈表實現字典查詢
本部分使用單向鏈表構建一個簡易字典系統,支持單詞查找、插入、修改、刪除等基本操作,數據從文件加載。
頭文件定義(linklist.h)
#ifndef _LINKLIST_H_
#define _LINKLIST_H_// 定義字典條目數據類型
typedef struct person
{char word[50]; // 存儲單詞(關鍵詞)char mean[512]; // 存儲釋義(解釋內容)
} DATATYPE;// 鏈表節點結構
typedef struct node
{DATATYPE data; // 節點存儲的數據(一個字典條目)struct node *next; // 指向下一個節點的指針
} LinkNode;// 鏈表管理結構
typedef struct list
{LinkNode *head; // 指向鏈表第一個節點int clen; // 當前鏈表中節點數量
} LinkList;// 函數聲明
LinkList *CreateLinkList(); // 創建空鏈表
int ShowLinkList(LinkList *list); // 顯示所有條目
int IsEmptyLinkList(LinkList *list); // 判斷鏈表是否為空
int InsertTailLinkList(LinkList *list, DATATYPE *data); // 尾部插入新條目
int InsertPosLinkList(LinkList *list, DATATYPE *data, int pos); // 指定位置插入
LinkNode *FindLinkList(LinkList *list, char *word); // 根據單詞查找節點
int ModifyLinkList(LinkList *list, char *name, DATATYPE *data); // 修改指定單詞條目
int DestroyLinkList(LinkList *list); // 銷毀鏈表并釋放內存
int DeleteLinkList(LinkList *list, char *word); // 刪除指定單詞條目(未在.c中實現?)
int GetSizeLinkList(LinkList *list); // 獲取鏈表長度
DATATYPE *GetItemLinklist(LinkList *list, int pos); // 獲取指定位置條目#endif
? 功能說明:提供完整的字典操作接口,包括增刪改查、遍歷、定位等功能。
實現代碼(linklist.c)
#include "linklist.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 創建新鏈表,初始化頭指針和長度
LinkList *CreateLinkList()
{LinkList *ll = malloc(sizeof(LinkList));if (NULL == ll){perror("CreateLinkList malloc"); // 分配失敗時打印錯誤信息return NULL;}ll->head = NULL;ll->clen = 0;return ll;
}// 尾部插入新節點
int InsertTailLinkList(LinkList *list, DATATYPE *data)
{LinkNode *newnode = malloc(sizeof(LinkNode));if (NULL == newnode){perror("InsertTailLinkList malloc");return 1;}newnode->next = NULL;newnode->data = *data; // 復制數據if (IsEmptyLinkList(list)){list->head = newnode; // 首次插入}else{LinkNode *tmp = list->head;while (tmp->next) // 找到最后一個節點{tmp = tmp->next;}tmp->next = newnode; // 插入到末尾}list->clen++;return 0;
}// 判斷鏈表是否為空
int IsEmptyLinkList(LinkList *list)
{return 0 == list->clen;
}// 打印鏈表中所有條目
int ShowLinkList(LinkList *list)
{LinkNode *tmp = list->head;while (tmp){printf("word:%s mean:%s\n", tmp->data.word, tmp->data.mean);tmp = tmp->next;}return 0;
}// 獲取鏈表當前長度
int GetSizeLinkList(LinkList *list)
{return list->clen;
}// 在指定位置插入新條目(pos 從 0 開始)
int InsertPosLinkList(LinkList *list, DATATYPE *data, int pos)
{int len = GetSizeLinkList(list);if (pos < 0 || pos > len){fprintf(stderr, "InsertPosLinkList pos error\n");return -1;}if (pos == len){return InsertTailLinkList(list, data); // 位置在末尾,調用尾插}else{LinkNode *tmp = list->head;int off = pos - 1;while (off--) // 移動到插入位置的前一個節點{tmp = tmp->next;}LinkNode *newnode = malloc(sizeof(LinkNode));if (NULL == newnode){perror("InsertPosLinkList malloc");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE)); // 復制數據newnode->next = NULL;newnode->next = tmp->next; // 新節點指向原下一個節點tmp->next = newnode; // 前驅節點指向新節點}list->clen++;return 0;
}// 根據單詞查找對應節點
LinkNode *FindLinkList(LinkList *list, char *word)
{LinkNode *tmp = list->head;while (tmp){if (0 == strcmp(tmp->data.word, word)) // 字符串匹配成功{return tmp;}tmp = tmp->next;}return NULL; // 未找到返回 NULL
}// 獲取指定位置的條目數據指針
DATATYPE *GetItemLinklist(LinkList *list, int pos)
{int len = GetSizeLinkList(list);if (pos < 0 || pos >= len){printf("pos is incorrect\n");return NULL;}LinkNode *tmp = list->head;for (int i = 0; i < pos; ++i){tmp = tmp->next;}return &tmp->data; // 返回該位置數據的地址
}// 修改指定單詞的條目內容
int ModifyLinkList(LinkList *list, char *name, DATATYPE *data)
{LinkNode *tmp = FindLinkList(list, name);if (NULL == tmp){printf("modify error\n");return 1;}memcpy(&tmp->data, data, sizeof(DATATYPE)); // 覆蓋原有數據return 0;
}// 銷毀整個鏈表,釋放所有內存
int DestroyLinkList(LinkList *list)
{LinkNode *tmp = list->head;while (tmp){list->head = list->head->next; // 先保存下一個節點free(tmp); // 釋放當前節點tmp = list->head;}free(list); // 釋放鏈表結構體本身return 0;
}
? 關鍵點說明:
FindLinkList
使用strcmp
精確匹配單詞。InsertPosLinkList
支持任意合法位置插入。DestroyLinkList
安全釋放所有節點,防止內存泄漏。- 所有函數均包含錯誤處理機制。
主程序測試(main.c)
#include <stdio.h>
#include <string.h>
#include "linklist.h"int main(int argc, char **argv)
{// 打開字典文件(假設路徑正確且格式為:單詞 釋義)FILE *fp = fopen("/home/linux/dict.txt", "r");if (NULL == fp){perror("fopen");return 1;}// 創建鏈表用于存儲字典條目LinkList *ll = CreateLinkList();if (NULL == ll){fprintf(stderr, "CreateLinkList error\n");return 1;}// 逐行讀取文件內容并解析while (1){DATATYPE data;char *word = NULL;char *mean = NULL;char linebuf[1024] = {0}; // 緩沖區存儲每行文本if (NULL == fgets(linebuf, sizeof(linebuf), fp)){break; // 文件讀取結束}word = strtok(linebuf, " "); // 提取第一個單詞(以空格分隔)mean = strtok(NULL, "\r"); // 提取剩余部分作為釋義(去除回車)strcpy(data.word, word); // 復制單詞if (NULL != mean){while (*mean == ' ' || *mean == '\t') // 跳過釋義前的空白字符{mean++;}strcpy(data.mean, mean); // 復制有效釋義}InsertTailLinkList(ll, &data); // 尾插法加入鏈表}// 進入交互查詢模式while (1){char want_word[50] = {0};printf("input word: ");fgets(want_word, sizeof(want_word), stdin); // 讀取用戶輸入want_word[strlen(want_word) - 1] = '\0'; // 去除換行符if (0 == strcmp(want_word, "#quit")) // 輸入 #quit 退出{break;}LinkNode *ret = FindLinkList(ll, want_word); // 查找單詞if (NULL == ret){printf("can't find %s\n", want_word); // 未找到提示}else{printf("word:%s mean:%s\n", ret->data.word, ret->data.mean); // 顯示釋義}}fclose(fp); // 關閉文件DestroyLinkList(ll); // 釋放鏈表內存return 0;
}
? 理想運行流程:
- 成功打開
dict.txt
文件;- 每行解析出
word
和mean
,如:→hello world this is a test
word="hello"
,mean="world this is a test"
- 用戶輸入單詞后,系統查找并輸出釋義;
- 輸入
#quit
結束程序;- 程序正常釋放資源,無內存泄漏。
📄 dict.txt 示例內容:
hello world this is a greeting
apple a fruit that grows on trees
cat a small domesticated carnivorous mammal
#quit
🖥? 交互示例:
input word: hello
word:hello mean:world this is a greeting
input word: cat
word:cat mean:a small domesticated carnivorous mammal
input word: dog
can't find dog
input word: #quit
💡 注意事項:
- 文件路徑
/home/linux/dict.txt
需存在且可讀;- 單詞與釋義之間用單個空格分隔;
- 支持忽略釋義前多余的空格或制表符;
- 不區分大小寫?——當前實現為嚴格匹配(區分大小寫)。
? 總結:兩道題分別實現了鏈表的逆序操作和字典應用,涵蓋了鏈表的基本操作(創建、插入、查找、遍歷、銷毀)以及實際應用場景,代碼結構清晰,具備基本錯誤處理能力。