这一期简单介绍一下双指针的使用,使用它可以简单高效解决很多问题。
链表结构如下:
public class ListNode {
Integer val;
ListNode next;
public ListNode(Integer val) {
this.val = val;
}
public String toString(ListNode head) {
StringBuilder builder = new StringBuilder();
while (head != null){
builder.append(head.val + "\t");
head = head.next;
}
return builder.toString();
}
}
1.寻找链表的中间节点
给定一个链表的头节点,要求返回链表的中间节点。如果链表节点为双数,则返回第二个中间节点
一般思路:
一般能想到的是,将链表遍历一遍得到它的长度len,然后 len/2 向下取整得到 size,然后再将头指针往后拨 size步,得到的就是中间节点。可行,但感觉太常规。这里看一下双指针的解法
双指针:
给两个指针: fast 和 slow,fast每次走两步,slow每次走一步,当 fast 走到尾节点(链表长度为单数),或者尾节点指向的 null(链表长度为复数)时,slow指向的就是中间节点,代码如下:
//寻找中间节点
public static ListNode findMiddleNode(ListNode head) {
ListNode fast = head;
ListNode slow = head;
//第一个条件针对聊表个数为复数时跳出循环,第二个条件链表个数为单数跳出循环
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
2.寻找链表的倒数第K个元素
输入一个链表,输出该链表中倒数第k个节点。本题从1开始计数,即链表的尾节点是倒数第1个节点。
这里一般思路大概也是先遍历一遍链表得到长度 len,len 减去 k 得到 size,然后让指针走 size 步得到节点,但是用双指针,遍历一遍链表就能解决:
给两个指针: fast 和 slow,fast 指针先走 k 步,然后快慢指针一起走,快指针走到表尾的next,也就是null时,慢指针指向的就是倒数第 k 个节点。
因为k有可能比链表长度还大,所以加一个fast不为空的判断:
//寻找倒数第k个元素
public static ListNode findKthFromEnd(ListNode head, int k) {
ListNode fast = head;
ListNode slow = head;
//逻辑是先让快指针先走K步,然后快慢指针一起走,快指针走到表尾的next,也就是null时,慢指针指向的就是倒数第 k 个节点
//因为k有可能比链表长度还大,所以加一个fast不为空的判断
while (fast != null && k > 0) {
fast = fast.next;
k--;
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
3.将链表右移K个位置
这里观察一下题目,分析一下,后面的部分右移旋转到前面,右移k个位置。新链表尾部时原链表尾部的倒数第 (k-1) 个节点,而且右移两个位置后,节点4 还是在节点5前面,次序不变。
也就是说,右移 2 个位置,就是在倒数第2个节点 (节点4) 前面断开,然后将原尾节点(结点5)的next指向原头结点,新链表就形成了。
那么现在问题就简化成了寻找倒数 (k-1)个节点,变量保存它的next 节点作为新的头节点,并且将原尾节点指向原头节点,返回新的头节点,代码如下:
/**
* @param head
* @param k 右移k个位置
* @return
*/
//向右移旋转链表
public static ListNode rotateRight(ListNode head, int k) {
ListNode fast = head;
ListNode slow = head;
//k可能比链表长度还长,要先计算出链表长度,然后给k取模
int len = 0;
ListNode cur = head;
while (cur != null) {
len++;
cur = cur.next;
}
if (len == 0) {
return head;
}
k = k % len;
//快指针先走k步
while (k!=0){
fast = fast.next;
k--;
}
//快指针走到原表表尾,慢指针指向右移完成后的表尾元素
while (fast.next != null){
fast = fast.next;
slow = slow.next;
}
ListNode newHead = slow.next;
fast.next = head;
slow.next = null;
return newHead;
}
简单介绍双指针三种用法,可以看到共同点都是寻找链表中的某个点。在想清题目的逻辑后,使用双指针就可以快速地定位到某个点。不必先遍历一遍得出长度,再计算出头节点要走地步数。在更复杂的题里,如果两个指针不够,还可以再增加指针~
目录