【C/C++练习】经典的快慢指针问题---移除元素

简介: 【C/C++练习】经典的快慢指针问题---移除元素

77eaa8bfa8524a63a69214c39bd6d414.gif

📖题目描述

0f05589b482e43b8a8ad293f209788fa.png


题目出处:移除元素

🔖示例

246d6c31f0584e5fb5966a821f9b4aa2.png

📖题解

 对于本题我将按照由易到难的顺序为大家分享三种解题思路,并逐一分析它们的优劣,以及注意事项。

🔖思路一:暴力求解

 我想暴力求解应该是第一次接触到此题的小伙伴们最先想出来的办法吧。这道题目暴力求解就是去遍历数组,当遇到数组元素等于val的时候,将后面的所有元素往前挪动一位,把val覆盖掉以实现移除的效果。具体过程如下动图所演示:

ccd62b880abc4fb39eb909ee4cc43456.gif

代码实现:

int removeElement(int* nums, int numsSize, int val)
{
    int i = 0;
    int len = numsSize;
    while(i < len)
    //循环控制变量用len,因为如果有重复,就会往前覆盖
    {
        if (nums[i] == val)
        {
            int j = 0;
            for (j = i; j < len; j++)
            //把后面的往前覆盖
            {
                if (j != numsSize - 1)
                {
                    nums[j] = nums[j + 1];
                }
            }
            len--;
            //找到一个之后跳出循环,继续从下标为0的数字开始检查
            }
        else
        {
            i++;
        }
    }
    return len;
}

需要注意:i是用来遍历整个数组的,当nums[i] == val的时候停下来,把i后面的所有数据往前挪动一位,且数组的有效长度len减一。挪动完后i的值不变,因为此时i位置上的元素是它后一位经过挪动覆盖过来的,我们并不知道他是否等于val,因此需要继续判断num[i]是否等于val,如果不等于再让i++继续去遍历后面的元素,直到i等于数组的有效长度,此时说明已经遍历结束。

复杂度分析:

 时间复杂度要讨论最坏的情况,对于本题最坏情况就是数组中所有的元素都等于val,假设数组一共有N NN个元素,那么将第一个元素移除需要把后面的N − 1 N-1N−1个元素全部往前挪动一位,也就是挪动N − 1 N-1N−1次,由于数组元素全是val所以经过挪动后第一个元素任然是val,但此时数组的有效元素个数已经变成了N − 1 N-1N−1,所以此时需要挪动N − 2 N-2N−2…以此类推总的挪动次数就是一个等差数列求和,因此时间复杂度就是O ( N 2 ) O(N^2)O(N 2 )

🔖思路二:空间换时间

 如果对空间复杂度没有要求的情况下,我们可以创建一个新数组,然后去遍历原数组,如果num[i]不等于val就把他拷贝到新数组,如果等于那就跳过并且有效数据个数减一,直到遍历结束,然后把新开数组的内容拷贝回原数组。具体过程如下动图所示:


89664d15c0204f098aedf62c13bef11e.gif

代码实现:

int removeElement(int* nums, int numsSize, int val)
{
    int len = 0;
    int* tmp = (int*)malloc(sizeof(int)*numsSize);
    for(int i = 0; i < numsSize; i++)
    {
        if(nums[i] != val)
        {
            tmp[len++] = nums[i]; 
        }
    }
    memcpy(nums, tmp, sizeof(int)*len);
    return len;
}

复杂度分析:

 时间复杂度方面,这种方案只遍历了一遍数组,因此它的时间复杂度是O ( N ) O(N)O(N),在空间复杂度方面,由于开辟了一个新数组,所以空间复杂度是O ( N ) O(N)O(N)。

🔖思路三:双指针

这种思路的具体过程是:同时维护两个指针,分别记作pri和pos,让他俩从第一个元素开始同时往后走,当pos指向的元素不等于val的时候,把其赋值给pri所指向的位置,然后两个指针继续往后走,如果pos指向的元素等于val,那么pri停下来,pos跳过这个元素继续往后走,直到pos指向的元素不等于val,把它赋值给pri指向的位置,然后两个指针继续同时往后走,直到pos指针来到数组的结尾。具体过程如下动图所示:


5d45ad08b87a419eaacd024f7572c0c9.gif

代码实现:

int removeElement(int* nums, int numsSize, int val)
{
    int pri = 0, pos = 0;
    while(pos < numsSize)
    {
        if(nums[pos] != val)
        {
            nums[pri] = nums[pos];
            pri++;
        }
        pos++;
    }
    return pri;
}

复杂度分析:

 在时间复杂度方面,这种方法只遍历了一遍数组,所以时间复杂度是O ( N ) O(N)O(N),在空间复杂度方面,由于没有额外申请空间所以空间复杂度是O ( 1 ) O(1)O(1)。对于这道题思路三无疑是最好的方法,这种快慢指针的思想大家要多多去体会。

📖题目描述

df548223fac24e45b19593f0edfdf881.png

191501b784e646eea420c4c8cb527fea.png

题目出处:删除有序数组中的重复项

📖题解


有了上面的分析基础,我们应该很容易猜到这道题也可以使用双指针的方法来做,但是这个题的双指针与上面的又有所不同。这道题要让pri指针从第一个元素开始,pos指针从第二个元素开始,因为相同的元素只需要保存一个,如果pos指向的元素与pri指向的元素相等就让pos往后走而pri不动,直到pos指向的元素不等于pri指向的元素,此时先让pri往后走一步,然后把pos指向的元素赋值给pri指向的元素,具体过程如下动图所示:


a2333218224349f68c80005f04551f37.gif

代码实现:

int removeDuplicates(int* nums, int numsSize)
{
    int pri = 0;
    int pos = pri + 1;
    while(pos < numsSize)
    {
        if(nums[pos] != nums[pri])
        {
            pri++;
            nums[pri] = nums[pos];
        }
        pos++;
    }
    return ++pri;
}

 今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是春人前进的动力!

目录
相关文章
|
2月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
41 3
|
4月前
|
搜索推荐 编译器 C语言
【C++核心】特殊的元素集合-数组与字符串详解
这篇文章详细讲解了C++中数组和字符串的基本概念、操作和应用,包括一维数组、二维数组的定义和使用,以及C风格字符串和C++字符串类的对比。
106 4
|
23天前
|
存储 程序员 C++
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
59 0
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
167 4
|
2月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
53 2
|
3月前
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
2月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
42 1
|
3月前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
81 1
|
3月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
58 2
|
3月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值