【力扣刷题篇】顺序表之双指针的多种应用

27. 移除元素

1. 题目介绍

  • 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
  • 要求 : 空间复杂度为 O(1)

2. 题目解析

  • 给出指定数组nums ,与指定值 val。 将数组nums中的所有值为val的元素删除。 最后返回该数组剩余元素的个数。
  • 原地移除, 代表不开辟新空间, 空间复杂度为常数阶。

3. 题解

双指针的应用

  • 此处双指针并非真的是指针类型的变量, 只是两个起到指针作用的int类型变量
    • 同向双指针: 两个指针均从数组头部向数组尾部进行移动
    • 反向双指针 : 两个指针一个从数组头部向数组尾部移动, 另一个指针从数组尾部向数组头部进行移动

1> 同向双指针

思路详解:
采取两个同向移动的指针用来遍历数组nums中的元素, 两个指针同时同向移动, 每遍历到一个非val元素指针一都会将其指向的元素的值 赋给指针二所指向的元素
区别在于:

  • 指针一在边路数组nums中的元素时,并不会停下
  • 指针二在遍历数组nums中的元素时, 遭遇到值为val的元素时, 就会停止向后移动

该循环结束的条件为, 指针一完全遍历数组nums, 此时指针一指向数组中最后一个元素的下一位置。

思路图解:
在这里插入图片描述

代码实现:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) 
    {
        int n = nums.size();   // 数组中元素个数
        int second = 0;      // 指针二
        for (int first = 0; first < n; first++)   //first 指针一
        {   // 指针一持续移动
            if (nums[first] != val)  // 当指针一 指向的元素不为val时
            {
                nums[second] = nums[first];// 将这个元素赋值给指针二
                second++;      // 指针二进行++ 
            }
        }             // 当指针一指向的元素为val时, 指针一继续移动,
        return second;// 指针二停止移动, 且此时不再进行赋值操作。
    }

作者:力扣官方题解
链接:https://leetcode.cn/problems/remove-element/solutions/730203/yi-chu-yuan-su-by-leetcode-solution-svxi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
时间复杂度 O(n)
空间复杂度 O(1)

2> 反向双指针

思路详解:
采取左右两个指针向中间移动, 当左右两个指针指向同一个元素时, 遍历一遍数组中的所有元素。
我们先让左指针进行移动, 直到左指针遇到 val。 此时让左指针停下,
然后再让右指针进行移动,直到右指针遇到非 val 。 此时让右指针也停下。
然后我们交换两个指针指向的元素。
当我们交换完成后, 此时左指针指向非val, 右指针指向val。 它们又可以继续移动了, 重复上述步骤。
由于是左指针先进行移动, 那么左指针会先移动到 右指针指向的元素,此时完全遍历数组中元素。 此时返回 左指针 的大小就是数组新元素的个数

class Solution {
public:
    int removeElement(vector<int>& nums, int val)
    {
        int n = 0;
        int m = nums.size();  // 注意 m = 元素个数, 用于索引需要 -1
        int temp = 0;
        while (n < m)        // n<m,m=nums.size() 适用于特殊情况,
       {                     // 数组中只有一个元素, 且该元素不是val
            while (nums[n] != val)
            {
                n++;
                if(n>=m)      // 防止 nums[n] 越界, n++后恰好越界
                {
                    break;     // 直接跳出当前while循环,防止下次判断
                }              // nums[n] != val ,有可能越界
            }
            while (nums[m-1] == val)
            {
                m--;
                if(m == 0)     // 同理防止越界
                    break;
            }
            if(n>=m)            // n ,m-1指向同一元素时,n<M
                break;          // 还会在进行一次while循环, 
            temp = nums[n];     // 而下次while循环有可能会数组越界
            nums[n] = nums[m-1];  // 因此需要跳出最外层while循环
            nums[m-1] = temp;
        }
        return n;       // n 的值代表 数组中新元素的个数
    }
};

作者: Joker不是Joker
时间复杂度 O(n)
空间复杂度 O(1)

