顺序表的算法题通用思路

目录

一、移除元素

(一)题目

(二)解题思路

二、删除有序数组中的重复项

(一)题目

(二)解题思路

三、合并两个有序数组

(一)题目

(二)解题思路(三指针法)


一、移除元素

(一)题目
(二)解题思路

关于顺序表算法题的解题思路:下策遍历中策新数组上策原数组操作

1、下策:纯遍历

① 遍历数组,当发现目标值 val 时,用临时变量保存该值。

② 将 val 后面的所有元素依次向前移动一位,覆盖掉当前的 val。

③ 把临时变量中保存的 val 放到数组的最后位置。

④ 重复上述过程,直到遍历完数组,最终数组前面部分就是移除 val 后的有效元素,后面部分是被移到末尾的 val。

这样虽然可以完成目标,但是时间复杂度为O(n²),效率不高。

cpp 复制代码
int removeElement(int* nums, int numsSize, int val) 
{
    int size = numsSize;  // 记录有效元素长度
    
    // 外层循环遍历所有元素,将i++移到for循环的更新部分
    for (int i = 0; i < size; i++) 
    {
        if (nums[i] == val) 
        {
            
            // 找到目标值,内层循环将后续元素前移一位
            for (int j = i; j < size - 1; j++) 
                nums[j] = nums[j + 1];  // 逐个覆盖

            size--;  // 有效长度减1
            i--;     // 关键:当前位置元素被替换,需要重新检查,因此i回退1
        }
    }
    
    return size;
}

2、中策:新数组

① 新建数组:创建一个临时数组,用于存储原数组中不等于目标值 val 的元素。

② 遍历原数组:逐个检查原数组元素,若元素不是 val,就将其加入临时数组。

③ 覆盖原数组:将临时数组中的元素按顺序复制到原数组的起始位置,完成后原数组前半部分就是过滤掉 val 后的结果。

这样子时间复杂度就被优化到了O(n),但是空间复杂度是O(n),所以还并不是最佳的。

cpp 复制代码
int removeElement(int nums[], int numsSize, int val) 
{
    // 处理数组为空的特殊情况(numsSize = 0)
    if (numsSize == 0) 
        return 0;
    
    // 临时数组,此时numsSize至少为1,避免长度为0
    int tempArr[numsSize];
    int j = 0; // 临时数组的下标
    
    // 遍历原数组,收集非 val 元素
    for (int i = 0; i < numsSize; i++) 
    {
        if (nums[i] != val) 
            tempArr[j++] = nums[i];
    }
   
    // 将临时数组元素覆盖到原数组前 j 个位置
    for (int i = 0; i < j; i++) 
        nums[i] = tempArr[i];

    // 返回新的有效长度
    return j;
}

3、上策:在原数组操作

(1)解题思路:双指针法

**(2)核心思想:**用两个指针(src、dst)遍历数组,src探路找 "非val值",dst占位存 "非val值",实现 "原地移除"(无需额外数组)。

(3)指针分工

**① src(源指针):**从左到右遍历数组,负责 "找" 非val值。

**② dst(目标指针):**从左到右移动,负责 "存"src找到的非val值。

(4)步骤

初始化src = 0,dst = 0

src遍历数组:

若numssrc == val:src直接后移(跳过val)。

若numssrc != val:将numssrc赋值给numsdst,dst后移,src后移。

遍历结束,dst的值即为有效元素个数k。

(5)代码实现

cpp 复制代码
int removeElement(int* nums, int numsSize, int val) 
{
    int dst = 0;
    int src = 0;
    for (src = 0; src < numsSize; src++) 
    {
        //src的值为val,src++
        //src的值部位val,赋值再++
        if (nums[src] != val) 
        {
            nums[dst] = nums[src];  
            dst++;
        }
    }
    return dst;  // dst即为有效元素个数k
}

二、删除有序数组中的重复项

(一)题目
(二)解题思路

1、核心思想

利用 "有序数组重复项必相邻" 的特性,用 dst 标记 "非重复元素的末尾位置",src遍历找 "下一个非重复元素"。

2、指针分工

**(1)dst(目标指针):**初始为 0,指向当前最后一个非重复元素。

**(2)src(源指针):**初始为 1,从第二个元素开始遍历,找与dst指向元素不同的值。

