順 序 表:數 據 存 儲 的 “ 有 序 陣 地 ”
- 線 性 表
- 順 序 表 - - - 順 序 存 儲 結 構
- 順 序 表 的 操 作 實 現
- 代 碼 全 貌 與 功 能 介 紹
- 順 序 表 的 功 能 說 明
- 代 碼 效 果 展 示
- 代 碼 詳 解
- SeqList.h
- SeqList.c
- test.c
- 總 結
💻作 者 簡 介:曾 與 你 一 樣 迷 茫,現 以 經 驗 助 你 入 門 數據 結 構。
💡個 人 主 頁:@笑口常開xpr 的 個 人 主 頁
📚系 列 專 欄:硬 核 數 據 結 構 與 算 法
?代 碼 趣 語:數 據 結 構 + 算 法 = 程 序 設 計。
💪代 碼 千 行,始 于 堅 持,每 日 敲 碼,進 階 編 程 之 路。
📦gitee 鏈 接:gitee
?????????在 數 據 結 構 的 世 界 里,每 一 種 設 計 都 可 能 孕 育 出 驚 人 的 效 率 變 革。你 是 否 深 思 過,一 組 精 心 組 織 的 數 據 究 竟 能 創 造 怎 樣 的 奇 跡?每 一 次 挖 掘 底 層 原 理,都 是 與 計 算 機 智 慧 的 巔 峰 對 話;每 一 次 剖 析 存 儲 模 式,都 在 破 解 數 據 世 界 的 終 極 密 碼。準 備 好 迎 接 這 場 盛 宴 了 嗎?讓 我 們 一 同 探 尋 數 據 結 構 的 順 序 表 的 無 盡 奧 秘,見 證 它 如 何 重 塑 數 字 時 代 的 運 行 法 則!
線 性 表
定 義
?????????線 性 表 是 由 相 同 數 據 類 型 的 ?n(?n ≥ 0)個 數 據 元 素 的 有 限 序 列。若 將 線 性 表 記 為
相 關 概 念
表 長:其 中 n 為 表 長,當 n = 0 時,稱 為 空 表;
表 頭:當 n > 0 時,在 這 個 序 列 中,a1 是 第 一 個 元 素,被 稱 為 表 頭 元 素
表 尾:an 是 最 后 一 個 元 素,被 稱 為 表 尾 元 素。
直 接 前 驅:a(i-1) 領 先 于 ai,稱 a(i-1) 是 ai 的 直 接 前 驅 元 素。
直 接 后 繼:ai 領 先 于 a(i+1),a(i+1) 是 ai 的 直 接 后 繼 元 素。
當 i 為 1,2,…,n-1 時,ai 有 且 僅 有 一 個 直 接 后 繼 元 素。
當 i 為 2,3,…,n 時,ai 有 且 僅 有 一 個 直 接 前 驅 元 素。
順 序 表 - - - 順 序 存 儲 結 構
定 義
?????????順 序 表 把 線 性 表 中 的 元 素 按 照 邏 輯 順 序 依 次 存 儲 在 一 組 地 址 連 續 的 存 儲 單 元 中,通 過 數 組 來 實 現。這 種 存 儲 方 式 讓 元 素 的 邏 輯 順 序 和 物 理 順 序 保 持 一 致,利 用 元 素 的 下 標 可 以 快 速 訪 問 到 任 何 一 個 元 素。
順 序 表 示 和 實 現
?????????假 設 順 序 表 的 每 個 元 素 占 用 l 個 存 儲 單 元,并 以 所 占 的 第 一 個 單 元 的 存 儲 地 址 作 為 數 據 元 素 的 存 儲 位 置。
?????????線 性 表 中 第 i + 1 個 數 據 元 素 的 存 儲 位 置 LOC(ai+1) 和 第 i 個 數 據 元 素 的 存 儲 位 置 LOC(ai) 之 間 滿 足 下 列 關 系:
線 性 表 的 第 i 個 數 據 元 素 ai 的 存 儲 位 置 為:
?????????式 中 LOC(a1) 是 線 性 表 中 第 一 個 數 據 元 素 a1 的 存 儲 位 置,通 常 稱 作 線 性 表 的 起 始 地 址 或 基 地 址。
?????????每 一 個 數 據 元 素 的 存 儲 位 置 都 和 順 序 表 的 起 始 位 置 相 差 一 個 和 數 據 元 素 在 線 性 表 中 的 第 n 個 元 素 成 正 比 的 常 數。由 此,只 要 確 定 了 存 儲 線 性 表 的 起 始 位 置,線 性 表 中 任 一 數 據 元 素 都 可 隨 機 存 取,所 以 線 性 表 的 順 序 存 儲 結 構 是 一 種 隨 機 存 取 的 存 儲 結 構。
順 序 表 的 操 作 實 現
?????????順 序 表 有 靜 態 順 序 表 和 動 態 順 序 表 兩 種,這 里 以 動 態 順 序 表 為 例 進 行 介 紹。
代 碼 全 貌 與 功 能 介 紹
?????????整 個 順 序表 由 三 個 主 要 文 件 構 成:SeqList.h、SeqList.c 和 test.c。這 種 多 文 件 的 架 構 設 計,有 助 于 將 不 同 功 能 模 塊 分 離,提 高 代 碼 的 可 讀 性、可 維 護 性 與 可 擴 展 性。
SeqList.h
?????????SeqList.h 包 含 了 順 序 表 所 需 的 頭 文 件 引 用、常 量 定 義 以 及 函 數 聲 明。
test.c
?????????test.c 是 順 序 表 的 主 邏 輯 文 件,負 責 處 理 用 戶 輸 入 和 代 碼 流 程 的 控 制。
SeqList.c
SeqList.c 則 實 現 了 順 序 表 的 具 體 功 能 函 數。
下 面 展 示
完 整 代 碼
。
讀 者 可 以 將 這 段 代 碼 復 制 到 自 己 的 編 譯 器 中 運 行。
SeqList.h
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define INIT_CAPACITY 4 //初始化的容量
typedef int SLDataType;
//動態順序表 --- 按需申請
typedef struct SeqList
{SLDataType* a;int size; //有效數據個數int capacity; //空間容量
}SL;//增刪查改//初始化
void SLInit(SL* ps);//釋放空間
void SLDestory(SL* ps);//輸出
void SLPrint(SL* ps);//擴容
void SLCheckCapacity(SL* ps);//尾插
void SLPushBack(SL* ps, SLDataType x);//尾刪
void SLPopBack(SL* ps);//頭插
void SLPushFront(SL* ps, SLDataType x);//頭刪
void SLPopFront(SL* ps);//某個數的后面插入
void SLBackInsert(SL* ps, int pos, SLDataType x);//某個數的前面插入
void SLFrontInsert(SL* ps, int pos, SLDataType x);//某個位置刪除
void SLErase(SL* ps, int pos);//查找
int SLFind(SL* ps, SLDataType x);//函數指針數組
void SeqListValue(int(*pf)(SL* ps, SLDataType x), SL* ps);void SeqListInsert(int(*pf)(SL* ps, SLDataType x), SL* ps, int num);//保存數據到文件中
void SaveSeqList(SL* ps);//加載數據到文件中
void LoadSeqList(SL* ps);
test.c
#define _CRT_SECURE_NO_WARNINGS 0 #include "SeqList.h"
void menu()
{printf("*************************************************\n");printf("***** 1. SLPushBack 2. SLPopBack *****\n");printf("***** 3. SLPushFront 4. SLPopFront *****\n");printf("***** 5. SLBackInsert 6. SLFrontInsert *****\n");printf("***** 7. SLErase 8. SLFind *****\n");printf("***** 9. SLPrint 0. exit *****\n");printf("*************************************************\n");
}
void test()
{SL s;SLInit(&s);int input = 0;do{menu();printf("請輸入你想要的操作:>\n");scanf("%d", &input);switch (input){case 0:SaveSeqList(&s);SLDestory(&s);break;case 1:SeqListValue(SLPushBack, &s);break;case 2:SLPopBack(&s);SLPrint(&s);break;case 3:SeqListValue(SLPushFront, &s);break;case 4:SLPopFront(&s);SLPrint(&s);break;case 5:SeqListInsert(SLBackInsert, &s, 5);break;case 6:SeqListInsert(SLFrontInsert, &s, 6);break;case 7:SeqListInsert(SLErase, &s, 7);break;case 8:SeqListInsert(SLFind, &s, 8);break;case 9:SLPrint(&s);break;default:printf("輸入錯誤,請重新輸入\n");break;}} while (input);
}
int main()
{test();return 0;
}
SeqList.c
#define _CRT_SECURE_NO_WARNINGS 0
#include "SeqList.h"
void SLInit(SL* ps)
{assert(ps);ps->a = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);if (ps->a == NULL){perror("malloc fail");return;}ps->size = 0;ps->capacity = INIT_CAPACITY;LoadSeqList(ps);
}void SLDestory(SL* ps)
{assert(ps);//空間是野指針//數組指向的空間越界free(ps->a);ps->a = NULL;ps->capacity = 0;ps->size = 0;printf("銷毀成功\n");
}void SLPrint(SL* ps)
{assert(ps);int i = 0;for (i = 0;i < ps->size;i++){printf("%d ", ps->a[i]);}printf("\n");
}void SLCheckCapacity(SL* ps)
{//擴容SLDataType* tmp = (SLDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(SLDataType));if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp;//不寫這句代碼的話是原地擴容,在原來的地址上增加ps->capacity *= 2;
}void SLPushBack(SL* ps, SLDataType x)
{printf("尾插\n");//方法1//assert(ps);//if (ps->size == ps->capacity)//{// SLCheckCapacity(ps);//}//ps->a[ps->size++] = x;ps->a[ps->size] = x;ps->size++;//方法2SLBackInsert(ps, ps->size, x);
}//不釋放空間,要釋放空間只能一塊一塊釋放
void SLPopBack(SL* ps)
{assert(ps);printf("尾刪\n");//方法1//assert(ps->size > 0);//方法2if (ps->size == 0){return;}//ps->a[ps->size - 1] = 0;ps->size--;//方法2//SLErase(ps, ps->size);
}
void SLPushFront(SL* ps, SLDataType x)
{printf("頭插\n");//方法1//assert(ps);//if (ps->size == ps->capacity)//{// SLCheckCapacity(ps);//}//int end = ps->size - 1;//while (end >= 0)//{// ps->a[end + 1] = ps->a[end];// end--;//}//ps->a[0] = x;//ps->size++;//方法2SLBackInsert(ps, 0, x);
}void SLPopFront(SL* ps)
{printf("頭刪\n");//方法1//assert(ps);//assert(ps->size > 0);//int begin = 1;//while (begin < ps->size)//{// ps->a[begin - 1] = ps->a[begin];// begin++;//}//ps->size--;//方法2SLErase(ps, 0);
}void SLErase(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos <= ps->size);int erase = pos - 1;while (erase <= ps->size - 1){ps->a[erase] = ps->a[erase + 1];erase++;}ps->size--;printf("消除成功\n");
}
int SLFind(SL* ps, SLDataType x)
{assert(ps);printf("查找\n");int i = 0;int flag = 0;for (i = 0; i < ps->size; i++){if (ps->a[i] == x){return i;}}return -1;
}void SeqListValue(int(*pf)(SL* ps, SLDataType x), SL* ps)
{int x = 0;printf("請輸入1個操作數:>\n");scanf("%d", &x);pf(ps, x);SLPrint(ps);
}void SLFrontInsert(SL* ps, int pos, SLDataType x)
{assert(ps);if (ps->capacity == ps->size){SLCheckCapacity(ps);}int tmp = ps->size - 1;while (tmp >= pos - 1){ps->a[tmp + 1] = ps->a[tmp];tmp--;}ps->a[pos - 1] = x;ps->size++;
}
void SLBackInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);if (ps->capacity == ps->size){SLCheckCapacity(ps);}int tmp = ps->size - 1;while (tmp >= pos){ps->a[tmp + 1] = ps->a[tmp];tmp--;}ps->a[pos] = x;ps->size++;
}
void SeqListInsert(int(*pf)(SL* ps, SLDataType x), SL* ps, int num)
{int x = 0;int y = 0;if (num == 5){printf("請輸入你要插入數的位置:>\n");scanf("%d", &x);printf("請輸入你要插入的數字:>\n");scanf("%d", &y);SLBackInsert(ps, x, y);SLPrint(ps);}else if (num == 6){printf("請輸入你要插入的數字:>\n");scanf("%d", &x);printf("請輸入你要插入的數字的位置:>\n");scanf("%d", &y);SLFrontInsert(ps, y, x);SLPrint(ps);}else if (num == 7){printf("請輸入你要擦除數的位置:>\n");scanf("%d", &x);pf(ps, x);SLPrint(ps);}else{printf("請輸入你要查找的數字:>\n");scanf("%d", &x);int ret = pf(ps, x);if (ret != -1){printf("下標是:%d\n", ret);}else{printf("查找的數字不存在\n");}}
}void SaveSeqList(SL* ps)
{FILE* pf = fopen("SeqList.txt", "wb");if (pf == NULL){perror("SaveSeqList");return;}else{int i = 0;for (i = 0; i < ps->size; i++){fwrite(ps->a + i, sizeof(SLDataType), 1, pf);}fclose(pf);pf = NULL;printf("保存文件成功\n");}
}
void LoadSeqList(SL* ps)
{//打開文件FILE* pf = fopen("SeqList.txt", "rb");if (NULL == pf){perror("LoadSeqList");return;}else{//讀數據SLDataType tmp = 0;int i = 0;while (fread(&tmp, sizeof(SLDataType), 1, pf)){if (ps->size == ps->capacity){SLCheckCapacity(ps);}ps->a[i] = tmp;ps->size++;i++;}fclose(pf);pf = NULL;}
}
順 序 表 的 功 能 說 明
數 據 結 構 定 義:順 序 表 是 用 物 理 地 址 連 續 的 存 儲 單 元 依 次 存 儲 數 據 元 素 的 線 性 結 構,邏 輯 順 序 與 物 理 順 序 一 致,基 于 數 組 實 現。使 用 動 態 數 組 存 儲,通 過 malloc/realloc 動 態 管 理 內 存,支 持 擴 容,靈 活 性 高。
初 始 化:SLInit 函 數 為 動 態 數 組 分 配 初 始 內 存 ,大 小 是 4 * 4 = 16 個 字 節。初 始 化 大 小 是 0,容 量 為 4。
銷 毀: SLDestory 函 數 釋 放 動 態 數 組 內 存,避 免 內 存 泄 漏。重 置 大 小 和 容 量 都 是 0,標 記 順 序 表 為 空。
擴 容:插 入 元 素 前 若 空 間 不 足(大 小 和 容 量 相 等 時),先 擴 容 再 操 作。SLCheckCapacity 函 數 自 動 擴 展 容 量(容 量 翻 倍),使 用 realloc 重 新 分 配 內 存,遷 移 數 據。這 里 因 為 容 量 翻 倍 會 進 行 異 地 擴 容。
數 據 插 入
操作類型 | 函數 | 時間復雜度 | 功能說明 |
---|---|---|---|
頭插 | SLPushFront | O(N) | 移動全部元素 |
尾插 | SLPushBack | O(1) | 直接插入 |
一個數的前面插入 | SLFrontInsert | O(N) | 將 pos 及之后的元素后移一位 |
一個數的后面插入 | SLBackInsert | O(N) | 將 pos-1 及之后的元素后移一位 |
數 據 刪 除
操作類型 | 函數 | 時間復雜度 | 功能說明 |
---|---|---|---|
頭刪 | SLPopFront | O(N) | 移動全部元素 |
尾刪 | SLPopBack | O(1) | 直接調整 size,無需移動 |
指定位置刪除 | SLErase | O(N) | 將pos+1及之后的元素前移一位 |
數 據 查 找:SLFind 函 數 順 序 遍 歷 數 組,查 找 元 素 x 的 首 次 出 現 位 置,返 回 下 標( 未 找 到 返 回 -1)。時 間 復 雜度:O(N)(平 均 情 況)。
數 據 持 久 化:SaveSeqList 函 數 用 于 將 順 序 表 中 的 信 息 以 二 進 制 形 式 保 存 到 文 件 中,LoadSeqList 函 數 用 于 從 文 件 中 加 載 順 序 表,實 現 數 據 的 持 久 化 存 儲。
錯 誤 處 理:在 內 存 分 配 和 文 件 操 作 中 使 用 了 perror 等 方 式 進 行 錯 誤 處 理,當 操 作 失 敗 時 輸 出 相 應 的 錯 誤 信 息。
代 碼 效 果 展 示
菜 單 展 示
?????????每 次 循 環 開 始 時 會 顯 示 菜 單,內 容 包 括:
SLPushBack:尾 插
SLPopBack:尾 刪
SLPushFront:頭 插
SLPopFront:頭 刪
SLBackInsert:從 一 個 數 的 后 面 插 入
SLFrontInsert:從 一 個 數 的 前 面 插 入
SLErase:擦 除 某 個 指 定 的 數
SLFind:查 找 某 個 數
SLPrint:輸 出 順 序 表
exit:退 出 程 序 并 將 順 序 表 保 存 到 文 件 中
尾 插(SLPushBack)
?????????輸 入 1 后,程 序 會 提 示 用 戶 輸 入 插 入 的 數 字。輸 入 完 成 后,數 字 被 添 加 到 順 序 表 的 末 尾。如 果 順 序 表 已 滿,會 自 動 增 加 容 量。
尾 刪(SLPopBack)
?????????輸 入 2 后,順 序 表 的 末 尾 的 一 個 數 字 會 被 刪 除 并 輸 出 刪 除 后 的 順 序 表。
頭 插(SLPushFront)
?????????輸 入 3 后,程 序 會 提 示 用 戶 輸 入 插 入 的 數 字。輸 入 完 成 后,如 果 順 序 表 已 滿,會 自 動 增 加 容 量,然 后 數 字 被 添 加 到 順 序 表 的 第 一 個 位 置。
頭 刪(SLPopFront)
?????????輸 入 4 后,順 序 表 的 第 一 個 位 置 的 數 字 會 被 刪 除 并 輸 出 刪 除 后 的 順 序 表。
從 某 一 個 數 字 的 后 面 插 入(SLBackInsert)
?????????輸 入 5 后,程 序 會 提 示 用 戶 輸 入 插 入 的 數 字 和 要 插 入 數 字 的 位 置。輸 入 完 成 后,數 字 被 添 加 到 順 序 表 的 要 輸 入 數 字 的 后 面。如 果 順 序 表 已 滿,會 自 動 增 加 容 量。
從 某 一 個 數 字 的 前 面 插 入(SLFrontInsert)
?????????輸 入 6 后,程 序 會 提 示 用 戶 輸 入 插 入 的 數 字 和 要 插 入 數 字 的 位 置。輸 入 完 成 后,數 字 被 添 加 到 順 序 表 的 要 輸 入 數 字 的 前 面。如 果 順 序 表 已 滿,會 自 動 增 加 容 量。
清 除 指 定 數 的 位 置(SLErase)
?????????輸 入 7 后,程 序 會 提 示 用 戶 輸 入 清 除 數 字 的 位 置。輸 入 完 成 后,輸 入 位 置 上 的 數 字 會 被 清 除。
查 找 數 字(SLFind)
?????????輸 入 8 后,程 序 會 提 示 用 戶 要 查 找 的 數 字。輸 入 完 成 后,程 序 會 查 找 數 字,如 果 找 到 會 輸 出 數 字 的 下 標,如 果 沒 有 找 到,則 輸 出 “查 找 的 數 字 不 存 在”。
輸 出 順 序 表(SLPrint)
?????????輸 入 9 后,程 序 會 在 屏 幕 上 顯 示 目 前 的 順 序 表。
退 出 順 序 表(exit)
?????????輸 入 0 后,程 序 會 將 順 序 表 中 的 信 息 以 二 進 制 形 式 保 存 到 文 件 “SeqList.txt” 中。釋 放 內 存 并 銷 毀 順 序 表, 然 后 退 出 程 序。
代 碼 詳 解
SeqList.h
#pragma once
?????????#pragma once 是 一 個 預 處 理 指 令,用 于 防 止 頭 文 件 被 重 復 包 含。在 大 型 項 目 中,多 個 源 文 件 可 能 會 包 含 同 一 個 頭 文 件,使 用 #pragma once 可 以 確 保 頭 文 件 的 內 容 只 被 編 譯 一 次,避 免 重 復 定 義 的 錯 誤。
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
?????????#include 指 令 用 于 引 入 必 要 的 標 準 庫 頭 文 件:
stdio.h:提 供 標 準 輸 入 輸 出 函 數,如 printf、scanf 等。
assert.h:提 供 assert 宏,用 于 在 運 行 時 檢 查 程 序 的 斷 言 條 件。
stdlib.h:提 供 內 存 管 理 函 數(如 malloc、realloc、free)和 其 他 標 準 庫 函 數。
#define INIT_CAPACITY 4 //初始化的容量
typedef int SLDataType;
?????????#define 指 令 用 于 定 義 一 些 常 量:
INIT_CAPACITY:定 義 順 序 表 的 初 始 容 量 為 4。
typedef int SLDataType:將 int 重 新 定 義 成 SLDataType
typedef struct SeqList
{SLDataType* a;int size; //有效數據個數int capacity; //空間容量
}SL;
?????????定 義 了 一 個 名 為 SeqList 的 結 構 體 并 重 新 命 名 為 SL,用 于 表 示 整 個 順 序 表:
a:指 向 結 構 體 的 指 針,用 于 動 態 分 配 內 存 來 存 儲 順 序 表 信 息。
size:整 數,記 錄 當 前 順 序 表 中 已 存 儲 的 數 字 個 數。
capacity:整 數,記 錄 當 前 順 序 表 的 最 大 容 量。
//增刪查改//初始化
void SLInit(SL* ps);//釋放空間
void SLDestory(SL* ps);//輸出
void SLPrint(SL* ps);//擴容
void SLCheckCapacity(SL* ps);//尾插
void SLPushBack(SL* ps, SLDataType x);//尾刪
void SLPopBack(SL* ps);//頭插
void SLPushFront(SL* ps, SLDataType x);//頭刪
void SLPopFront(SL* ps);//某個數的后面插入
void SLBackInsert(SL* ps, int pos, SLDataType x);//某個數的前面插入
void SLFrontInsert(SL* ps, int pos, SLDataType x);//某個位置刪除
void SLErase(SL* ps, int pos);//查找
int SLFind(SL* ps, SLDataType x);//函數指針
void SeqListValue(int(*pf)(SL* ps, SLDataType x), SL* ps);void SeqListInsert(int(*pf)(SL* ps, SLDataType x), SL* ps, int num);//保存數據到文件中
void SaveSeqList(SL* ps);//加載數據到文件中
void LoadSeqList(SL* ps);
?????????函 數 聲 明,定 義 了 對 順 序 表 進 行 操 作 的 各 個 功 能 函 數:
SLInit:用 于 初 始 化 順 序 表,分 配 內 存 并 設 置 初 始 狀 態。
SLDestory:用 于 銷 毀 順 序 表,釋 放 分 配 的 內 存。
SLPrint:用 于 顯 示 順 序 表 中 所 有 的 數 字。
SLCheckCapacity:檢 查 通 訊 錄 的 容 量,必 要 時 進 行 擴 容。
SLPushBack:用 于 對 順 序 表 末 尾 添 加 數 字。
SLPopBack:用 于 對 順 序 表 末 尾 刪 除 數 字。
SLPushFront:用 于 對 順 序 表 第 一 個 位 置 添 加 數 字。
SLPopFront:用 于 對 順 序 表 第 一 個 位 置 刪 除 數 字。
SLBackInsert:用 于 對 順 序 表 的 指 定 位 置 的 后 面 插 入 數 字。
SLFrontInsert:用 于 對 順 序 表 的 指 定 位 置 的 前 面 插 入 數 字。
SLErase:用 于 從 順 序 表 中 刪 除 指 定 的 數 字。
SLFind:根 據 輸 入 的 數 字 查 找 順 序 表 中 的 數 字,返 回 數 字 在 順 序 表 中 的 下 標,找 不 到 返 回 特 定 值。
SeqListValue:函 數 指 針,傳 遞 函 數 地 址,用 于 程 序 中 相 同 的 部 分,節 省 代 碼 量。
SeqListInsert:函 數 指 針,傳 遞 函 數 地 址,用 于 程 序 中 相 同 的 部 分,節 省 代 碼 量。
SaveSeqList:將 順 序 表 中 的 信 息 保 存 到 文 件 中。
LoadSeqList:從 文 件 中 加 載 信 息 到 順 序 表 中。
SeqList.c
內 存 管 理 機 制
void SLInit(SL* ps)
{assert(ps);ps->a = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);if (ps->a == NULL){perror("malloc fail");return;}ps->size = 0;ps->capacity = INIT_CAPACITY;LoadSeqList(ps);
}
//擴容
void SLCheckCapacity(SL* ps)
{//擴容SLDataType* tmp = (SLDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(SLDataType));if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp;//不寫這句代碼的話是原地擴容,在原來的地址上增加ps->capacity *= 2;
}
初 始 化:使 用 calloc 分 配 初 始 空 間 并 將 所 有 數 據 初 始 化。
動 態 擴 容:每 次 容 量 不 足 時 增 加 原 來 空 間 數 的 2 倍。
內 存 安 全:通 過 realloc 確 保 數 據 連 續 性,失 敗 時 輸 出 錯 誤 信 息。
順 序 表 核 心 操 作
尾 刪
void SLPopBack(SL* ps)
{assert(ps);printf("尾刪\n");//方法1//assert(ps->size > 0);//方法2if (ps->size == 0){return;}//ps->a[ps->size - 1] = 0;ps->size--;//方法2//SLErase(ps, ps->size);
}
?????????總 共 有 兩 種 方 法 這 里 以 方 法 1 為 例,方 法 2 中 尾 刪 是 任 意 位 置 刪 除 的 特 例(pos = size)。使 用 方 法 1 時 間 復 雜 度 為 O(1),而 使 用 方 法 2 刪 除 最 后 一 個 元 素 時,SLErase 仍 需 執 行 while 循 環(盡 管 循 環 體 不 執 行),效 率 略 低 于 方法 1。
刪 除 處 理:直 接 減 小 有 效 長 度 即 可,將 數 字 置 為 0 沒 有 意 義。
尾 插
void SLPushBack(SL* ps, SLDataType x)
{printf("尾插\n");//方法1assert(ps);if (ps->size == ps->capacity){SLCheckCapacity(ps);}ps->a[ps->size++] = x;//ps->a[ps->size] = x;//ps->size++;//方法2//SLBackInsert(ps, ps->size, x);
}
?????????總 共 有 兩 種 方 法 這 里 以 方 法 1 為 例,方 法 2 中 尾 插 是 任 意 位 置 插 入 的 特 例(pos = size)。方 法 1 和 方 法 2 的 差 別 和 尾 刪 類 似,這 里 不 再 描 述。
自 動 擴 容:添 加 前 檢 查 容 量,不 足 時 自 動 擴 展。
輸 入 處 理:直 接 插 入 數 字 即 可。
頭 刪
void SLPopFront(SL* ps)
{printf("頭刪\n");//方法1assert(ps);assert(ps->size > 0);int begin = 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];begin++;}ps->size--;//方法2//SLErase(ps, 0);
}
插 入 處 理:將 所 有 位 置 的 數 字 前 移 一 位,大 小 減 1 即 可。
頭 插
void SLPushFront(SL* ps, SLDataType x)
{printf("頭插\n");//方法1assert(ps);if (ps->size == ps->capacity){SLCheckCapacity(ps);}int end = ps->size - 1;while (end >= 0){ps->a[end + 1] = ps->a[end];end--;}ps->a[0] = x;ps->size++;//方法2//SLBackInsert(ps, 0, x);
}
?????????總 共 有 兩 種 方 法 這 里 以 方 法 1 為 例,方 法 2 中 頭 插 是 任 意 位 置 插 入 的 特 例。
自 動 擴 容:添 加 前 檢 查 容 量,不 足 時 自 動 擴 展。
輸 入 處 理:將 所 有 位 置 的 數 字 后 移 一 位,直 接 插 入 數 字 即 可。
指 定 數 字 的 后(前) 面 插 入
void SLBackInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);if (ps->capacity == ps->size){SLCheckCapacity(ps);}int tmp = ps->size - 1;while (tmp >= pos){ps->a[tmp + 1] = ps->a[tmp];tmp--;}ps->a[pos] = x;ps->size++;
}
void SLFrontInsert(SL* ps, int pos, SLDataType x)
{assert(ps);if (ps->capacity == ps->size){SLCheckCapacity(ps);}int tmp = ps->size - 1;while (tmp >= pos - 1){ps->a[tmp + 1] = ps->a[tmp];tmp--;}ps->a[pos - 1] = x;ps->size++;
}
自 動 擴 容:添 加 前 檢 查 容 量,不 足 時 自 動 擴 展。
輸 入 處 理:將 指 定 位 置(前 面 插 入 包 括 指 定 位 置,后 面 插 入 不 包 括 指 定 位 置) 的 數 字 后 移 一 位,直 接 插 入 數 字 即 可。
刪 除 指 定 位 置 的 數字
void SLErase(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos <= ps->size);int erase = pos - 1;while (erase <= ps->size - 1){ps->a[erase] = ps->a[erase + 1];erase++;}ps->size--;printf("消除成功\n");
}
數 據 遷 移:刪 除 后 將 后 續 元 素 前 移 覆 蓋。
查 找 數 字
int SLFind(SL* ps, SLDataType x)
{assert(ps);printf("查找\n");int i = 0;int flag = 0;for (i = 0; i < ps->size; i++){if (ps->a[i] == x){return i;}}return -1;
}
函 數 功 能:在 順 序 表 中 查 找 指 定 的 數 字,返 回 其 索 引(未 找 到 返 回 - 1)。
數 據 持 久 化
void SaveSeqList(SL* ps)
{FILE* pf = fopen("SeqList.txt", "wb");if (pf == NULL){perror("SaveSeqList");return;}else{int i = 0;for (i = 0; i < ps->size; i++){fwrite(ps->a + i, sizeof(SLDataType), 1, pf);}fclose(pf);pf = NULL;printf("保存文件成功\n");}
}
void LoadSeqList(SL* ps)
{//打開文件FILE* pf = fopen("SeqList.txt", "rb");if (NULL == pf){perror("LoadSeqList");return;}else{//讀數據SLDataType tmp = 0;int i = 0;while (fread(&tmp, sizeof(SLDataType), 1, pf)){if (ps->size == ps->capacity){SLCheckCapacity(ps);}ps->a[i] = tmp;ps->size++;i++;}fclose(pf);pf = NULL;}
}
二 進 制 存 儲:使 用 fwrite 和 fread 進 行 整 塊 數 據 讀 寫。
自 動 加 載:初 始 化 時 自 動 從 文 件 恢 復 數 據。
容 量 管 理:加 載 時 動 態 擴 展 內 存 確 保 足 夠 空 間。
test.c
菜 單 界 面
void menu()
{printf("*************************************************\n");printf("***** 1. SLPushBack 2. SLPopBack *****\n");printf("***** 3. SLPushFront 4. SLPopFront *****\n");printf("***** 5. SLBackInsert 6. SLFrontInsert *****\n");printf("***** 7. SLErase 8. SLFind *****\n");printf("***** 9. SLPrint 0. exit *****\n");printf("*************************************************\n");
}
使 用 星 號 (*) 繪 制 邊 框,提 供 清 晰 的 視 覺 界 面。
數 字 編 號 對 應 不 同 功 能,方 便 用 戶 選 擇。
主 循 環 邏 輯
void test()
{SL s;SLInit(&s);int input = 0;do{menu();printf("請輸入你想要的操作:>\n");scanf("%d", &input);switch (input){case 0:SaveSeqList(&s);SLDestory(&s);break;case 1:SeqListValue(SLPushBack, &s);break;case 2:SLPopBack(&s);SLPrint(&s);break;case 3:SeqListValue(SLPushFront, &s);break;case 4:SLPopFront(&s);SLPrint(&s);break;case 5:SeqListInsert(SLBackInsert, &s, 5);break;case 6:SeqListInsert(SLFrontInsert, &s, 6);break;case 7:SeqListInsert(SLErase, &s, 7);break;case 8:SeqListInsert(SLFind, &s, 8);break;case 9:SLPrint(&s);break;default:printf("輸入錯誤,請重新輸入\n");break;}} while (input);
}
初 始 化:調 用 SLInit 分 配 內 存 并 加 載 文 件 數 據。
循 環 控 制:使 用 do-while 確 保 至 少 執 行 一 次 菜 單 選 擇。
選 項 處 理:通 過 switch 分 支 調 用 不 同 功 能 函 數。
安 全 退 出:退 出 前 保 存 數 據 并 釋 放 內 存,防 止 資 源 泄 漏。
程 序 入 口
int main()
{test();return 0;
}
?????????main 函 數 是 程 序 的 入 口 點,調 用 test 函 數 啟 動 順 序 表,最 后 返 回 0 表 示 程 序 正 常 結 束。
總 結
?????????至 此,關 于 數 據 結 構 順 序 表 探 索 暫 告 一 段 落,但 你 的 編 程 征 程 才 剛 剛 啟 航。編 寫 代 碼 是 與 計 算 機 邏 輯 深 度 對 話,過 程 中 雖 會 在 結 構 設 計、算 法 實 現 的 困 境 里 掙 扎,但 這 些 磨 礪 加 深 了 對 代 碼 邏 輯 和 數 據 組 織 的 理 解。愿 你 合 上 電 腦 后,靈 感 不 斷,在 數 據 結 構 的 世 界 里 持 續 深 耕,書 寫 屬 于 自 己 的 編 程 傳 奇,下 一 次 開 啟,定 有 全 新 的 精 彩 等 待。小 編 期 待 重 逢,盼 下 次 閱 讀 時 見 證 你 們 更 大 的 進 步,共 赴 代 碼 之 約!