顺序表的基本操作,唯一难点在于[[#增删]]时,位序(从1开始)与数组下标(从0开始)不一致。在处理此两者关系时,只需记住位序物理上是数组下标的后一位。比如 L->length 作为下标,指向数组最后一位的后一位。脑袋中有这一层映射,算法就不会写错。

ADT接口

#define MaxSize 50
typedef int ElemType;

/* 顺序表定义(408 王道标准) */
typedef struct {
    ElemType data[MaxSize];
    int length;
} SqList;

/* ── 简单操作(前缀 SL_ 避免与链表函数名冲突) ── */
void    SL_InitList    (SqList *L);
void    SL_DestroyList (SqList *L);
bool    SL_ListEmpty   (SqList L);
int     SL_ListLength  (SqList L);
void    SL_PrintList   (SqList L);

/* 按位序插入/删除(位序从 1 开始) */
bool    SL_ListInsert  (SqList *L, int i, ElemType e);
bool    SL_ListDelete  (SqList *L, int i, ElemType *e);

/* 按位序取值,按值查找位序(未找到返回 0) */
bool    SL_GetElem     (SqList L, int i, ElemType *e);
int     SL_LocateElem  (SqList L, ElemType e);

增删

顺序表按位序操作(从 1 开始),但底层 data[]数组下标(从 0 开始)。所有易错点都源于这层映射:

data 下标 = 位序 - 1

插入

for(int j = L->length; j >= i; j--)
    L->data[j] = L->data[j-1];
L->data[i-1] = e;

循环中 j 不是位序,而是数组下标——它表示"待写入的位置":

j 的值 操作 含义
L->length data[length] = data[length-1] 最后一个元素右移一格
逐个右移
i data[i] = data[i-1] 把位序 i 处的元素也腾出

循环结束后,data[i-1](位序 i 对应的下标)已空出,直接写入 e

易错:若写成 j > i 配合 data[i] = e,位序 1 处的元素不会被移动,插入位置偏移一格。

删除

*e = L->data[i-1];       // 先保存被删元素
while(i < L->length) {   // i 直接当循环变量
    L->data[i-1] = L->data[i];
    i++;
}
L->length--;

取出 data[i-1] 后,i 的"位序"使命已经结束,直接复用它做前移循环的下标。每轮 data[i-1] = data[i] 就是把后一个元素覆盖到前一个位置,无需额外引入变量。

小结

插入是从后往前腾位置,删除是从前往后填空位;写对的关键就是搞清楚循环变量到底是下标还是位序。