线性表与链表(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]

相关推荐
老鼠只爱大米2 小时前
LeetCode经典算法面试题 #142:环形链表 II(哈希表、快慢指针等多种方法详细解析)
算法·leetcode·链表·快慢指针·floyd算法·环形链表
小龙报2 小时前
【C语言进阶数据结构与算法】LeetCode27 && LeetCode88顺序表练习:1.移除元素 2.合并两个有序数组
c语言·开发语言·数据结构·c++·算法·链表·visual studio
Script kid3 小时前
Redis(Remote Dictionary Server远程字典服务器)五种常见数据结构及常见用法和指令
服务器·数据结构·redis
范纹杉想快点毕业3 小时前
C语言查找算法对比分析
数据结构·算法
好奇龙猫3 小时前
【大学院-筆記試験練習:线性代数和数据结构(14)】
数据结构
Remember_9933 小时前
【数据结构】深入理解Map和Set:从搜索树到哈希表的完整解析
java·开发语言·数据结构·算法·leetcode·哈希算法·散列表
TTGGGFF3 小时前
控制系统建模仿真(二):掌握控制系统设计的 MAD 流程与 MATLAB 基础运算
开发语言·数据结构·matlab
金融小师妹4 小时前
基于AI多模态分析的日本黄金储备60%跃升研究:外汇结构重构与资产价格联动机制解构
大数据·数据结构·人工智能·深度学习
重生之后端学习4 小时前
25. K 个一组翻转链表
java·数据结构·算法·leetcode·职场和发展