快慢指针
快慢指针可以实现的目标是,针对无法预知长度的链表,我们可以做到通过快慢指针的步幅差,定位到任意等分位置。
最经典的是定位到中间位置,即快指针一次走两步,慢指针一次走一步,当快指针走到链表尾部时,慢指针一定在链表的中间节点(奇数时为正中间,偶数时为后半部头节点)
例题 leetcode-234 回文链表
要实现O(1)的空间复杂度时,就需要使用快慢指针,通过对某一部分的链表反转后再进行两部分链表的比较就可以判断是否为回文链表。
具体解析在代码中体现。
public boolean isPalindrome(ListNode head) {
//快慢指针方法
/**
* 方法描述:
* 判断是否为回文链表的关键就是从中间开始,往两边走或者从两头开始,往中间走
* 在给的数据为链表的情况下,其实就需要反转某一部分的链表(这里我们把链表分为前部和后部)
* 反转完链表后再进行逐个比较,我们可以选择反转前部链表也可以选择反转后部链表。
* 我们下面的方法中选择反转前部链表,并且通过快慢指针实现。
* 快慢指针可以实现的是,快指针走到链表尾部时,满指针刚好到链表在的中间部分。
* 具体是:快指针每次走两步,慢指针每次走一步
*/
// 快慢指针定义,一开始都在头节点处
ListNode faster = head, slower = head;
// 反转链表所需辅助节点变量
// 其中每次到一个新节点时,将tmp节点设置为当前节点,并将tmp节点的next节点设为pre节点
// 最后将pre节点设置为tmp节点,这就实现了头插法,也就将链表反转了
ListNode pre = null , tmp = null;
if (head.next == null) return true;
// 因为要走两步,所以要判断是不是next节点是不是为空
// 这边代码的顺序十分重要!!!!!
// 当把slower赋值给tmp后,不能马上对pre进行头插法交换,因为这样会导致slower的next变成上一个元素,导致死循环
while (faster != null && faster.next != null) {
tmp = slower;
slower = slower.next;
faster = faster.next.next;
tmp.next = pre;
pre = tmp;
}
// 判断走的是奇数还是偶数,如果是奇数,那么faster最后刚好到尾节点,那么slower节点刚好在正中间
// 如果是偶数,那么faster超过尾节点,变成null,slower节点的位置则在链表后半部分的头节点
if (faster != null) slower = slower.next;
while (pre != null && slower != null) {
if (pre.val != slower.val) return false;
pre = pre.next;
slower = slower.next;
}
return true;
}