Day22 順序表與鏈表的實現及應用(含字典功能與操作對比)

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;
}

? 理想運行流程

  1. 成功打開 dict.txt 文件;
  2. 每行解析出 wordmean,如:
    hello world this is a test
    
    word="hello", mean="world this is a test"
  3. 用戶輸入單詞后,系統查找并輸出釋義;
  4. 輸入 #quit 結束程序;
  5. 程序正常釋放資源,無內存泄漏。

📄 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 需存在且可讀;
  • 單詞與釋義之間用單個空格分隔;
  • 支持忽略釋義前多余的空格或制表符;
  • 不區分大小寫?——當前實現為嚴格匹配(區分大小寫)。

? 總結:兩道題分別實現了鏈表的逆序操作字典應用,涵蓋了鏈表的基本操作(創建、插入、查找、遍歷、銷毀)以及實際應用場景,代碼結構清晰,具備基本錯誤處理能力。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/96520.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/96520.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/96520.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

51單片機與stm32單片機,先學習哪一個?

糾結 51 單片機和 STM32 該先學哪個&#xff0c;就像剛學開車的人在自動擋和手動擋之間打轉。有人一上來就愛開自動擋&#xff0c;踩著油門就能跑&#xff0c;不用琢磨換擋踩離合的門道&#xff1b;有人偏要從手動擋練起&#xff0c;哪怕起步時熄十幾次火&#xff0c;也得搞明白…

DS 0 | 數據結構學習:前言

數據結構是CS最基礎、最重要的課程之一在學習數據結構時&#xff0c;通常來講&#xff0c;學生遇到的難點不在于對數據結構的理解&#xff0c;而在于如何寫程序。即編寫特定的程序&#xff0c;來實現這些數據結構&#xff0c;特別是如何按照面向對象思想將一個個數據結構設計成…

JVM-(8)JVM啟動的常用命令以及參數

JVM啟動的常用命令以及參數 在上文 JVM 堆內存邏輯分區 中已經使用過一些 jvm 啟動命令&#xff0c;本文著重講述JVM啟動命令用法以及一些常用的參數 一. 基本命令格式 java [options] classname [args...] java [options] -jar filename.jar [args...]① [options] - 命令行…

GO學習記錄七——上傳/下載文件功能,添加啟動運行工具

本來計劃是學習Docker部署的&#xff0c;研究了一天沒搞出來&#xff0c;得出結論是需要翻墻&#xff0c;懶得弄了&#xff0c;暫時放置。 一、以下是&#xff0c;上傳/下載代碼&#xff0c;和之前是重復的&#xff0c;只多添加了&#xff0c;上傳/下載功能。 測試目錄為工程根…

SQL中對視圖的操作命令匯總

以下是基于搜索結果整理的SQL視圖操作命令匯總&#xff0c;按功能分類說明&#xff1a; 一、創建視圖 使用 CREATE VIEW 語句定義視圖&#xff0c;需指定視圖名稱和基礎查詢表達式&#xff1a; CREATE VIEW view_name AS SELECT column1, column2, ... FROM table_name WHER…

【Spring Cloud 微服務】2.守護神網關Gateway

目錄 1.API網關的作用 2.Spring Cloud Gateway 是什么&#xff1f; 3.核心由來與背景 1. 微服務架構的挑戰&#xff1a; 2. API 網關模式的興起&#xff1a; 3. Zuul 的局限性&#xff1a; 4. Spring Cloud Gateway 的誕生&#xff1a; 4.核心特征&#xff1a; 5.核心概…

解讀商業智能BI,數據倉庫中的元數據

之前的文章討論過數據分析、數據治理、數據倉庫等等&#xff0c;即使是非業內人員從字面意思&#xff0c;也是可以了解一二的&#xff0c;但是&#xff0c;很多人對于元數據可能就比較陌生了。那么&#xff0c;今天我們就來聊一聊元數據管理。數據倉庫要說元數據&#xff0c;那…

3 種無誤的方式刪除 Itel 手機上的短信

如果你希望釋放存儲空間、保護隱私&#xff0c;或者準備出售或轉讓手機&#xff0c;刪除 Itel 手機上的短信是一個實用的步驟。無論是收件箱中充斥著垃圾短信、過時的對話還是敏感內容&#xff0c;刪除不需要的短信可以讓你的消息體驗更加干凈和安全。本文將向你介紹 3 種簡單且…

【學習筆記】網絡安全專用產品類別與參考標準

一、基本標準 1.1 關鍵設備 網絡關鍵設備認證依據的強制標準為 GB 40050-2021。 1.2 專用產品 網絡安全專用產品認證依據的強制標準為 GB 42250-2022。 二、數據備份與恢復產品標準 相關標準&#xff1a; GB/T 29765-2021《信息安全技術 數據備份與恢復產品技術要求與測試評…

