线性表与链表(part 1)

Day 1:快慢指针的原理

408 改卷中,线性表的大题往往要求 "时间复杂度 O(n)""空间复杂度 O(1)"。意味着都是原地解决问题。

1.0 标准静态分配定义

cpp 复制代码
#define MaxSize 50
typedef struct{
  int data[MaxSize]; //存放数据元素
  int length;        //当前长度
}SqList;

注意

  1. 它是 SqList (Sequence List),不是链表。

  2. 数组下标从 0 开始,长度是 length,最后一个元素是 data[length-1]

2.0 顺序表的逆置

ps:两种写法都是原地逆置 (不需要额外开辟数组空间),时间复杂度都是 O(n)

题目:将顺序表 L {8,7,9,6,3}中的所有元素原地逆置。(这里的顺序表有分奇数和偶数)

考点:下标控制。

奇数或者偶数这么写行,他们的j指针指向不同。(双指针写法)

Reverse_L(SqList *L):操作顺序表 L 的逆置函数

同样支持奇数 / 偶数长度:条件就是 Length/2 一半

cpp 复制代码
// 核心逻辑:首尾交换,向中间靠拢
void Reverse(SqList *L) {
    int temp; 
    // i 控制前半段,L->length - 1 - i 是对应的后半段
    for (int i = 0; i < L->length / 2; i++) {
        temp = L->data[i];
        L->data[i] = L->data[L->length - 1 - i];
        L->data[L->length - 1 - i] = temp;
    }
}

易错点 :循环条件是 i < L->length / 2。如果写成 < length,换完又换回去了,等于没变。

3.0 删除值为x的元素(区间)

这边我们心中必须要有抽象的数组画面:

cpp 复制代码
// 把 k 作为参数传入
void Function(Sqlist &L, int k)
{
    int c=0;
    for(int i=0; i<L.length; i++)
    {
        if(L.data[i] != k)
        {
            L.data[c++] = L.data[i];
        }
    }
    L.length = c;
}

这里定义的c实际上就好像拿一个 "空筐",挨个检查顺序表里的元素:是k就扔掉,不是k就放进筐里,放一个就把筐往后挪一位,最后告诉顺序表:"你的有效元素就只有筐里这些,数量是c"。

如果这里我想要;

题目:删除顺序表中值在给定值 s 与 t 之间(包含 s, t)的所有元素。

要求:s< t,若 s, t不合理或表空,显示出错信息。

思考 : 这道题和"删除 k"有区别吗? 本质完全一样! "删除 k"是把 != k的留下来。 "删除 [s, t]"是把 k < s || k > t 的留下来。

bash 复制代码
// 删区间的完整函数(基于你的原代码修改)
void Function(Sqlist &L, int s, int t)
{
    // 新增:合法性检查(题目要求)
    if (L.length == 0) {
        printf("错误:顺序表为空!\n");
        return;
    }
    if (s >= t) {
        printf("错误:区间不合法(s >= t)!\n");
        return;
    }

    // 核心逻辑(仅改了if条件)
    int c=0;
    for(int i=0; i<L.length; i++)
    {
        // 原:if(L.data[i] != k)
        // 改:保留不在[s,t]区间的元素
        if(L.data[i] < s || L.data[i] > t)
        {
            L.data[c++] = L.data[i];
        }
    }
    L.length = c;
}

4.0 有序顺序表原地去重

题目 :从有序顺序表中删除所有重复的元素,使表中所有元素的值均不同。

难点解析:

因为是有序的,所以相同的元素一定挨在一起(如 1, 2, 2, 3)。

如果是无序表,O(n) 时间内无法原地去重(需要 Hash 表,空间 O(n)),所以考试通常考有序。

其实要我来看有序顺序表去重和之前的删除操作核心思路完全一致 ------ 都是用 "双指针筛选有效元素"

  1. 处理特殊情况 :如果顺序表为空(L.length == 0)或只有 1 个元素(L.length == 1),直接返回(本身无重复);

  2. 定义双指针

    • j:记录已去重后的最后一个元素的下标(初始化为 0,因为第一个元素必然保留);

    • i:遍历原顺序表的指针(从 1 开始,因为第 0 个元素已经在去重结果里);

  3. 筛选不重复元素 :遍历过程中,若L.data[i] != L.data[j](当前元素和已保留的最后一个元素不重复),就把L.data[i]放到j+1的位置,然后j++(更新去重后的最后一个元素下标);

  4. 更新顺序表长度 :遍历结束后,去重后的元素个数是j+1(因为j是下标),所以L.length = j + 1

代码实现(和之前删除逻辑的对比)
bash 复制代码
void RemoveDuplicates(Sqlist &L) {
    // 特殊情况:空表或只有1个元素,无需去重
    if (L.length <= 1) return;

    int j = 0; // 已去重的最后一个元素下标(初始保留第0个元素)
    for (int i = 1; i < L.length; i++) {
        // 筛选条件:当前元素 != 已保留的最后一个元素(因为有序,重复必连续)
        if (L.data[i] != L.data[j]) {
            L.data[++j] = L.data[i]; // 放到j的下一个位置,再更新j
        }
    }
    L.length = j + 1; // 去重后的长度是j+1(下标j对应第j+1个元素)
}

初始顺序表[1,2,2,3]

  • 初始L.length=4j=0(保留第 0 个元素1),i从 1 开始遍历:
  1. i=1,元素是 2 :对比L.data[1]=2L.data[j=0]=1 → 不重复,执行L.data[++j] = 2(j 变成 1),数组变成[1,2,2,3](无变化);

  2. i=2,元素是 2 :对比L.data[2]=2L.data[j=1]=2 → 重复,不操作;

  3. i=3,元素是 3 :对比L.data[3]=3L.data[j=1]=2 → 不重复,执行L.data[++j] = 3(j 变成 2),数组变成[1,2,3,3]

遍历结束后,j=2,所以去重后的长度是j+1=3,最终有效元素就是前 3 个:[1,2,3]

相关推荐
我能坚持多久16 小时前
【初阶数据结构01】——顺序表专题
数据结构
rainbow688917 小时前
深入解析C++STL:map与set底层奥秘
java·数据结构·算法
果果燕18 小时前
今日学习笔记:双向链表、循环链表、栈
笔记·学习·链表
wangjialelele18 小时前
平衡二叉搜索树:AVL树和红黑树
java·c语言·开发语言·数据结构·c++·算法·深度优先
xuxie9918 小时前
day 21 双向链表以及循环链表
数据结构·链表
历程里程碑19 小时前
普通数组----合并区间
java·数据结构·python·算法·leetcode·职场和发展·tornado
梵刹古音20 小时前
【C语言】 指针与数据结构操作
c语言·数据结构·算法
爱敲代码的TOM21 小时前
数据结构总结
数据结构
皮皮哎哟1 天前
数据结构:嵌入式常用排序与查找算法精讲
数据结构·算法·排序算法·二分查找·快速排序
堕2741 天前
java数据结构当中的《排序》(一 )
java·数据结构·排序算法