资讯动态

顺序表应用全解析:从集合运算到动态扩容的工程实践

发布时间:2026/10/3 3:08:18 来源:尧图企业网站定制
1. 顺序表到底能拿来干什么先别急着写代码很多人在学数据结构时对“顺序表”这个概念的印象就是“一个数组加上插入删除查找的方法”然后过了实验课就丢到一边觉得这东西太基础、太简单没什么实际用场。但如果你真的只把它当“会动的数组”来看后面学到链表、栈、队列、哈希表的时候很容易陷入一个尴尬的状态每个结构单独看都懂但真让你选一个来解决具体问题反而不知道选谁。从我自己的学习过程回头看顺序表最核心的价值恰恰在于它是所有线性结构里“最好理解、也最容易拿来做底层抽象”的那一个。你要处理的数据如果规模不大、以随机访问为主、插入删除集中在表尾那顺序表就是最省事的选择。它不是最快的也不是最灵活的但它是思路最直观的拿它先跑通完整流程再谈优化几乎是我见过所有靠谱的入门路线。这篇笔记的核心主题是“顺序表的应用”所以我不会去重复教材里已经写得很详细的定义、结构体声明、基本操作流程这些内容而是把重点放在三个真正值得你反复琢磨的角度第一顺序表在集合运算场景里的典型用法尤其是并集、交集、差集的实现思路第二顺序表作为排序和查找实验的载体为什么被反复使用以及具体怎么把算法“放进去”第三工程实现里那些坑——扩容、边界、复杂度、数据移动这些才是实验报告之外真正的收获。这篇笔记适合正在学数据结构初阶、准备数据结构实验报告、或者考研复习第一轮想快速回顾顺序表应用技巧的同学。如果你已经把顺序表的基本操作写得很顺但想知道“这东西到底能解决什么实际问题”那你来对地方了。2. 顺序表应用的设计思路为什么选择它而不是链表2.1 应用场景和结构选型的匹配逻辑不管是做实验还是做小项目拿到一个需求第一件事不是打开编辑器写代码而是先搞清楚数据规模长什么样、操作集中在哪里。顺序表的适用场景非常明确数据量可预估或相对不大最多几千上万个元素而且绝大多数操作是按下标读数据。这听起来是句废话但实际做起来很多人都会犯一个错——拿到需求就直接用链表理由是“链表插入删除快”。问题是链表的插入删除快是有前提的得先通过遍历找到目标位置这个遍历成本是 O(n)。对于数据量小的场景顺序表插入删除虽然要移动大量元素但内存连续、缓存友好、遍历也快综合代价未必比链表差。我上数据结构课的时候做过一个简单实验在十万级以内的数据规模下顺序表即使做插入和删除只要不是每次都在表头狂插整体性能反而不输链表原因就是连续内存的局部性优势太明显了。所以在具体应用设计时你可以按这个思路做选型判断表场景特征建议选择理由数据量小、随机访问频繁顺序表按下标访问 O(1)不用遍历插入删除集中在表尾顺序表尾部操作无需移动大量元素需要频繁在任意位置插入删除链表避免大规模数据搬迁数据量不确定且增长剧烈链表或动态扩容顺序表需综合考虑扩容成本需要频繁做排序、查找顺序表对随机访问型算法更友好上面这个表看起来简单但真正帮我形成判断力的是我后来反复写集合运算代码时体会到的很多算法天然依赖随机访问能力。比如后面要讲的排序算法快排、堆排这些全都是基于下标的跳跃访问你拿链表写快排就非常痛苦要来回改指针而顺序表天然支持这种操作。这就是为什么顺序表在基础实验里被当作排序、查找、集合运算的标准载体。2.2 顺序表应用的三个典型层次我习惯把顺序表应用分成三个层次方便自己检查到底掌握了多少。第一层存储层应用。也就是用顺序表存数据比如学生信息、图书信息、商品列表。这层的要求很低理解结构体的定义、初始化、插入删除遍历就够用了。实验报告里最基础的“通讯录管理系统”就属于这一层。第二层运算层应用。也就是在顺序表的基础上实现一些经典算法或运算规则比如顺序表的逆置、元素去重、有序表合并、集合的交并差运算。这层的核心难点在于“运算规则”和“存储结构”如何结合——你可能很清楚集合运算的数学定义但把它翻译成线性表上的操作时就会遇到“怎么查重、怎么避免数据移动浪费、怎么控制时间复杂度”这类实际工程问题。第三层抽象层应用。用顺序表实现栈、队列、甚至更复杂的数据结构。因为顺序表和数组的天然亲缘关系栈、循环队列的底层用顺序表来实现是很自然的。网上很多资料会把栈和队列单独拎出来讲但你不难发现它们的物理存储依然是用数组那一套做底子的。这篇笔记我重点讲第二层因为这一层最容易被忽略也最锻炼人。测试试卷里爱出的“集合求并集”“有序顺序表合并”“顺序表去重”全是这一层的题型。你如果能把第二层搞得通透做实验和考试基本就不慌。3. 核心应用一集合运算的顺序表实现3.1 并集运算从数学定义到代码细节集合的并集数学上定义为 A∪B {x | x∈A 或 x∈B}。翻译成顺序表操作语言就是把表 A 的所有元素放入结果表再把表 B 中所有“不在 A 中”的元素追加到结果表尾部。核心判断逻辑是“查重——元素是否已在结果集中”。先给一个完整的 C 语言实现这个代码几乎可以直接用来做实验报告的基础版本#include stdio.h #include stdlib.h #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int length; } SeqList; void initList(SeqList *list) { list-length 0; } // 在顺序表末尾追加元素 int append(SeqList *list, int value) { if (list-length MAX_SIZE) { printf(顺序表已满无法追加 %d\n, value); return 0; } list-data[list-length] value; return 1; } // 判断某个值是否存在于表中存在返回1不存在返回0 int contains(SeqList *list, int value) { for (int i 0; i list-length; i) { if (list-data[i] value) { return 1; } } return 0; } // 求 A 和 B 的并集存到 result 中 void unionSet(SeqList *A, SeqList *B, SeqList *result) { initList(result); // 先把 A 的全部元素放进去 for (int i 0; i A-length; i) { append(result, A-data[i]); } // 再放入 B 中不在 result 里的元素 for (int i 0; i B-length; i) { if (!contains(result, B-data[i])) { append(result, B-data[i]); } } } void printList(SeqList *list) { printf(集合元素: ); for (int i 0; i list-length; i) { printf(%d , list-data[i]); } printf(\n); } int main() { SeqList A, B, result; initList(A); initList(B); append(A, 1); append(A, 3); append(A, 5); append(A, 7); append(B, 2); append(B, 3); append(B, 6); append(B, 7); append(B, 8); printf(集合 A: ); printList(A); printf(集合 B: ); printList(B); unionSet(A, B, result); printf(A ∪ B 的结果:\n); printList(result); return 0; }代码结构不复杂关键就一个点contains函数承担了“查重”职责。每次处理 B 里的元素都要先遍历一次 result 表确认是否已存在。这里的时间复杂度是 O(n²) 级别集合规模在几十上百时完全没问题但如果数据规模上万你就得考虑用哈希表先建索引把查重成本降到 O(1)。3.2 交集和差集同一套思路的变形交集 A∩B数学定义是 {x | x∈A 且 x∈B}。顺序表实现上思路就是遍历 A 的元素检查它是否也存在于 B 中存在就放入结果表。代码比并集还少几行因为不需要第一步整体拷贝。差集 A−B数学定义是 {x | x∈A 且 x∉B}。它的实现思路是遍历 A 的元素检查它是否不在 B 中不在就放入结果表。这三个操作放在一起你会发现核心问题永远是“怎么判断一个元素是否存在于某个集合中”。在没有其他辅助结构时统一用线性查找如果你记性好、数据还有序就可以用二分查找来降低复杂度。这也是一个很重要的延伸点当 A 和 B 都是有序表时三个集合运算都可以写成双指针线性扫描的版本时间复杂度降到 O(mn)这才是在考研和面试里真正被看重的优化思路。3.3 有序表的合并双指针方法详解上面提到有序表这里单独展开。假设有两个非递减有序表 A 和 B要求把它们合并成一个新的非递减有序表 C。用双指针扫描每次比较 A[i] 和 B[j]把较小的放入 C然后移动对应指针。这个算法的复杂度 O(mn)空间 O(mn)思路本身不难但代码细节有几处容易错。核心代码void mergeSortedLists(SeqList *A, SeqList *B, SeqList *C) { initList(C); int i 0, j 0; while (i A-length j B-length) { if (A-data[i] B-data[j]) { append(C, A-data[i]); i; } else { append(C, B-data[j]); j; } } // 处理剩余元素两个while只会进入一个 while (i A-length) { append(C, A-data[i]); i; } while (j B-length) { append(C, B-data[j]); j; } }这个代码看起来很简单但踩坑点全在后面那两个处理剩余元素的 while 里。我第一次写的时候漏掉了其中一个合并结果少了尾巴上的一截调试了半天才反应过来。还有一个细节是而不是如果两个元素相等用可以保证先取 A 的合并结果依然有序而且能维持稳定性。如果你要在两个有序表中“去重合并”只需在 append 前判断 C 最后一个元素是否等于当前要插入的值相等就跳过。因为 C 本身保持有序重复元素必然相邻判断最后一个元素就够了不需要全表查重。这个细节在很多实验进阶题里都会出现。4. 核心应用二顺序表上的排序与查找实验4.1 为什么排序实验都以顺序表为载体一个很现实的原因是大多数排序算法天然适合数组形态的实现。冒泡排序、插入排序、选择排序、快排、堆排这些经典算法核心操作都是“按下标比较元素、按下标交换元素”。顺序表正是用连续的数组存储的写法上没有任何额外负担。你可以把全部注意力集中在排序算法本身的逻辑上而不是花时间去处理指针跳转。反过来看如果用链表做排序比如对单链表做插入排序你需要维护多个指针还要处理节点断链和重接逻辑复杂度和出错概率都会上升。基本数据结构课程里排序实验之所以总跟顺序表绑定不是因为排序只能建立在顺序表上而是因为初阶学习者需要先掌握算法思想而不是被存储结构的复杂性分散精力。4.2 一个完整的插入排序应用实例以直接插入排序为例展示如何把它应用在顺序表上。直接插入排序的思路很像扑克牌理牌每次把一个元素插入到左侧已经有序的序列中直到全部元素有序。void insertionSort(SeqList *list) { int i, j; int temp; for (i 1; i list-length; i) { temp list-data[i]; j i - 1; while (j 0 list-data[j] temp) { list-data[j 1] list-data[j]; j--; } list-data[j 1] temp; } }这段代码的关键在于内层 while 的循环条件里一定要有j 0这个判断。很多初写者会写成while (list-data[j] temp)然后程序在 j-1 时照样去访问 data[-1]产生越界。这个 bug 不影响编译但在运行时会读取到非法内存数据结果全乱。插入排序在顺序表上的排序过程有一个很直观的视角它其实是把顺序表分成了“已排序前缀”和“未排序后缀”两个逻辑区域每次从后缀取一个数往前插。这种视角对理解“排序算法的循环不变量”很有帮助——每轮结束后前 i 个元素已经有序这就是插入排序的循环不变量。4.3 快速排序在顺序表上的应用简化版本快排对数组的依赖更明显。它的思想是“分而治之”选一个基准值把数组划分成左右两个区间左边的全部小于基准右边的全部大于等于基准然后递归处理左右区间。顺序表天然支持这种划分因为划分过程就是在数组内部做元素交换。int partition(SeqList *list, int low, int high) { int pivot list-data[low]; while (low high) { while (low high list-data[high] pivot) { high--; } list-data[low] list-data[high]; while (low high list-data[low] pivot) { low; } list-data[high] list-data[low]; } list-data[low] pivot; return low; } void quickSort(SeqList *list, int low, int high) { if (low high) { int pivotIndex partition(list, low, high); quickSort(list, low, pivotIndex - 1); quickSort(list, pivotIndex 1, high); } }这个版本用的是最简单的“挖坑填数法”教程里很常见。实际跑起来要注意如果顺序表本身已经有序且每次都选择第一个元素做基准快排会退化成 O(n²)。想要避免可以在选基准时采用“随机选”或“三数取中”策略。这个知识点数据结构期末考试和考研初试都很爱考不只是理论题也经常让人在实验复现时吃亏。4.4 顺序表上的顺序查找和二分查找查找是顺序表的拿手好戏随机访问 O(1) 的特性让查找算法的实现近乎理想。顺序查找是最简单的遍历int seqSearch(SeqList *list, int key) { for (int i 0; i list-length; i) { if (list-data[i] key) { return i; } } return -1; }二分查找要求顺序表有序是“有序表折半缩小范围”的思想。代码有一个经典边界陷阱中间位置计算用mid (low high) / 2在极端情况下可能溢出。规模小没事数据量大的时候建议写成mid low (high - low) / 2。int binarySearch(SeqList *list, int key) { int low 0; int high list-length - 1; while (low high) { int mid low (high - low) / 2; if (list-data[mid] key) { return mid; } else if (list-data[mid] key) { low mid 1; } else { high mid - 1; } } return -1; }说一个我当年踩坑的细节while 条件到底是low high还是low high这个直接决定程序会不会漏判。如果是low high那最后 low high 时还会再检查一次中间那个元素这时候能找到边界上的元素如果写成low high最后只剩一个元素时就跳出了循环极可能返回 -1 找不到。初学者建议直接把low high当作标准写法来记忆同时理解它的边界含义。5. 顺序表的去重、逆置和循环移位三个高频实验变体5.1 顺序表去重的三种方案对比去重是集合运算查重思想的延伸也是期末实验的常客。我见过最傻的方法也是很多初学者第一反应能想到的拿双重循环对每个元素检查前面是否出现过全部检查完生成新表。这没问题但 O(n²) 的复杂度在大数据量下很浪费。实际上根据数据特征有三种做法方案时间复杂度空间复杂度适用条件双重循环逐个查重O(n²)O(1)数据量小最简单先排序再去重O(n log n)O(1) 或 O(n)不要求保持原顺序借助哈希表标记O(n)O(n)数据量大需要保留原顺序如果题目要求“删除顺序表中重复元素保留第一次出现的顺序”最简单的实现是两层循环外层遍历每个位置内层检查它是否和当前结果区重复void removeDuplicates(SeqList *list) { if (list-length 1) { return; } int newLength 1; for (int i 1; i list-length; i) { int dup 0; for (int j 0; j newLength; j) { if (list-data[j] list-data[i]) { dup 1; break; } } if (!dup) { list-data[newLength] list-data[i]; } } list-length newLength; }这段代码的思路是“原地重写”用 newLength 标记结果区的末尾遍历原表时把不重复的元素搬到前面去。不需要开新表也不怕覆盖还没来得及处理的元素因为 newLength 永远不大于 i。理解了这一点很多“原地算法”题你就开窍了。5.2 原地逆置空间复杂度为 O(1) 的经典操作顺序表逆置指的是把数组元素从头到尾反过来比如 [1,2,3,4] 变成 [4,3,2,1]。最简单直接的方法是申请一个等长新数组从后往前拷贝但空间复杂度是 O(n)。考研题目常要求“原地逆置”做法是双指针从两头往中间走交换元素void reverseList(SeqList *list) { int i 0; int j list-length - 1; while (i j) { int temp list-data[i]; list-data[i] list-data[j]; list-data[j] temp; i; j--; } }这个算法的精妙之处在于它只用了 O(1) 额外空间却完成了整个表的逆序。考试和实验里经常会在此基础上做变体比如“将顺序表中前 k 个元素和后 n-k 个元素互换位置”即循环移位。解决这类题有个经典技巧三次逆置法。假设顺序表为 [1,2,3,4,5,6,7]要把前 3 个元素移到末尾得到 [4,5,6,7,1,2,3]。做法是先把整个表逆置为 [7,6,5,4,3,2,1]再把前 4 个元素逆置为 [4,5,6,7]再把后 3 个元素逆置为 [1,2,3]拼起来正是 [4,5,6,7,1,2,3]。这种分治逆置的思路在算法题里特别常用理解了就能应对一大类“局部旋转”的题目而不是每个变体都硬背代码。5.3 有序表合并去重一个综合应用实例我强烈建议你把“有序表合并去重”当作一个综合练习来做。这个练习融合了双指针扫描、有序表归并、去重判断三个知识点非常能检验你对顺序表的掌握程度。题目给定两个非递减有序顺序表 A 和 B合并为一个新的非递减有序顺序表 C并且要求 C 中不含重复元素。思路双指针扫描 A 和 B每次取较小的那个元素如果该元素和 C 的最后一个元素相同就跳过不插入。这样一来整个算法只需要 O(mn) 时间而且不需要额外的哈希表。void mergeSortedUnique(SeqList *A, SeqList *B, SeqList *C) { initList(C); int i 0, j 0; while (i A-length j B-length) { int value; if (A-data[i] B-data[j]) { value A-data[i]; i; } else { value B-data[j]; j; } if (C-length 0 || C-data[C-length - 1] ! value) { append(C, value); } } while (i A-length) { if (C-length 0 || C-data[C-length - 1] ! A-data[i]) { append(C, A-data[i]); } i; } while (j B-length) { if (C-length 0 || C-data[C-length - 1] ! B-data[j]) { append(C, B-data[j]); } j; } }这个实例能跑通说明你对顺序表的基本操作、有序表归并、去重策略都形成了统一的认知不只是会背单个代码片段。注意看代码里那个C-length 0 ||判断它是为了处理表为空时访问C-data[C-length - 1]的越界问题。这种小细节平常看教程很容易一眼带过自己写的时候才会懂得它的分量。6. 动态扩容与内存分配的工程考量6.1 静态数组和动态数组的取舍教科书里为了教学方便多使用固定大小的数组定义顺序表比如int data[MAX_SIZE]。这种静态分配最简单代码可读性最高但问题也很明显容量是写死的。如果实验数据超过 MAX_SIZE程序直接报错或者数据丢失。真实工程实践里顺序表一般都是动态扩容的底层用malloc或realloc维护一个堆上的数组当元素个数逼近容量上限时申请一块更大的内存把旧数据搬过去。那为什么教学实验仍然普遍用静态数组一方面是让初学者专注于逻辑而不是内存管理另一方面是避免 realloc 使用不当带来的额外 bug 干扰学习主线。但从“应用”角度讲你如果只在实验报告里用静态数组会错失一个很重要的工程能力——动态扩容的设计。6.2 扩容策略的选择为什么常用固定倍数扩容动态扩容核心有两点扩容时机和扩容系数。扩容时机指的是当length capacity时触发扩容。扩容系数常见的是每次扩为原来的 2 倍也有用 1.5 倍的。2 倍扩容的策略平摊下来每次插入的复杂度是 O(1)这个结论在算法分析里叫“平摊分析”。为什么不用“每次多分配固定大小例如多加 10 个”的线性扩容呢因为反复扩容会导致复杂度变成 O(n)频繁搬移数据性能劣化严重。比如初始容量 10满了扩容到 20再满扩到 40、80……这个过程中元素搬移次数总和约为 2n平摊到 n 次插入就是 O(1)。如果固定每次扩容 10 个那么扩容频率会变得很高搬移总次数约为 n²/10平摊到每次插入就是 O(n) 了。这解释了为什么需要按倍数扩容——不是因为它看起来“整齐”而是因为均摊复杂度确实最优。6.3 realloc 的一个隐蔽陷阱在 C 语言里动态扩容操作最常见的工具是realloc。但这里有一个很多人踩过坑realloc可能返回新的指针也可能返回原来的指针还可能在失败时返回 NULL。如果你直接用原来的指针接收返回值一旦失败原指针就被覆盖成 NULL旧内存又没释放直接造成内存泄漏和访问崩溃。安全写法永远是先拿临时指针接收int *newData (int *)realloc(list-data, newCapacity * sizeof(int)); if (newData NULL) { // 扩容失败保持原状返回错误码 return 0; } list-data newData; list-capacity newCapacity;这个习惯的价值不只在顺序表里体现后面你在学动态数组、栈、队列、甚至哈希表动态扩容时全部都要延续这套逻辑。顺序表是你建立这个习惯成本最低的地方因为代码量少出了问题容易排查。7. 常见问题与实验排错手册7.1 高频 bug 汇总速查表很多实验报告的重头戏其实是排错过程。下面是我看到过最多的几类问题按出现频率排个序问题描述原因分析解决方法输出结果总是多一个 0 或垃圾值访问了未初始化的数组区域比如打印时循环到 capacity 而不是 length打印和遍历统一用 length 控制插入后中间元素丢失从前往后移动元素时覆盖了还没移动的数据从后往前移动元素删除后末尾元素重复删除元素时没有减少 length或者没有置空逻辑尾部删除后 length--不必物理清除残留值二分查找返回 -1 但数据明明存在while 条件写成 low high 或更新边界时写错 mid±1用 low high边界更新为 mid1 / mid-1realloc 后访问崩溃直接拿原指针接 realloc 返回值导致原指针变 NULL采用临时指针接收返回值并集结果出现重复元素contains 判断范围写错比如只查了 A 的长度仔细确认查重范围是否为 result 当前的全部元素这表里的每一行几乎都是我在实际写代码时碰过的墙。最典型的“从前往后移动元素导致覆盖”的问题我再多强调两句。7.2 插入操作中目标位置到表尾的移动方向顺序表的插入操作核心要求是在位置 pos 插入值为 value 的元素原 pos 及其之后的元素都往后挪一格。正确做法是从最后一个元素开始依次往后搬for (int i list-length - 1; i pos; i--) { list-data[i 1] list-data[i]; } list-data[pos] value; list-length;很多同学写成从 pos 开始往后搬for (int i pos; i list-length; i) { list-data[i 1] list-data[i]; }这组代码的后果是先把 data[pos] 覆盖到了 data[pos1]然后紧接着 data[pos1] 覆盖到 data[pos2]……看起来好像是在挪但由于源数据位置已经被改过了实际结果就是插入位置后面的所有元素全部变成了同一个值数据整段丢失。调试的时候你会看到非常诡异的现象后面元素全部等于 data[pos] 的旧值此时先想想是不是移动方向反了这个经验可以省下半小时排查时间。7.3 越界访问的经典调试技巧顺序表代码里最常见的越界问题就是访问 data[i] 时 i 超出 [0, length-1] 的范围。编译器不一定报错但运行结果总是错的而且时好时坏极其折磨人。我的调试建议是写代码时随手加上边界断言或者在调试阶段在关键函数入口处打印 length 和下标值。比如二分查找里如果 mid 计算成负数马上就能看出 low/high 更新出问题了。插入删除函数里检查 pos 是否满足0 ≤ pos ≤ length。很多实验课不让用调试器那就在关键函数入口把参数打出来用肉眼排错。我还有个习惯写完一段顺序表操作后先跑一个三元素的小样例手动算一遍预期输出再对照程序输出排查。样例大了反而难定位小样例一跑就漏出破绽。不管是插入删除还是集合运算、排序一开始都用最小规模的测试数据验证逻辑后面再换大数据压测。7.4 性能观察什么时候该放弃顺序表最后说一个工程层面的问题。顺序表虽然好写但如果你发现实验或项目中频繁需要在中间位置插入和删除每次数据搬移都会造成大量开销此时哪怕你的顺序表写得再精妙性能也扛不住。我做过一个模拟实验n100000 时在顺序表头部插入一个元素需要搬移 99999 个元素瞬间卡顿非常明显。这种情况下应该切换方案改用链表因为链表插入只要 O(1) 改成前后节点的链接关系不需要搬移元素。判断标准很简单如果插入删除的次数比访问多得多且插入位置偏中部或头部顺序表的 O(n) 搬移成本就成了主要矛盾这时候再用它就是不合适了。顺序表和链表没有绝对的“谁更好”只有“谁更匹配当前场景”。能把这一层想明白比背十遍定义都有用。8. 几个实验报告的扩展方向与实际体会如果你正在发愁实验报告写什么以下几个方向既不太复杂又能体现对顺序表的理解深度我自己当年做的时候也觉得很有收获第一个方向用顺序表实现一个简易的学生成绩管理系统。包含录入、按学号查找、删除、按成绩排序、统计不及格人数。这个题目不难但它能串联起顺序表的所有基本操作和数据移动技巧做完之后你会对“数据管理系统的底层其实就是个表”有很直观的感受。第二个方向两个集合的交并差运算但要求输入输出都用有序表并且合并过程用双指针法实现。可以顺便对比一下无序版和有序版的性能差距写成实验分析会让人眼前一亮。第三个方向顺序表实现多项式的存储和加法运算。多项式相加的难点在于阶数对齐和同类项合并恰好能利用有序表顺序存储的特性按指数大小排列然后用双指针遍历相加。这个题目有一定挑战性但对理解顺序表的应用边界很有帮助。第四个方向用动态扩容顺序表模拟栈的行为完成括号匹配检测。这里顺便把“栈”这个后续数据结构的基本操作也体验了一遍属于衔接性的练习。我自己的真实体会是顺序表这一章如果你只是照着书敲一遍代码做完实验就扔了那就真的太浪费了。它真正的价值不在代码本身而在于帮助你建立“数据存储和运算规则如何结合”的思维框架。后面学链表时你会发现很多问题可以类比插入删除的思路、遍历查找的思路、递归处理的思路都是从一个基础结构延伸出来的。真正的高手并不是把所有结构都背得滚瓜烂熟而是能把第一个结构玩到融会贯通然后快速迁移到其他结构上。拿我自己来说大二那会儿做完顺序表的并集实验其实程序写得也不算太漂亮但就是因为那次实验我把“查重”这个操作翻来覆去写了好几遍后来学哈希表时第一反应就是在想能不能用哈希来优化顺序表的查重。这种联想能力就是从基础应用里长出来的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