双指针的妙用:面试中的脑筋急转弯问题

As far as I know, the Towers of Hanoi puzzle has no practical use except one:

teaching recursion to computer science students.
… …
Because it works so well as an example, the Towers of Hanoi is included in most

textbooks that treat recursion and has become—much like the “hello, world”

program in Chapter 1—part of the cultural heritage that computer scientists share.

----- Eric S. Roberts Programming Abstraction in C++


尽管理论上说,一次遍历和两次遍历的时间复杂度只差了一个constant,理论上是 asymptotically of the same order。但是,在现实中,往往一次遍历能省下大量的空间和资源(有的时候甚至只能搞一次遍历!详情可以参阅我的上一篇博文:蓄水池问题。)

而且除了实用角度,在一次遍历的情景下考虑问题可以训练思维(啊,又到了我最喜欢的脑筋急转弯环节)。正如汉诺塔的(几乎)唯一用途是 teach computer students recursion,或许下面的一个问题最大的application就是面试/锻炼思维。sigh

不过这个问题是极为有趣的,今天就让我们一起来康康吧!


问题描述

给定一个单向链表(singly linked list),要求在一次遍历的情况下,返回处在链表中间的node。

这个问题的限制——一次遍历——使得我们不能先遍历一次,储存链表的长度N,然后再遍历一次,去寻找 N/2的node。如果我们将自己的思维局限在单指针,恐怕永远也不能写出一个满意的答案。

另一个相似的问题的描述如下:^力扣

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。要求使用一次扫描

可以看出,这两个问题的本质是一样的。

直觉

想象你一个人跋涉在一条蜿蜒的公路上。你不知道自己已经走了多远,离终点还有多远——直到你看见了一个路标,或者打开手机GPS定位——可见,没有外界的辅助,我们常常是”不识庐山真面目,只缘身在此山中“。

那么,在一个一次遍历问题中,我们同样也需要一个“路标”!这个路标告诉我们,离终点还有多远。

如果你是一个盲人,拿着一根长1.5米的拐棍。假设拐棍和地面的垂直线的夹角为 $\theta $,那么,这个盲人离他的拐棍所点地的地方的水平距离,就是 1.5 sin ⁡ θ 1.5 \sin \theta 1.5sinθ米!

又想象你在一片黑暗中前行,你伸出双手探路。那么你的头离你的双手触摸到的位置的距离,就是你的手臂长度。

基于这样的一个朴素的思想,我们可以提出以下算法。

算法

  1. 初始化两个指针,都指向开始的位置。
  2. 指针1向前挪动一步,指针2与此同时向前挪动两步。
  3. 重复步骤2,直到指针2到达终点。
  4. 此时指针1所指的位置,就是单向链表的中间 Node

这便完成了对问题一的解答。指针1就是在漫漫长路上跋涉的我们,指针2就是一个路标。

第二个问题的解答是相似的,在这里直接放一个源码,并结束本篇博客。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode * dummy_head = new ListNode(-1);
        dummy_head->next = head;
        ListNode * pt1 = dummy_head;
        ListNode * pt2 = dummy_head;
        for (int i = 0; i<n+1; i++) {
            pt2 = pt2->next;
        }
        while (pt2 != NULL) {
            pt1 = pt1->next;
            pt2 = pt2->next;
        }
        pt1->next = pt1->next->next;
        return dummy_head->next;
    }
};

That’s the beauty of algorithm!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值