算法基础--双指针

本文介绍了双指针算法的基本概念,包括其分类(如快慢指针、首尾指针等),并提供了一个通用的套路模板。通过实例演示如何运用双指针解决LeetCode上的问题,如最长回文子串和盛最多水的容器。

       前面已经写了两篇关于算法方面的文章,这几天想了下,决定把这个算法整理成一个系列,除了是帮助自己巩固算法知识外,还能够把自己总结的每种算法的套路保存下来并分享给大家,这样以后即使是哪天想要重拾起来,也是能够根据这些算法套路快速重拾。

        我想了下,算法这块主要分为五大块,分别是双指针、栈(单调栈)、深度优先搜索(DFS)、广度优先搜索(BFS)、动态规划。今天就从双指针开始,从双指针算法概述、套路模板,以及根据这个模板,进行几道算法题的讲解,从简单到难,让小白也能由浅入深了解这个算法。

        双指针,是一种通过设置两个指针不断进行单向移动来解决问题的算法思想。一般包含两种形式:一、两个指针指向同一个序列。二、两个指针分别指向不同的序列。指向同一序列的比较常见,代表有快慢指针首尾指针固定间距指针等。指向不同序列的双指针代表有归并排序这种,需要合并时用双指针或者多指针。

        说得那么概念,可能大家反而没什么感觉,不就是两个指针,根据一定的条件触发两个指针的移动,然后把满足一定条件的结果作为最终结果嘛。但是,下面,来点干货,套路模板来了,不管什么样的算法题,只要是双指针的题目,记住下面这个套路模板,然后稍微调整下边界值,就可以直接往上套了,以不变应万变:

public Type twoPoints() {
        int left;
        int right;
        Type result
        while (满足继续循环条件) {
            if (满足左指针移动) {
                left++;
				if(满足更新result条件) 更新result;
            } else {
                right++/right--;
				if(满足更新result条件呢) 更新result;
            }
        }
        return result;
    }

那么好,看到上面这个模板我就想立马给各种类型的双指针场景给安排上,让我试一试这把刀锋不锋利,首先来第一种,快慢指针,我在leetcode上一下子就找到了,看看这题目怎么描述的:

3. 无重复字符的最长子串

 

不知道是不是刷题刷多了,看到题就会大概猜到这道题要用什么算法,我相信很多人都有这种感觉,一看到这题目,就知道这肯定是双指针,并且是快慢指针就能搞定。那么好,我们直接套模板,把代码写出来:

public int lengthOfLongestSubstring(String s) {
        Set<Character> containString = new HashSet<>();// 子串中包含的所有字符
        char[] sChar = s.toCharArray();
        int left = 0;
        int right = 0;
        int max = 0;
        while (right < sChar.length) {// 右指针走到最右端的时候,循环就可以结束了
            if (!containString.contains(sChar[right])) {// 满足右指针继续右移的条件
                containString.add(sChar[right]);
                max = max > right - left + 1 ? max : right - left + 1;
                right++;
            } else {// 满足左指针右移的条件
                while (containString.contains(sChar[right])) {
                    containString.remove(sChar[left]);
                    left++;
                }
            }
        }
        return max;
    }

那么好,这道题可能很多小伙伴会说太简单了,如果双指针只有这水平,那大家都可以洗洗睡了,还在算法的题海里面挣扎个什么劲。我们接着往下看第二种,收尾指针,刚好也有一道比较经典的题:

11. 盛最多水的容器

如果放在几年前,在我功力还不是那么深厚的时候,我一看这题目,果断给他来个双层循环,暴力计算出所有两个坐标之间的面积,记录一个最大值,然后比较更新,10分钟代码我就给它撸完。然后一跑,尴尬了,暴力的居然还能过,只是这速度和内存的使用情况,有点不太好看。

但是现在嘛,首尾指针给它安排上,两个指针指向的数值,比较小的往对方靠拢(为什么这样自己去想想),记录一个最大值,不断刷新满足条件的值,直到两个指针相遇,结束。

public int maxArea(int[] height) {
        int left = 0;
        int right = height.length - 1;
        int max = 0; // 记录中间过程满足条件最大值
        while (left < height.length && right > left) {// 满足指针继续移动
            int curretn = (height[left] > height[right] ? height[right] : height[left]) * (right - left);
            max = curretn > max ? curretn : max;
            if (height[left] >= height[right]) {// 右指针左移
                right--;
            } else { // 左指针右移
                left++;
            }
        }
        return max;
    }

好,前面两种都介绍完了,下面就轮到固定间距双指针了,但是尴尬的是我居然没找到相关类型的题目,额,先跳过,这种类型的题目一般是滑动窗口会用的比较多,下次遇到了我再更新到这里来,我们先看下两个指针指向不同序列这种类型,这个比较有意思,刚好之前也遇过:

56. 合并区间

class Solution {
    public int[][] merge(int[][] intervals) {
        if (intervals.length <= 1) {
            return intervals;
        }
        Arrays.sort(intervals, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[0] == o2[0]) {
                    return o1[1] - o2[1];
                }
                return o1[0] - o2[0];
            }
        });

        int min = intervals[0][0];
        int max = intervals[0][1];
        List<int[]> resultList = new ArrayList<>();
        for (int i = 1; i < intervals.length; i++) {
            if (max >= intervals[i][0]) {
                // 存在交集,合并
                max = max > intervals[i][1] ? max : intervals[i][1];
                continue;
            } else {
                // 不存在交集,将min和max先入结果集,然后再重置min和max
                int[] tempResult = {min, max};
                resultList.add(tempResult);
                min = intervals[i][0];
                max = intervals[i][1];
            }
        }
        resultList.add(new int[] {min, max});

        return resultList.toArray(new int[resultList.size()][2]);
    }
}

两个指针min和max,指向不同的列,找出相交的集合,并返回。

双指针的题目一般情况下都不会很难,从我刷的题目经验来看,顶多中等题吧,关键在于有双指针解题的思想(就是看到问题第一时间能够知道使用双指针解题的思路),下面列一下在leetCode上使用双指针思想解题的题目,供大家练手:
5. 最长回文子串
11. 盛最多水的容器
15. 三数之和
18. 四数之和
26. 删除有序数组中的重复项
27. 移除元素
31. 下一个排列
83. 删除排序链表中的重复元素
160. 相交链表
283. 移动零
392. 判断子序列
821. 字符的最短距离
870. 优势洗牌
905. 按奇偶排序数组
986. 区间列表的交集

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值