PTA(顺序表)1-2 数组元素的区间删除
PTA(顺序表)1-2 数组元素的区间删除
zhangzhang1-2 数组元素的区间删除
分数 5
作者 DS课程组
单位 浙江大学
给定一个顺序存储的线性表,请设计一个函数删除所有值大于min而且小于max的元素。删除后表中剩余元素保持顺序存储,并且相对位置不能改变。
函数接口定义:
1 | int Delete( int A[], int L, int minA, int maxA ); |
其中A是整型数组,存储原始线性表的元素;L是表长,即A中元素的个数;minA和maxA分别为待删除元素的值域的下、上界。函数Delete应将A中所有值大于minA而且小于maxA的元素删除,同时保证表中剩余元素保持顺序存储,并且相对位置不变,最后返回删除后的表长。
裁判测试程序样例:
1 |
|
输入样例:
1 | 10 |
输出样例:
1 | 4 -8 12 5 9 10 |
答案
1 | int Delete( int A[], int L, int minA, int maxA ) { |
太妙了:
- 如果符合要删的,就删掉,下一位要看的往前了,i不变
- 如果不符合要删的,就i++,看下一位
妙在用 “原位覆盖 + 单次遍历” 的极简逻辑,完美平衡了 “正确性、效率、空间复杂度”,没有多余操作,精准解决了 “删除区间元素 + 保持顺序” 的核心需求。具体妙点可拆解为 3 个维度:
一、逻辑极简:用 “遍历 + 条件保留” 替代复杂删除,思路直观
普通初学者可能会先标记所有要删除的元素,再统一移动剩余元素,或用额外数组存储保留元素 —— 但这个答案直接在原数组上操作,核心逻辑只有 “一句话”:
“遍历数组,遇到要删除的元素就用后续元素覆盖它(相当于删除),遇到要保留的元素就往后走”。
代码里的 while (i != L) 循环是核心:
- 当
A[i]是 “要删除的(minA <= A[i] <= maxA)”:用j循环把i后面的元素往前挪 1 位(覆盖A[i]),同时表长L--(因为删了一个元素),且i不递增(因为覆盖后A[i]变成了原A[i+1],需要重新判断这个新元素)。 - 当
A[i]是 “要保留的”:i++继续判断下一个元素。
整个过程像 “筛选” 一样,把不需要的元素直接 “过滤掉”,保留的元素自然维持原相对顺序,逻辑非常直接,没有多余步骤。
二、空间最优:原地操作,不占额外空间
很多人会想到用一个 “临时数组” 存储所有要保留的元素,最后再把临时数组复制回原数组 —— 这种方法需要额外开辟 O(L) 的空间(比如 int temp[MAXN])。
而这个答案完全在原数组 A 上操作,没有定义任何额外数组或大变量,空间复杂度是 O(1)(仅用了 i 和 j 两个循环变量),完美贴合 “顺序存储线性表” 的原地操作特性,也符合题目隐含的 “空间效率” 要求。
三、效率合理:避免重复遍历,时间复杂度可控
虽然代码里有两层循环(外层 while + 内层 for),但本质上每个元素最多被 “移动” 一次:
- 要删除的元素会被后面的元素覆盖,覆盖后就不会再被处理;
- 要保留的元素只会被 “判断一次”,不会被重复移动。
比如输入样例中,要删除的元素是 2、1、3、3,每个被删除元素后面的元素只需要往前挪 1 次,整个过程的总操作次数和 “元素总数” 成正比,实际时间复杂度接近 O(L)(比 “先标记再统一删除” 的两次遍历更高效)。
对于题目中 L 最大为 20(MAXN=20)的规模,这种效率完全足够,且代码比 “双指针优化版” 更简洁(双指针虽能做到严格 O(L),但需要额外维护 “保留位置指针”,对初学者来说不如这个答案直观)。
总结:妙在 “贴合问题规模的最优解”
这个答案没有追求 “极致的时间复杂度”(比如用双指针),也没有用 “简单但费空间的临时数组”,而是选择了最适合本题场景的实现方式:
- 逻辑上,初学者能一眼看懂 “删元素就往前挪” 的直观操作;
- 代码上,行数极少(核心逻辑 10 行内),没有冗余变量;
- 性能上,完全满足
MAXN=20的规模需求,且原地操作无额外空间消耗。


