算法技巧:双指针总结(1)

1.双指针的技巧

双指针大致分为二类,一个是快慢指针,剩下一个是左右指针,左右指针中,通常会将单调性与左右指针一起结合。

注意:这里所指的双指针在某一些题目中是指针,但大多数都是数组元素下标

接下来,用题目去说明

题一:移动零

 1.1 链接

283. 移动零 - 力扣(LeetCode)

1.2 思路

前提:数组长度为n,数组为nums

首先定义两个变量des和cur,用cur去遍历整个数组,我们要实现[0,des]为非零,[dest+1,cur-1] 为零,[cur,n]为待处理数据,就拿[0,1,0,3,12]来说明,我们要实现数组分块的效果.

  • 首先,cur位于元素下标为0处,des赋值为-1(这样是为了满足区间的合法性),我们先判nums[cur]是否为0;
  • nums [cur] 为0,那么我们让cur++,去找非0的数值,直到找到第一个非0的数,就停下来,
  • 若 nums[cur] 不为0时,我们将 nums[des+1] 与 nums[cur] 进行交换(这步是因为,我们已经判断了[dest+1,cur-1]这个区间内数都为0) ,在des++,cur++
  • 然后我们在重复上述操作,直到cur>=n,就停止。

1.3代码 
void Swap(int* e1,int* e2)
{
    int tmp=*e1;
    *e1=*e2;
    *e2=tmp;
}

void moveZeroes(int* nums, int numsSize) {
    int des=-1;
    int cur=0;
    while(cur<numsSize)
    {
        if(nums[cur]==0)
        {
            cur++;
        }
        else{
            Swap(&nums[des+1],&nums[cur]);
            des++;
            cur++;
        }
    }
}

题二:删除有序数组里的重复项

1.1链接

26. 删除有序数组中的重复项 - 力扣(LeetCode)

1.2思路

前提数组名为nums

定义两个指针,一个为slow,一个fast,我们让fast去遍历数组,去找与nums[slow]不同的值,找到了第一个不同的值,就停下

  1. 如果nums[fast] !=nums[slow],那么就slow++,再将 nums[fast] 赋值给 nums[slow](注意当fast找到了第一个不同的值时,(slow,fast)之间都是与nums[slow]重合的数),再让fast++
  2. 如果nums[fast] ==nums[slow],则让 fast++
  3. 在重复上述操作,直到 fast>=nums.size(),就结束
  4. 我们最终返回的时 slow+1,slow+1就时新数组的元素个数。
1.3代码 
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int fast=0;
        int slow=0;
        //也可以理解重新开了一个数组,每次将进行筛选赋值
        while(fast<nums.size())
        {
            if(nums[fast]!=nums[slow])//如果进入了for循环,
            //那么则保证了在从slow(包括slow)到fast(不包括fast)都是相同的数;
            {
                slow++;
                nums[slow]=nums[fast];
            }
            fast++;

        }
        return slow+1;

    }
};

题三:快乐数

1.1链接

 202. 快乐数 - 力扣(LeetCode)

1.2思路

知识前提准备鸽巢原理,有n个巢,和n+1个鸽子,那么至少会有一个巢鸟的数量>1。

首先,我们由题目可知,在判断这个一个数是否为快乐水,它只有两种可能,一是:最终这个数变成了1,二是:无限循环 但始终变不到 1。最终能到达1,其实也是相当于无限循环1。

下面我们来证明一下

  •  首先这个数最大只能是2的31次方-1,那么也就是2,147,483,647
  • 它<9,999,999,999,这个各个位的数的平方和为810,则2的31次方-1经过判断快乐水的操作后,其平方数和一定位于 [ 1, 810 ] 之间
  • 我们对2的31次方-1,进行811次这样的求平方数和的操作
  • 由鸽巢原理可知,一定会有一个数重合,那么此时就构成了闭环。 
  • 那么我们也可以,确定它一定会出现一个闭环,只不过是闭环中的数是否相同而已。

相信大家看到上面画的图后,一定也想到了链表中如何判断循环链表的那道题了。

这里我们也采取一样的思路,定义一个快指针fast,一个慢指针slow,慢指针一次只往前走一步,快指针一次往前走2步,。由于快指针一直都比慢指针快并且这里一定会出现一个闭环,那么快指针一定会与慢指针相遇。(这就相当于二个人在操场跑步,一个人总比你快,那么你们一定会相遇的)

141. 环形链表 - 力扣(LeetCode)

有空,可以去重新写一下环形链表

1.3代码
class Solution {
public:
    int bitsum(int n) {
        int sum = 0;
        while (n) {
            int m = n % 10;
            sum += m * m;
            n = n / 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        int slow = n;
        int fast = bitsum(n);
        while (slow != fast) {
            slow = bitsum(slow);
            fast = bitsum(bitsum(fast));
        }
       //当代码执行到这,就以为这,它们相遇了,就是出现值相等了,
       //那么我们不必在往后了,因为在往后,也是在重复这个闭环而已。
       //并且最后slow的位置一定是闭环的开始和结束那个点
        return slow == 1;//判断slow是否为1
    }
};

后续题目(思路是:双指针结合单调性)明天补上,

  • 42
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值