本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
本文的几个问题都是非常经典的链表反转问题,这里主要采用双指针的方式来解决这些问题,这些问题都来自牛客网 面试必刷TOP101
反转链表
解题思路:
-
使用栈:这里最先想到的就是使用栈,栈的最基本特征就是先进后出,将链表全部放入栈中,再取出就反转了链表。
-
双指针:然后就是双指针,我们可以使用cur指向当前待反转节点,和使用pre指向已经翻转节点的头节点,再将cur指向pre成为已经反转链表的头节点,当cur指向null的时候就是说明,所有的链表反转完成。然后返回 pre。 整个过程图解如下:
如果直接将cur和pre互换的话会造成前面的链表丢失,所以从cur换到pre的过程我们需要一个临时的指针temp指针指向cur.next,当cur指向pre的时候我们可以通过temp指针找回下一个需要反转的节点,让cur指向。
这样就可以使用下面这段代码进行反转
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
完整答案:
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head == null){
return head;
}
ListNode cur = head;
ListNode pre = null;
while(cur != null){
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
这里我们可以看待两个指针一直再指向不同的节点,来进行重新指向的问题。
给定区间链表反转
解题思路
- 和上面的区别就是:上面的是要将整个链表反转过来,而这个题目只要将中间部分反转。这里有个问题就是,这里我们会想到记录好区间的前一个节点和后一个节点。通过上面的反转,然后直接指向就可以了。
- 我们还是可以使用双指针的方式,使用pre指向待反转链表的前一个节点,cur初始链表中要反转区间的第一个节点,pre指向cur的后一个节点cur.next这里我们还是使用temp这个临时节点来记录。
首先我们将cur指向temp.next
然后我们将temp指向pre.next
最后我们将pre指向temp
一个细节: 为什么在第二步的时候我们不直接temp指向cur呢?如果只是第一张图可能觉得好像没有问题,但是我们要保证的是每次cur后面的节点插入到pre和pre.next中间的节点之中去,所以我们需要像第二步那样指向。而如果按照我们这个问题来,那将会有多个节点指向cur
因此我们的核心代码就出现了
ListNode temp = cur.next;
cur.next = temp.next;
temp.next = pre.next;
pre.next = temp;
完整答案:
在上面分析的基础上,我们还需要添加一个头节点,如果反转区间为0开始那就没有前置节点,所以我们需要一个添加一个头节点
public class Solution {
public ListNode reverseBetween (ListNode head, int m, int n) {
// write code here
ListNode res = new ListNode(-1);
res.next = head;
ListNode pre = res;
ListNode cur = head;
//找到区间开始节点
for(int i = 1; i < m; i++){
pre = cur;
cur = cur.next;
}
for(int i = m; i < n; i++){
ListNode temp = cur.next;
cur.next = temp.next;
temp.next = pre.next;
pre.next = temp;
}
return res.next;
}
}
对比
和上面一题做对比,两题都是双指针解决这个问题,但是思路是不一样的,前一题的解决方法中,指针cur和pre一直在转换节点,本题的这两个指针指向的节点一直没有转变,看似相似都是双指针,但是双指针的运用是完全不一样的。
按组翻转
解题思路
这题的核心代码部分和第一种是一样的,但是运用到了嵌套的思维。 那就是每翻转一组,我就将剩下的头和尾再翻转一次,所以这样判断循环结束的条件就不是cur == null了
而是cur是否为下一组的开头,这里我们就设置它为tail。
这里只标注了第一组的tail,使用嵌套每做完一组就标记下一组的tail
完整代码
public class Solution {
public ListNode reverseKGroup (ListNode head, int k) {
// write code here
ListNode tail = head;
for(int i = 0; i < k; i++){
if(tail == null){
return head;
}
tail = tail.next;
}
//第一部分的核心代码
ListNode pre = null;
ListNode cur = head;
while(cur != tail){
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
//使用嵌套
head.next = reverseKGroup(tail,k);
return pre;
}
}