例题描述
删除顺序表表中所有等于某给给定值的所有结点。
示例 :
- 输入:
1->2->6->3->4->5->6
, val =6
- 输出:
1->2->3->4->5
结构体定义
typedef int SLDataType;
struct SeqList{
SLDataType* array;
size_t size;
size_t capacity;
};
思路一
借助辅助空间筛选,最后搬回原链表,修改有效个数即可
- 用户先申请一段新连续的内存空间,如果申请失败,直接退出。
- 逐个遍历顺序表结点,与给定的
data
值进行比较。如果不相等,将当前结点的值拷贝放入申请的内存空间。
如果相等,则放弃本次循环而进行下一次循环(类似于跳过相同元素)。
通过计数器来改变新内存空间的下标,通过size
来约束循环次数,保证循环正好访问了全部的有效元素。
下图为遍历完毕的情形:
- 遍历完所有的元素后,将当前新内存空间的内容拷贝至原来内存,此时计数器的大小即为有效个数的大小,搬运完毕之后将顺序表的有效元素个数更新为计数器的个数
count
。
- 释放掉最开始申请的,作为辅助空间的这段内存空间。
代码实现
struct SeqList* removeElements(struct SeqList* psl, SLDataType data) {
//1. 申请空间
int *pData = (int*)malloc(sizeof(int)*psl->size);
if (pData == NULL) {
exit(0);
}
//2. 非data的元素拷贝到新空间
int count = 0;
for (int i = 0; i < psl->size; ++i) {
if (psl->array[i] != data) {
pData[count++] = psl->array[i];
}
}
//3. 把新空间内容放回原空间
memcpy(psl->array, pData, sizeof(SLDataType)*count);
psl->size = count;
//4. 释放申请的内存
free(pData);
}
分析
- 程序时间复杂度为
O(N)
,因为要遍历顺序表中所有元素。 - 空间复杂度也为
O(N)
,因为每处理一个表中元素就要创建一个变量来存储结点中的值。
【为了进一步优化程序,降低时间或空间复杂度,引入了下一种思路】
思路二
遍历顺序表,遇到与给定值相等的元素不处理,遇到非值向前搬移(不同情况步距不同)
- 通过下标进行元素遍历,通过两个计数器来标记顺序表元素
i
与有效个数点j
- 如果此值不等于给定元素,则将此元素的值存放在有效值计数器所在的下标,顺序表元素计数器
+1
,有效个数计数器也+1
,完成有效元素录入流程。
- 如果等于给定元素,则进行下一次循环,顺序表元素计数器加一,有效个数计数器保持不变,处理之后元素。
- 继续判断表中之后元素,如果相等继续执行第二步录入逻辑,此时元素的值会覆盖掉表中与给定值相等的元素,完成相等结点的删除。
- 循环结束条件:数组元素计数器到达了有效值结尾,不再进行元素值判定,此时有效值计算器下标之前所更新元素组成的新顺序表就是删除给定值之后的顺序表。完成逻辑。
代码实现
struct SeqList* removeElements(struct SeqList* psl, SLDataType data{
int j = 0;
for (int i = 0; i < psl->size; ++i) {
if (psl->array[i] != data) {
psl->array[j++] = psl->array[i];
}
}
psl->size = j;
}
分析
- 程序时间复杂度为
O(N)
,因为要遍历顺序表中所有元素。 - 空间复杂度也为
O(1)
,因为处理每个表中元素不会创建临时变量来储存,而是直接覆盖到原顺序表中,开辟内存空间为常数个
。
所以思路二相比于思路一的空间复杂度更低,代码更加高效。程序编写就是要一步一步进行优化。
这里提供上篇博客链接,此博客介绍了删除链表中等于给定值 val 的所有结点的思路,与本篇博客顺序表的讲解异曲同工但思路不同,有兴趣的读者可以点击蓝色字体跳转至上篇博客。