顺序表的算法题通用思路

目录

一、移除元素

(一)题目

(二)解题思路

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

(一)题目

(二)解题思路

三、合并两个有序数组

(一)题目

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


一、移除元素

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

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

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遍历数组:

若nums[src] == val:src直接后移(跳过val)。

若nums[src] != val:将nums[src]赋值给nums[dst],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遍历数组

若nums[src] == nums[dst]:src后移(跳过重复项)。

若 nums[src] != nums[dst] ,说明找到新的非重复元素;dst 后移一位,准备存储新元素;若 dst和 src 不相邻 (避免无效赋值),则将 nums[src] 赋给 nums[dst];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的有效数据会被覆盖(如nums1[0]可能被nums2[0]覆盖);所以要从后往前遍历,利用nums1的空位置(后n位),避免覆盖。

2、指针分工

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

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

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

3、步骤

**(1)**比较nums1[l1]和nums2[l2],将较大值放入nums1[l3]。

**(2)**对应指针左移(如放nums1[l1]则l1--,放nums2[l2]则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--];
    }
}
相关推荐
寻寻觅觅☆10 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
YJlio10 小时前
1.7 通过 Sysinternals Live 在线运行工具:不下载也能用的“云端工具箱”
c语言·网络·python·数码相机·ios·django·iphone
偷吃的耗子11 小时前
【CNN算法理解】:三、AlexNet 训练模块(附代码)
深度学习·算法·cnn
2013编程爱好者11 小时前
【C++】树的基础
数据结构·二叉树··二叉树的遍历
NEXT0611 小时前
二叉搜索树(BST)
前端·数据结构·面试
化学在逃硬闯CS11 小时前
Leetcode1382. 将二叉搜索树变平衡
数据结构·算法
ceclar12312 小时前
C++使用format
开发语言·c++·算法
Gofarlic_OMS12 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
夏鹏今天学习了吗12 小时前
【LeetCode热题100(100/100)】数据流的中位数
算法·leetcode·职场和发展
忙什么果13 小时前
上位机、下位机、FPGA、算法放在哪层合适?
算法·fpga开发