初识算法 · 双指针(1)

目录

前言:

双指针算法

题目一:

​编辑

题目二:


前言:

本文作为算法部分的第一篇文章,自然是少不了简单叭叭两句,对于算法部分,多刷是少不了,我们刷题从暴力过度到算法解法,自然是一个很痛苦的过程,而算法本身的思考是很重要的,所以算法部分的介绍大多都是通过题目直接介绍,以题目来灌注算法知识,因为每人对于算法的接受程度有限,每篇大多都是两题左右,对于难题部分,大多时候只会出一道,并且均以leetcode作为题目来源,本文都会以最优质的解法来介绍不同的算法,通过三个部分来解决题目,题目解析,算法原理,算法编写。


双指针算法

题目一:

样例为:

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

题目解析:

给定一个数组,移动0到末尾,不必考虑0的相对顺序,但是要保持非零的相对顺序。

如果不使用双指针,有很多解法,比如我们可以将所有的非零元素移动到最开始,但是移动一次,就需要遍历一次,时间复杂度是接近于O(N^2)的,因为不能数组,这是一种暴力解法,那么我们如何使用双指针呢?

算法原理:

通过使用双指针,将数组划分为三个区域:

[0,dest]划分为非零元素的区域,[dest,cur]划分为0元素的区域,[cur,end]划分为还没有遍历的区域。既然是使用双指针算法,我们自然需要定义两个“指针”,可是这里实际上不是指针,这里我们需要对双指针有一个形象的认识,双指针并不是真正的用指针,它们代表的只是形象的指向两个元素而已,这里的指针可是是一个数,可以是数组,可以是任何能代替指向的东西。

这里是数组下标,所以定义两个下标是必不可少的。

然后就是进行划分区域,二者都是从0开始划分,dest我们不知道如何定义可以先不管,cur遍历数组,找到非零的元素,就给dest,那么怎么给呢?dest可以从-1开始,因为cur就是从0开始的,找到了非零元素,dest++进行交换,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]);
        }
    }
};

就过啦,可能算法代码出乎你想象的简单,习惯就好。

简单的分析一下时间复杂度吧,一次遍历,O(N),如果暴力是平方,这优化的就很不错了。

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

题目二:

样例为:

输入:n = 19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1

题目解析:

题目名为快乐数,让我们看看题目有多快乐吧!

快乐数的定义为将一个数多次等于该数上的每一位数字的平方相加,如果经过多次变化,数字可以变为1,那么就是快乐数,但是如果是一直无线循环没有变成1,那么该数就不是快乐数。对于这种题目就没有暴力解法了,因为真要暴力的话很有可能套死循环出不去了。所以我们直接进入到算法原理部分。

算法原理:

我们不妨画图,看看变化是怎么个事儿:

对于19来说,经过了4次变化就到1,那么对于2来说,经过多次变化,出现了和第一次变化一样的值,4。

那么我们可不可以理解2在变化的时候出现了环,且数的变化出不了这个环,所以它不是快乐数,那么好像看起来也没啥啊,19也没有出现环,可是,如果我们换个角度,19变化的时候,是不是出现了一个环,环里面只有1呢?

这时候相信大部分人已经明了了,我们只需要判断环里面是不是1即可,即我们可以使用两个指针,一个走的快,一个走的慢,二者是一定相遇的,相遇的时候,看相遇的点是不是1就可以了。

那么就有第二个问题了,为什么一定会出现环呢?

这里要使用到的是我们之前未曾听闻的一个数学原理,鸽巢原理

我们使用鸽巢原理来证明一定会出现环状:

这是Leetcode中告诉的n最大的取值,那么:

这是最大的取值,我们不妨这样,一共有10个数字,我们再大一点,10个9,也就是说,我们计算一下9999999999经过一次变化之后的取值,变化之后的取值是810,也就是说,变化之后最大的值一定不会超过810。不妨定义函数F(x)等于一次变化,那么一个数经过811变化,也就是产生了811个数,但是总的区间只有810,那么一定有一个数是重复的,这就是鸽巢原理的应用。

算法编写:

class Solution 
{
public:
    int _isHappy(int num)
    {
        int ans = 0,sum = 0;
        while(num)
        {
            sum = num % 10;
            ans = ans + sum * sum;
            num /= 10;
        }
        return ans;
    }
    bool isHappy(int n) 
    {
        int slow = n,fast = n;
        while(1)
        {
            slow = _isHappy(slow);
            fast = _isHappy(_isHappy(fast));
            if(slow == fast)
            {
                if(slow == 1)
                return true;
                else
                return false;
            }
        }
    }
};

一个函数,_ishappy对应函数F(x),那么快慢指针,就是一个变化两次,一个变化一次,当它们相等的时候,判断即可,这里也应证了双指针的算法并不是真的使用指针,它更多的只是一种思想而已!!


今日算法到这里了,感谢阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值