3、步骤

**(1)**若数组为空,直接返回 0。

**(2)**初始化dst=0,src=1。

**(3)**src遍历数组

若numssrc == numsdst:src后移(跳过重复项)。

若 numssrc != numsdst ,说明找到新的非重复元素;dst 后移一位,准备存储新元素;若 dst和 src 不相邻 (避免无效赋值),则将 numssrc 赋给 numsdst;src 始终后移,继续遍历。

遍历结束,返回dst+1(dst是下标,长度需 + 1)。

4、代码实现

cpp 复制代码
int removeDuplicates(int* nums, int numsSize) 
{
    if (numsSize == 0) 
        return 0;  // 处理空数组
    
    int dst = 0;
    int src = dst + 1;
    
    while(src < numsSize) 
    {
        if (nums[src] != nums[dst]) {
            dst++;  // 目标指针后移
            // 只有当两个指针不相邻时才需要赋值(避免自己赋给自己)
            if (dst != src) 
                nums[dst] = nums[src];
        }
        src++;
    }
    return dst + 1;  // 返回非重复元素个数
}

(1)​时间复杂度:O (n)(仅遍历数组 1 次)。

(2)空间复杂度:O (1)(无额外申请空间,原地操作)。

三、合并两个有序数组

(一)题目
(二)解题思路(三指针法)

1、核心思想

若从前往后遍历,nums1的有效数据会被覆盖(如nums10可能被nums20覆盖);所以要从后往前遍历,利用nums1的空位置(后n位),避免覆盖。

2、指针分工

**(1)l1:**指向nums1有效数据的末尾(初始为m-1)。

**(2)l2:**指向nums2的末尾(初始为n-1)。

**(3)l3:**指向nums1合并后的末尾(初始为m+n-1)。

3、步骤

**(1)**比较nums1l1和nums2l2,将较大值放入nums1l3

**(2)**对应指针左移(如放nums1l1则l1--,放nums2l2则l2--),l3始终左移。

**(3)**若 nums2 先遍历完 (l2 < 0),结束;若nums1先遍历完 (l1 < 0),将 nums2 剩余元素直接拷贝到 nums1 前端。

4、代码实现

cpp 复制代码
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) 
{
    // 初始化三个指针
    int l1 = m - 1;    // nums1有效数据末尾
    int l2 = n - 1;    // nums2末尾
    int l3 = m + n - 1;// nums1合并后末尾
    
    // 第一步:合并两个数组的有效数据
    // 只要有一个不满足条件,就不可以进入循环,有一个条件为假就不可以
    // 都为真才可以进入循环
    // l3--;是先用后减,等价于省略了l3--
    while (l1 >= 0 && l2 >= 0) {
        if (nums1[l1] > nums2[l2]) 
            nums1[l3--] = nums1[l1--];
        else 
            nums1[l3--] = nums2[l2--];
    }

    // 第二步:处理nums2剩余元素(若nums1先遍历完)
    // l1越界导致上面循环停止的话,那么也就完成了
    // 如果是l2越界导致上面循环停止的话,那么nums2的剩余内容到nums1中去
    while (l2 >= 0) {
        nums1[l3--] = nums2[l2--];
    }
}
相关推荐
金融小师妹14 分钟前
AI因子共振模型显示:金银比突破区间上沿,白银定价逻辑进入再校准阶段
人工智能·算法·均值算法·线性回归
不会就选b14 分钟前
数据结构之链表OJ题(中)
数据结构·链表
J2虾虾23 分钟前
C语言 typedef 用法
c语言·数据结构·算法
hunterkkk(c++)32 分钟前
线段树例题
算法
故渊at42 分钟前
第二板块:Android 四大组件标准化学理 | 第七篇:Activity 页面载体与任务栈算法
android·算法·生命周期·activity·任务栈
兰令水1 小时前
leecodecode【区间DP+树形DP】【2026.6.10打卡-java版本】
java·算法·leetcode
budingxiaomoli1 小时前
二叉树中的深搜
数据结构
weixin199701080161 小时前
[特殊字符] 1688开放平台API Sign签名算法详解(Java / Python / PHP 实现)
java·python·算法
断点之下1 小时前
数据结构从零开始④:堆——一种特殊的完全二叉树(附堆排序、TopK问题)
数据结构