在介绍了线性表的顺序存储结构和基本运算的实现后,我们可以根据这种实现的思维来解决相关的应用问题,比如删除元素,通过一个例子来说明。
例1——删除元素
问题:已知长度为n的线性表A采用顺序存储结构,设计算法,删除线性表中所有值为x的数据元素。
要求:时间复杂度为O(n)、空间复杂度为O(1)的算法
第一种解法用基本运算来实现,代码如下:
void Remove_node(SqList* sqlist , int element)
{
if(NULL == sqlist)
{
return;
}
//pos记录要删除的元素下标索引
int pos;
//通过Select_List查找要删除的元素element,并返回该元素的位置pos
while((pos = Select_List(sqlist, element)) >= 0)
{
//然后根据位置pos删除元素
RemoveByPos_List(sqlist , pos);
}
}
对于这个算法来说, Remove_node操作在删除元素时还要去循环移动元素的位置,所以它的时间复杂度为O(n2)、空间复杂度为O(1),而我们的要求是时间复杂度为O(n),显然这个算法的时间复杂度不符合要求。
在这个删除元素的例子中,我们需要明白一点:在实际解决问题中,很多情况下我们用同样的思维方式去考虑问题,但是不一定用所有的基本运算组合起来去解决问题,因为有时候这样去做的话,并不能很好的解决一些问题。因此应该根据实际的情况选择合适的算法,而并不是一味地去用基本运算
。
在这一篇中,我们不仅要学习删除元素的算法,还有删除元素的算法背后解决问题的过程和思维方式,这也是我们需要学习并磨练的地方。
数据结构本身是一门灵活性非常强的学科,因此我们在学习已有的知识的基础上,应该多加思考,尽量去扩展自己的思维,也就是说,当我们面对一个问题的时候,能做到举一反三,在提出一个解法时,应该思考一下:是否还有更好的解法。
因此我们可以用第二种解法,复制要保留的元素:
图2-第二种解法
假设现在我们要删除线性表L中所有为2的数据元素,那么可以通过复制要保留的元素依次保存到线性表L1中,也就是说对于要删除的所有为2的数据元素,不要复制到线性表L1中。在这里实际上L和L1之间是共享存储空间的,因此算法的时间复杂度为O(n),空间复杂度为O(1)。
第二种解法的思想:
1.逐个复制要保留的元素
2.要点:L1和L共用空间,即不需要额外空间
第二种解法的具体实现代码:
//x为要删除的数据元素
void Remove_node2(SqList* sqlist , int element)
{
//size记录非x的元素个数
int size = 0;
int i;
for(i = 0; i < sqlist->length; i++)
{
//把不重复的数据元素复制到顺序表L1中
if(sqlist->DataAddr[i] != element)
{
sqlist->DataAddr[size] = sqlist->DataAddr[i];
size++;
}
}
//然后把不重复的元素个数赋值给length
sqlist->length = size;
}
测试代码:
#define _CRT_SECURE_NO_WARNINGS
#include "sqlist.h"
int main(void)
{
//初始化顺序表
SqList *sqlist = Init_List();
int i;
for(i = 0; i < 6; i++)
{
if(i % 2 == 0)
{
Insert_Element_List(sqlist,2);
continue;
}
Insert_Element_List(sqlist,i);
}
printf("------------打印全部元素---------------\n");
//打印全部的元素
Print_List(sqlist);
printf("------------删除所有值为2的元素-----------\n");
//第一种解法
//Remove_node(sqlist, 2);
//第二种解法
Remove_node2(sqlist, 2);
Print_List(sqlist);
return 0;
}
测试结果:
从测试的结果来看,第二种解法同样能达到第一种解法的效果,同时还能降低时间复杂度。