算法篇1:双指针思想的运用(1)--C++

一.算法解析

双指针,顾名思义就是两个指针,常见的算法中,我们可以看到两种:

1.对撞指针:一般用于顺序结构,也称为左右指针。

  • 对撞指针从两端向中间移动。一个指针从最左端开始,另一个从最右端开始,然后逐渐往中间逼近。
  • 对撞指针的中终止条件是两个指针相遇后者错开(也有可能在内部找到结果后就直接返回了结果)。总的来说就是:
    • left == right
    • left > right

 2.快慢指针:又被称之为龟兔赛跑算法,其基本思想就是使用两个移动速度不同的指针在数组或链表等序列结构上移动。

这样的结构在环形数组上非常有用·。

其实不单单是环形链表和数组,我们再研究任何出现循环往复的情况是,都可以使用快慢指针的思想。

快慢指针的视线方法有很多种,最常用的一种就是:
在一个循环中,每一次让慢指针向后移动一位,快指针向后移动两位,实现两个指针,一快一慢。

二、题目解析 

1.移动0 

题目链接: 283. 移动零 - 力扣(LeetCode)

这道题的要求十分的简单,我们可以看到,题目的要求需要我们保持非0元素的相对位置不变。

我们就可以使用快慢指针来解决这道题。

首先我们可以通过定义两个下标位置来模拟快慢指针,我们可以怎样来定义呢?

本题目中,我们可以用一个cur指针来烧苗整个数组,另一个指针dest指针来记录非0元素序列的最后一个元素。根据cur在扫描的过程中,遇到的不同的情况,分类处理,实现数组的划分。

通过上面的思想,我们就可以着手实现代码了:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        for (int cur = 0, dest = -1; cur < nums.size(); cur++)
            if (nums[cur]) swap(nums[++dest], nums[cur]);
    }
};

2. 复写0

题目链接: 1089. 复写零 - 力扣(LeetCode)

 

这道题,想要在原地复写,我们还是通过双指针的算法思想来实现:

1.首先我们要找到最后一个需要复写的元素的位置:

可以在扫描数组的时候直接判断数组元素的值,为非0元素就直接领cur++,dest++;

是0就直接让deSt += 2 

直到最后 dest >= n - 1为止,在这里,我们还需要注意处理边界情况,如下图这种情况:

 

解决了上面的问题之后,理清了思想之后,我们可以 着手实现代码了:

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        //首先我们找到最后一个需要腹泻的元素位置:
        int cur = 0, dest = -1;
        int n = arr.size();
        while(cur < n)
        {
            if (arr[cur])
                dest++;
            else
                dest += 2;
            if (dest >= n - 1)
                break;
            cur ++;
        }
        //处理边界情况:
        if (dest > n - 1)
        {
            dest -= 2;
            arr[arr.size() - 1] = 0;
            cur--;
        }
        while (cur >= 0)
        {
            if (arr[cur])
            {
                arr[dest--] = arr[cur];
            }
            else
            {
                arr[dest--] = 0;
                arr[dest--] = 0;
            }
            cur--;
        }
    }
};

3. 快乐数

题目链接:202. 快乐数 - 力扣(LeetCode)

首先分析一下题意

 为了方便叙述:将【对于一个正整数,每一次将该数替换为=为他每一个位置上的数字的平方和】将这个操作记为x;

题目告诉我们,当我们不断地重复x的操作时,计算一定会进入死循环,死的方式有多种:

一:一直在1中死循环即: 1--》1--》1--》1.....

二:在历史的数据中死循环,但始终得不到一!

由于上面的两种情况只会出现一种,因此,只要我们能确定循环是在【情况1】中进行,还是子啊【情况二】中进行,就能得到结果

这里我们简单证明一下,为什么会只得到两种结果:

  • a.经过一次变化的最大值 9 ^ 2 * 10 = 81(这种情况指的就是最大的一个数,9999999999),也就是说,变化的区间就在【1,810】;
  • b.根据鸽巢原理:一个数变化811次之后必然会形成一个循环。
  • c.因此我们可以使用快慢指针来实现。

有了上面的知识基础,我们现在开始写代码:

class Solution {
public:
    int sumbit(int n)
    {
        int sum = 0;
        while (n)
        {
            sum += pow(n % 10, 2);
            n /= 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        int fast = sumbit(sumbit(n));
        int slow = sumbit(n);
        while (slow != fast)
        {
            fast = sumbit(sumbit(fast));
            slow = sumbit(slow);
        }
        if (slow == 1)
            return true;
        else
            return false;
    }
};

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值