专题五:双指针法

一、

双指针法将时间复杂度O(n^2)的解法优化为 O(n)的解法。也就是降一个数量级,题目如下:

15.三数之和
18.四数之和
双指针来记录前后指针实现链表反转:

206.反转链表
使用双指针来确定有环:

142题.环形链表II
双指针法在数组和链表中还有很多应用,后面还会介绍到。

二、

示例 1:
输入: “the sky is blue”
输出: “blue is sky the”

,我们将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒叙了,那么再把单词反转一下,单词就正过来了。

所以解题思路如下:

  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转

erase的时间复杂度是O(n)

那么使用双指针来移除冗余空格:fastIndex走的快,slowIndex走的慢,最后slowIndex就标记着移除多余空格后新字符串的长度。

void removeExtraSpaces(string& s) {
    int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针
    // 去掉字符串前面的空格
    while (s.size() > 0 && fastIndex < s.size() && s[fastIndex] == ' ') {
        fastIndex++;
    }
    for (; fastIndex < s.size(); fastIndex++) {
        // 去掉字符串中间部分的冗余空格
        if (fastIndex - 1 > 0
                && s[fastIndex - 1] == s[fastIndex]
                && s[fastIndex] == ' ') {
            continue;
        } else {
            s[slowIndex++] = s[fastIndex];
        }
    }
    if (slowIndex - 1 > 0 && s[slowIndex - 1] == ' ') { // 去掉字符串末尾的空格
        s.resize(slowIndex - 1);
    } else {
        s.resize(slowIndex); // 重新设置字符串大小
    }
}

注意:在移除空格的时候也要想到可以使用双指针法。

如果使用库函数 erase的话,它本身的时间复杂度是O(n)

三、总结

1、数组篇

所以此时使用双指针法才展现出效率的优势:「通过两个指针在一个for循环下完成两个for循环的工作。」

2、字符串篇

使用双指针法,「定义两个指针(也可以说是索引下表),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。」,时间复杂度是O(n)。

在替换空格 中介绍使用双指针填充字符串的方法,如果想把这道题目做到极致,就不要只用额外的辅助空间了!

思路就是「首先扩充数组到每个空格替换成"%20"之后的大小。然后双指针从后向前替换空格。」

有同学问了,为什么要从后向前填充,从前向后填充不行么?

从前向后填充就是O(n^2)的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。

「其实很多数组(字符串)填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。」

那么在字符串:花式反转还不够!中,我们使用双指针法,用O(n)的时间复杂度完成字符串删除类的操作,因为题目要产出冗余空格。

「在删除冗余空格的过程中,如果不注意代码效率,很容易写成了O(n^2)的时间复杂度。其实使用双指针法O(n)就可以搞定。」

「主要还是大家用erase用的比较随意,一定要注意for循环下用erase的情况,一般可以用双指针写效率更高!」

3、链表篇

使用双指针法来翻转链表,「只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表。」

在链表中求环,应该是双指针在链表里最经典的应用,在链表:环找到了,那入口呢?中讲解了如何通过双指针判断是否有环,而且还要找到环的入口。

「使用快慢指针(双指针法),分别定义 fast 和 slow指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。」

4、N数之和篇

其实使用双指针也可以解决1.两数之和的问题,只不过1.两数之和求的是两个元素的下标,没法用双指针,如果改成求具体两个元素的数值就可以了,大家可以尝试用双指针做一个leetcode上两数之和的题目,就可以体会到我说的意思了。

使用了哈希法解决了两数之和,但是哈希法并不使用于三数之和!

使用哈希法的过程中要把符合条件的三元组放进vector中,然后在去去重,这样是非常费时的,很容易超时,也是三数之和通过率如此之低的根源所在。

去重的过程不好处理,有很多小细节,如果在面试中很难想到位。

时间复杂度可以做到O(n^2),但还是比较费时的,因为不好做剪枝操作。

所以这道题目使用双指针法才是最为合适的,用双指针做这道题目才能就能真正体会到,「通过前后两个指针不算向中间逼近,在一个for循环下完成两个for循环的工作。」

只用双指针法时间复杂度为O(n2),但比哈希法的O(n2)效率高得多,哈希法在使用两层for循环的时候,能做的剪枝操作很有限。

在双指针法:一样的道理,能解决四数之和中,讲到了四数之和,其实思路是一样的,「在三数之和的基础上再套一层for循环,依然是使用双指针法。」

对于三数之和使用双指针法就是将原本暴力O(n ^ 3)的解法,降为O(n ^ 2)的解法,四数之和的双指针解法就是将原本暴力O(n ^ 4)的解法,降为O(n ^ 3)的解法。

同样的道理,五数之和,n数之和都是在这个基础上累加。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值