二、顺序表插入和删除(数据结构)
1. 顺序表的基本操作----插入
- 顺序表是一种数据结构,其元素在内存中连续存储,并且可以使用下标直接访问每个元素。在顺序表中插入或删除元素时,需要移动后续元素的位置以保持顺序表的有序性。
- 对于在下标位置i处插入值为x的元素,需要将下标为i~n-1的所有元素向右移动一个位置,然后将x插入到位置i的元素中。
- 对于删除操作,需要将下标为i+1~n-1的所有元素向左移动一个位置,然后将下标为n-1的元素置空。
- 需要注意的是,当动态扩展顺序表时,需要重新分配内存空间,因为当前内存空间可能已经被占满了。同时,为了减少多次扩充内存空间的开销,可以考虑一次性分配更大的内存空间。
#define MaxSize 10 //定义最大长度
typedef struct
{
int data[MaxSize]; //用静态的“数组”存放数据元素
int length; //顺序表的当前长度
}SqList; //顺序表的类型定义
void ListInsert(SqList &L, int i, int e)//在顺序表L的位序i处插入元素e
{ 。
//j大于等于3时,循环一直继续,data[4]放到data[5],data[3]放到data[4]统统后移一位。
for (int j = L.length; j >= i; j--) //将第i个元素及之后的元素后移
L.data[j] = L.data[j-1]; //插入位置后的元素后移,给插入位置腾出空间
L.data[i-1] = e; //在位置i处放入e,位序为3处插入,则在数组(从0开始数)中表示第3-1位置处。
L.length++; //长度加1
}
int main()
{
SqList L; //声明一个顺序表
InitList(L); //初始化顺序表
//.......插入几个元素 假设插入1 2 4 5 6五个元素,则length = 5
ListInsert(L, 3, 3);//往第3个位置插入数据元素3 ---- 1 2 e 4 5 6 位序3后的元素4 5 6后移
return 0;
}
- 注意位序、数组下标的关系,并从后面的元素依次移动。
注意: - 顺序表长度为5,在位序3处插入一个元素e,长度变为6。如上图所示,顺序表中各个数据元素必须是连续相邻的,而data[6]~data[9]之间都是空的,若此时要在位序9处插入元素3:**ListInsert(L, 9, 3);**是错误的操作。
- 这样的话data[6]、data[7]中是空的,为无效操作,即这段代码是不够健壮的。此时可以加入条件判断语句。判断i的值是否合法,i 的合法值范围应该是 [1, length+1]。如果有人想往第九个位置插入元素,那么 i 的值超出合法范围。
- 检查顺序表是否已经存满了,如果已满,那也不应该继续插入元素。
可以给代码加入反馈,判断插入操作是否满足当前条件,并给出反馈:
bool ListInsert(SqList &L, int i, int e)
{
if (i < 1 || L.length + 1) //判断i的范围是否有效
return false;
if (L.length >= MaxSize) //当前存储空间已满,不能插入
return false;
for (int j = L.length; j > i; j--) //将第i个元素及之后的元素后移一位
L.data[j] = L.data[j-1];
L.data[i - 1] = e; //在位置i处放入e
L.length++; //添加一个元素,长度加 1
return true;
//好的算法,应该具有“健壮性” 能处理异常情况,并给使用者反馈
如下图:顺序表长度已满,不能再插入数据元素。
插入操作的时间复杂度:最好、最坏、平均
2. 顺序表的基本操作-----删除
注意位序、数组下标的关系,并从前面的元素依次移动,思考如果参数没有引用会怎么样?
加入引用,是为了确保将ListDelete()函数与main函数中的操作的相同数据类型如变量e,L在同一内存空间中,所对应的是同一份数据,而不是其中一方的复制体。
bool ListDelete(SqList &L, int i, int &e) //使用&e,则其与main函数中变量e在内存当中对应的是同一份数据。
{
if (i < 1 || i > L.length) //判断i的范围是否有效
return false;
e = L.data[i - 1]; //将被删除的元素赋值给e
for(int j = i; j < L.length; j++) //将第i个位置后的元素前移
L.data[j-1] = L.data[j];
L.length--; //线性表长度减 1
return true;
}
int main()
{
SqList L; //声明一个顺序表
InitList(L); //初始化顺序表
// .....此处省略一些代码, 插入几个元素
int e = -1; //用变量e把删除的元素“带回来”
if (Listdelete(L, 3, e)) //接收函数返回值,判断是否返回false or true
printf("已经删除第3个元素,删除元素值为= %d\n", e);
else
print("位序i不合法,删除失效\n");
return 0;
}
进行删除操作时,data[2]中数据被删除,那么先移动前面的元素,将data[3]移动到data[2]、data[4]移动到data[3]。
3. 删除操作的时间复杂度
4. 知识点思维导图
注意:更改length的大小,
在进行顺序表的插入和删除操作时,有一些注意事项和要点需要考虑:
-
插入和删除的位置合法性:在插入或删除元素之前,需要检查插入或删除位置的合法性。如果位置超出了顺序表的有效范围,需要给出错误提示并终止操作。
-
移动元素:在插入和删除操作中,需要移动其他元素以腾出位置或保持有序性。移动元素时,要注意避免数据丢失或覆盖。可以从最后一个元素开始移动,这样可以保证数据不会被覆盖。
-
插入时的内存分配:如果顺序表已经占满,需要进行动态扩展,并重新分配内存空间。可以考虑一次性分配更大的内存空间,以减少频繁扩充内存的开销。
-
删除元素的处理:删除元素后,需要将最后一个元素置空或作特殊处理,避免出现悬挂指针的问题。
-
时间复杂度:顺序表的插入和删除涉及到移动元素,因此其时间复杂度为O(n),其中n是顺序表的长度。如果需要频繁进行插入和删除操作,可能需要考虑其他更高效的数据结构,如链表。