反向指针相比于同向指针, 它避免了繁琐的赋值操作,且数组中有几个val它才交换几次(甚至少交换一次)
优点在于 交换次数少,
缺点在于 需要进行多次判断防止数组越界

26. 删除有序数组中的重复项

1 . 题目介绍

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

2. 题目提示

  • 考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k 。

3 . 题目解析

该数组中为非严格递增数列, 可以将其认为一个类似递增的数列, 区别在于该数列中存在相同元素。 删除重复元素指的是 ,重复只保留一个。
最后所得数组, 该数组中元素的相对顺序不能改变。
也就是说, 最后所得结果为一个递增序列的数组。
返回删除重复元素后数组中剩余元素的个数

4 . 题解

双指针的应用:
类似于上题, 依旧是双指针的应用

方式一:

class Solution {
public:
    int removeDuplicates(vector<int>& nums)
    {
        int src=0;
        int dst=0;
        while(src<nums.size())
        {
            if(src ==0) // 当dst =0时, nums[dst-1]会越界,因此在dst = src = 0时, 均对其++ 
            {           
                src++;  // dst = src = 0 必须存在的意义是: 数组中仅有一个元素, 此时++之后直接退出while循环
                dst++;  
            }
            else
            {
                if(nums[src] != nums[dst])   // src 与dst指向元素不同, 就将src指向的元素赋给dst
                {
                    nums[dst] = nums[src];
                }
                if(nums[dst] != nums[dst -1])  // 判断dst 指向的元素与其前一个是否相同, 不同则++ ,相同dst停止移动, 等待下次循环 当前所指向元素被覆盖
                {
                    dst++;
                }
                src++;
            }
        }
        return dst;
    }
};

作者 : Joker不是Joker
时间复杂度: O(n)
空间复杂度: O(1)

方式二:
双指针代码优化

class Solution {
public:
    int removeDuplicates(vector<int>& nums)
    {
        int src=0;
        int dst=0;
        while(src<nums.size())
        {
            if(nums[src] !=nums[dst])     // 判断条件变为 src 与dst指向元素是否相同, 相同src移动, dst停止。 指向元素不同时,src赋值给dst-1之后二者同时移动
            {
                if((src -dst)>1)     // 避免src自我赋值
                {   
                    nums[dst+1] = nums[src];
                }
                dst++;
            }
            src++;
        }
        return dst+1;
    }
};

作者: Joker不是Joker    
时间复杂度: O(n)        //时间上超越100%用户
空间复杂度: O(1)

88 . 合并两个有序数组

1. 题目介绍

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

2. 题目提示

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

3. 题目解析

该题, 是将两个非严格递增序列的数组合并为一个非严格递增序列的数组。 无时间或空间复杂度的要求。

4. 题解

方式一:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) 
    {
        int src=m-1, dst=n-1;
        for(int i=m+n-1; i>=0; i--)
        {
            if(src == -1)
            {
                nums1[i] = nums2[dst--];
            }
            else if(dst == -1)
            {
                nums1[i] = nums1[src--];
            }
            else if(nums1[src] >= nums2[dst])
            {
                nums1[i] = nums1[src--];
            }
            else
            {
                nums1[i] = nums2[dst--];
            }
        }
    }
};
       //    此方式过于臃肿
作者 : Joker不是Joker
时间复杂度: O(m+n)
空间复杂度: O(1)

方式一优化:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) 
    {
        int src=m-1, dst=n-1;
        int count= m+n-1;
        while(src>=0 || dst>=0)    // 将for循环用while循环替代,循环结束条件并非遍历(m+n)个元素, 而是当某一个数组被遍历完, 另一个数组对nums1进行赋值或是不赋值
        {
            if(src == -1)
            {
                nums1[count--] = nums2[dst--];
            }
            else if(dst == -1)
            {
                src--;
            }
            else if(nums1[src] >= nums2[dst])
            {
                nums1[count--] = nums1[src--];
            }
            else
            {
                nums1[count--] = nums2[dst--];
            }
        }
    }
};
作者 : Joker不是Joker
时间复杂度: O(m+n)   // 时间上超越100%用户
空间复杂度: O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值