Pytho“張量”(Tensor)和 Java的“向量”(Vector)區別和聯系

在Python和Java中&#xff0c;“張量”&#xff08;Tensor&#xff09;和“向量”&#xff08;Vector&#xff09;是兩個不同語境下的概念&#xff0c;它們的設計目標、功能和應用場景存在顯著差異&#xff0c;但也存在一定的共性。以下從區別和聯系兩方面詳細說明&#xff1a;…

Ubuntu部署K8S集群

Ubuntu部署K8S集群 本例以三臺Ubuntu24.04為例,1master節點2worker節點 環境準備 修改hostname,三臺服務器分別執行 hostnamectl set-hostname k8s-master01hostnamectl set-hostname k8s-worker01hostnamectl set-hostname k8s-worker02 配置靜態ip(不同系統修改方法略微差…

openEuler系統安裝Ascend Docker Runtime的方法

在openEuler系統中使用NPU前一定要安裝Ascend Docker Runtime,也是在安裝CANN和mis-tei前的必備工作。 使用容器化支持、整卡調度、靜態vNPU調度、動態vNPU調度、斷點續訓、彈性訓練、推理卡故障恢復或推理卡故障重調度的用戶,必須安裝Ascend Docker Runtime。 下面是具體的安…

控制對文件的訪問:Linux 文件系統權限管理總結

在 Linux 系統中&#xff0c;文件權限是保障系統安全和數據完整性的核心機制。紅帽企業 Linux 9.0通過一套靈活且精細的權限控制體系&#xff0c;讓用戶能夠精確管理文件和目錄的訪問范圍。本章將系統梳理 Linux 文件系統權限的核心概念、管理方法及高級應用&#xff0c;為系統…

ansible中roles角色是什么意思?

文章目錄一、介紹二、Ansible Roles目錄編排三、創建role四、playbook調用角色五、roles中tags使用免費個人運維知識庫&#xff0c;歡迎您的訂閱&#xff1a;literator_ray.flowus.cn 一、介紹 角色是ansible自1.2版本引入的新特性&#xff0c;用于層次性、結構化地組織playbo…

pytorch 網絡可視化

1.torchsummary在 Anaconda prompt 中進入自己的 pytorch 環境&#xff0c;安裝依賴包。 bash pip install torchsummary 2.tensorboardX 3. graphviz torchviz 4.Jupyter Notebook tensorwatch 5.netron 6.hiddenlayer 7.PlotNeuralNet

可以一鍵生成PPT的AI PPT工具(最新整理)

在當今快節奏的職場環境中&#xff0c;高效制作專業PPT已成為一項必備技能。傳統PPT制作流程耗時費力&#xff0c;從構思大綱、搜集資料、撰寫內容到設計排版&#xff0c;往往需要數小時甚至數天時間。AI生成PPT工具的興起徹底改變了這一局面&#xff0c;讓職場人士能夠專注于內…

數倉核心概念闡述

數倉核心概念闡述一、數據倉庫建模模型二、數據處理架構三、流批處理架構演進**為什么需要流批融合&#xff1f;****1. Lambda 架構&#xff08;雙引擎護航&#xff09;****2. Kappa 架構&#xff08;流處理一統江湖&#xff09;****關鍵概念對照表****實際案例理解****演進趨勢…

Spring Boot 自動配置全流程深度解析

在 Spring Boot 的世界里&#xff0c;“約定優于配置” 理念通過自動配置機制展現得淋漓盡致。從一個簡單的SpringBootApplication注解開始&#xff0c;背后隱藏著一套精妙的自動配置加載流程。本文將從SpringBootApplication出發&#xff0c;逐步拆解自動配置類是如何被發現、…

AI:業務驅動與技術賦能:企業智能化應用的雙向進化深度指南

一、業務與技術的雙螺旋進化模型 1.1 從單向適配到雙向驅動的認知轉變 傳統的信息化建設往往遵循"業務提需求、技術做實現"的線性模式&#xff0c;這種模式在穩定的業務環境中確實有效&#xff0c;但在當前快速變化的數字化時代已經顯露出明顯的局限性。真正的數字化…

2721. 【SDOI2010】外星千足蟲

2721. 【SDOI2010】外星千足蟲 題解 題目描述 題目描述 公元2089年6月4日&#xff0c;在經歷了17年零3個月的漫長旅行后&#xff0c;“格納格魯一號”載人火箭返回艙終于安全著陸。此枚火箭由美國國家航空航天局&#xff08;NASA&#xff09;研制發射&#xff0c;行經火星、…