双指针系列-几道简单的 Leetcode 题目

Leetcode 面试题 10.01. 合并排序的数组

问题描述

给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法,将 B 合并入 A 并排序。

初始化 A 和 B 的元素数量分别为 m 和 n。

示例:

输入:
A = [1,2,3,0,0,0], m = 3
B = [2,5,6], n = 3

输出: [1,2,2,3,5,6]
说明:

A.length == n + m

解题报告

一般情况下,合并两个有序数组时,还需要第三个数组,大小为两个有序数组的大小之和。但是此题数组 A 的大小足够大,所以从两个有序数组的末尾元素开始处理,从数组 A 的末尾开始填充。

时间复杂度: O ( m + n ) O(m+n) O(m+n)
空间复杂度: O ( 1 ) O(1) O(1)

实现代码

class Solution {
public:
    void merge(vector<int>& A, int m, vector<int>& B, int n) {
        int i=m-1,j=n-1,pos=m+n-1;
        while(j>=0&&i>=0){
            if(A[i]>B[j]) A[pos--]=A[i--];
            else A[pos--]=B[j--];
        }
        while(j >= 0) A[pos--] = B[j--];
    }
};

Leetcode 面试题 02.02. 返回倒数第 k 个节点

问题描述

面试题 02.02. 返回倒数第 k 个节点
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

注意:本题相对原题稍作改动

示例:

输入: 1->2->3->4->5 和 k = 2
输出: 4
说明:

给定的 k 保证是有效的。

解题报告

设置两个指针 slowfastfast 先走 k 步,然后 slowfast 以相同速度前进,当 fast 到达末尾时,slow 即在倒数第 k 个节点。这种逻辑和 Leetcode 面试题52.两个链表的第一个公共节点【思路巧妙,源自题解区】 有点相似。

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

实现代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    int kthToLast(ListNode* head, int k) {
        ListNode* slow=head,*fast=head;
        for(int i=1;i<=k;i++){
            fast=fast->next;
        }
        while(fast){
            slow=slow->next;
            fast=fast->next;
        }
        return slow->val;
    }
};

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

问题描述

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2],

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。

你不需要考虑数组中超出新长度后面的元素。

解题报告

设置两个指针 iji 指着当前有效的索引,j 则跳过重复元素,将非重复元素依次放到指针 i 所在的位置,然后 i 往后移。

时间复杂度: O ( m ) O(m) O(m)
空间复杂度: O ( 1 ) O(1) O(1)

需要注意的是索引的判断,一不小心就容易越界。

实现代码

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.size()==0) return 0;
        int i=0,j=1;
        while(j<nums.size()){
            while(j<nums.size()&&nums[j]==nums[j-1])j++;
            if(j<nums.size())
                nums[++i]=nums[j++];
        }
        return i+1;
    }
};

Leetcode Leetcode 27. 移除元素

问题描述

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

给定 nums = [3,2,2,3], val = 3,

函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。

你不需要考虑数组中超出新长度后面的元素。

解题报告

和上一题有一点相似之处,设置指针 l 指向数组的头部,设置指针 r 指向数组的尾部。将 r 指向非 val 的元素,然后将左边的 val 移到右边。

while(l<=r) 取等号的原因是:如果数组只有一个元素而且等于 val,会无法删除该元素。

if(l<=r&&nums[l]==val) 中需要满足 l<=r 的原因是从 while(l<=r&&nums[r]==val) 这个循环跳出来有可能是 l>r 且此时 r<0

时间复杂度: O ( m ) O(m) O(m)
空间复杂度: O ( 1 ) O(1) O(1)

实现代码

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int clipLen=0,l=0,r=nums.size()-1;
        while(l<=r){
            while(l<=r&&nums[r]==val) {
                r--;
                clipLen++;
            }
            if(l<=r&&nums[l]==val){
                nums[l]=nums[r--];
                clipLen++;
            }
            l++;
        }
        return nums.size()-clipLen;
    }
};

总结

指针在移动的时候,一定要注意一些边界条件,很容易就发生越界错误了。

  1. 两个指针的关系判断时,是否取等号;
  2. 从循环中跳出时,要全面考虑跳出循环的条件。

参考资料

[1] Leetcode 面试题 10.01. 合并排序的数组
[2] Leetcode 面试题 02.02. 返回倒数第 k 个节点
[3] Leetcode 面试题52.两个链表的第一个公共节点【思路巧妙,源自题解区】
[4] Leetcode 26. 删除排序数组中的重复项
[5] Leetcode 27. 移除元素

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值