☰
顺序表怎么实现删除操作
2026/10/15 6:14:09 网站建设 项目流程

先抓本质:顺序表"删除"到底在干什么

顺序表是连续存储的,逻辑相邻 = 物理相邻。删掉中间一个元素后,如果不管它,那个位置就出现"空洞",后面的元素下标就对不上逻辑位序了。

所以顺序表的删除永远是同一个套路:

用后面的元素覆盖前面要删的位置(或直接放弃某些位置),最后把length改小。

关键点:删除 ≠ 抹掉内存,那个格子里的数据其实还在,只是被后续元素盖掉了、或者干脆不再属于这个表(length变小了)。

所有删除题,最后都归结为两类手法:

手法何时用代价
前移覆盖要求保持元素相对次序平均移动 (n-1)/2 个元素
尾元素补位不要求次序(如删最小值)只移动 1 个

顺序表删除操作的常见类型

我按考研出题的实际分类列,一共这么几类:

类型典型题目核心手法时间
① 按位序删除删第 i 个元素(基本操作)前移O(n)
② 按值删除单个删第一个值为 x 的定位 + 前移O(n)
③ 按值删除全部删除所有值为 x 的元素前移(计数器)O(n)
④ 按值域区间删除删 s ≤ 值 ≤ t 的所有元素有序表:块前移;无序表:逐个前移O(n)
⑤ 去重删除重复元素有序表:双指针;无序表:暴力/哈希O(n) / O(n²)
⑥ 删除极值删最小值、最大值扫描 + 尾元素补位O(n)
⑦ 清空表ClearList直接length = 0O(1)

①② 按位序 / 按值删单个

按位序删(教材标准操作ListDelete):

bool ListDelete(SqList &L, int i, ElemType &e) { if (i < 1 || i > L.length) return false; // 位序合法性 e = L.data[i - 1]; // 位序 i → 下标 i-1 for (int j = i; j < L.length; j++) // 从 i 开始整体前移 L.data[j - 1] = L.data[j]; L.length--; return true; }

移动次数 =n - i:删表尾是O(1),删表头是O(n),平均(n-1)/2,所以整体O(n)。

按值删单个:先LocateElem找到位序,再调ListDelete,本质还是上面那套。


③ 删除所有值为 x 的元素(高频)

两种写法,都要背:

写法一:计数器记"保留了多少个"

void Del_x(SqList &L, ElemType x) { int k = 0; // k = 已保留的元素个数 for (int i = 0; i < L.length; i++) if (L.data[i] != x) L.data[k++] = L.data[i]; // 保留的往前压 L.length = k; }

写法二:计数器记"删掉了多少个"

void Del_x(SqList &L, ElemType x) { int num = 0, i = 0; while (i < L.length) { if (L.data[i] == x) num++; // num = 已删个数 else L.data[i - num] = L.data[i]; // 往前挪 num 位 i++; } L.length -= num; }

两个计数器含义正好相反(一个记留、一个记删),前移的下标写法也不同(data[k++]vsdata[i-num])。


④ 删除值在 [s, t] 之间的所有元素

这类题的关键分支:表是有序还是无序,解法完全不同。

(a)有序表——因为有序,满足条件的一定是连续一段,可以一次块移动搞定:

bool Del_s_t(SqList &L, ElemType s, ElemType t) { int i, j; if (s >= t || L.length == 0) return false; for (i = 0; i < L.length && L.data[i] < s; i++); // 找第一个 ≥ s if (i >= L.length) return false; // 没有元素在区间 for (j = i; j < L.length && L.data[j] <= t; j++); // 找第一个 > t for (; j < L.length; i++, j++) // t 之后的整块前移 L.data[i] = L.data[j]; L.length = i; return true; }

(b)无序表——没法块移动,只能逐个判断、逐个前移:

bool Del_s_t(SqList &L, ElemType s, ElemType t) { int i, k = 0; if (s >= t || L.length == 0) return false; for (i = 0; i < L.length; i++) { if (L.data[i] >= s && L.data[i] <= t) k++; // 命中区间,计数 else L.data[i - k] = L.data[i]; // 否则前移 } L.length -= k; return true; }

⑤ 删除重复元素(去重)

有序表去重(王道经典):相同元素必定相邻,用双指针 i(有效末尾)、j(扫描指针),一趟搞定:

bool Delete_Same(SqList &L) { if (L.length == 0) return false; int i, j; for (i = 0, j = 1; j < L.length; j++) if (L.data[i] != L.data[j]) // 和"上一个保留的"不同才算新的 L.data[++i] = L.data[j]; L.length = i + 1; return true; }

无序表去重:因为相同元素不相邻,双指针失效。要么暴力 O(n²)(每个元素和后面所有比),要么开哈希表空间换时间。这是有序/无序的核心分界点,别混用。


⑥ 删除最小值元素

王道原题的设定是:最小值唯一,用表尾元素填补空位(题目没要求保持次序,所以不必前移,省时间):

bool Del_Min(SqList &L, ElemType &value) { if (L.length == 0) return false; value = L.data[0]; int pos = 0; for (int i = 1; i < L.length; i++) if (L.data[i] < value) { value = L.data[i]; pos = i; } L.data[pos] = L.data[L.length - 1]; // 尾元素补位,而不是前移 L.length--; return true; }

注意每题审题:要求"保持相对次序"就必须前移;只说"删除"且不强调次序,就可以用补位,O(n) 且只移动一次。


⑦ 清空表

void ClearList(SqList &L) { L.length = 0; }

逻辑上表空了,但内存中的数据还在。这和"释放空间"(free)是两回事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询