单链表-快慢指针-解决回文问题

先简单了解下:

单链表:

快慢指针:

slow:正常的指针,每次走一步

fast:每次走两步

回文问题:

noon、奥利奥、1221等等这种就是回文

 

如何用快慢指针解决单链表的回文问题呢?

看到noon 、 奥利奥这样的字眼,我们可以想

单链表-快慢指针-解决回文问题_第1张图片

以noon为例,我们可以从no  on 中间开始,一边往左,一边右,同时去判断,直到结束(遇到null了)

那么怎么找到这个中间位置呢?——快慢指针

为什么快慢指针能找到中间位置呢?——如果走n步的话,s(slow)走了n步,f(fast)走了2n步

这时候如果链表长度刚好为2n,那么s就在中间位置了呀

(这里先埋下个伏笔,链表的长度都一定是2的倍数(偶数)吗,通常遇到2n我们都要去考虑下奇偶数的问题)

那如果要使指针能够往左边走的话,我们是不是需要在原先的链表上逆序?(先不考虑破坏原链表的问题哈,我们这里只要结果)

逆序中我们需要几个指针呢?

1个?——肯定不行

2个?——不够,原因如下:

单链表-快慢指针-解决回文问题_第2张图片

假设现在我们有这样的链表,s、prev两个指针(先忽略fast)

如果此时,s要完成逆序,需要把s的next指向prev,那会发生什么呢?

单链表-快慢指针-解决回文问题_第3张图片

这时候可以发现,链表断掉了

那有人说了,我不用prev,我用next,就是这个

单链表-快慢指针-解决回文问题_第4张图片

如果这是一条很长的链表(1,2,3,4只是中间的某几个点的标注)

那么s如何找到它的前面一个结点呢?——再遍历一遍嘛——当然可以,你有钱任性(不管时间复杂度)

因此我们就选用三个指针好了 ,prev s 和next 这三个,这样就绝对万无一失了(怎么指向我都有原来的指针指着,不会丢)

单链表-快慢指针-解决回文问题_第5张图片

好了我们确定了用三个指针后,就是实现逆序的过程

第一步:改变s.next的指向

单链表-快慢指针-解决回文问题_第6张图片

第二步,prev去到s的位置(s准备去next的位置)

(注意这里用的并不是prev.next 中间过程的prev是指向前面的,也就是这样的)

单链表-快慢指针-解决回文问题_第7张图片

第三步,s去到next的位置(同样不是用s.next过去的哦!s.next现在已经指着1了而不是2)

单链表-快慢指针-解决回文问题_第8张图片

第四步,next移动到下一个位置(这时候可以用next.next了)

单链表-快慢指针-解决回文问题_第9张图片

这个逆序的过程其实是在s不断前进直到在中间位置的过程中进行

 

那怎么到中间位置呢?——靠的是fast来停止而不是slow哦

这是因为f走得快,容易到链表的结尾

刚才埋下的奇偶问题这里f也遇到了

也就是链表长度为奇数偶数时,f所在位置时不同的,同时也决定了要不要给s处理一下(是否再往后走一步,为什么?看下去)

先来看下为奇数为偶数时的s f情况(就先仅仅看s f移动,先不看逆序)

奇数:

单链表-快慢指针-解决回文问题_第10张图片

偶数:

单链表-快慢指针-解决回文问题_第11张图片

(f每次走就是 f. next.next)

可以看到到应该停止下来的地方时 奇偶不同 f的位置也不同

就是 奇数:f.next==null  偶数: f==null

这时我们再结合逆序来看下s(之后就是判断回文了,此时需要判断下s的情况)

奇数情况(此时就不再看f了 因为已经找到中间位置了)

单链表-快慢指针-解决回文问题_第12张图片

可以看到如果需要进行比较的话肯定是prev和s间进行比较,而此时还需要让s走下一步,这样才到开始判断的地方(也就是c并不需要判断),即

单链表-快慢指针-解决回文问题_第13张图片

不断 判断 和 用next移到各自下一个,直到遇到null

偶数情况:

单链表-快慢指针-解决回文问题_第14张图片

此时可以看到,不需要对s进行额外的操作就可以开始判断回文

到这里,就全部解析完用快慢指针 解决单链表回文问题

 

当然解决单链表回文问题还可以用其他方法解决 例如借助堆栈

https://blog.csdn.net/tangli555/article/details/51384296

用快慢指针只是因为我第一次学的时候就是用这个来解决,而且这个相比借助用堆栈也比较难理解一丢丢丢丢丢丢

借鉴代码:

https://www.jianshu.com/p/462fa3e4cd43

class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null || head.next == null) {
return true;
}
ListNode prev = null;
ListNode slow = head;
ListNode fast = head;

#去找中间位置,同时把前面部分逆序
while (fast != null && fast.next != null) {
  fast = fast.next.next;
  ListNode next = slow.next;
  slow.next = prev;
  prev = slow;
  slow = next;
}

#这是为奇数时对s额外的操作
if (fast != null) {
  slow = slow.next;
}

#开始判断回文
while (slow != null) {
  if (slow.val != prev.val) {
    return false;
  }
  slow = slow.next;
  prev = prev.next;
}

return true;

}
}

 

你可能感兴趣的:(算法)