双指针专题
初尝数据结构,第一次接触到双指针思想,觉得很有趣。很多问题迎刃而解的同时,也深深体会到了数据结构和算法的魅力所在。仿佛回到了高中全神贯注钻研圆锥曲线和导数,后百思终得解的那个晚自习。
简述
双指针,顾名思义就是通过两个指针的移动来解决问题。定义两个变量,通过其移动的先后顺序,“速度”差、间距等等特性,自由灵活的进行选择和变通,使得某些场景下的问题大大简化或更加方便的得以解决。
以下看几道题来体验其思想。题述部分偷懒直接贴图了哈~重在领悟doge。
寻找中间节点
题述
题解
使用双指针思想中的“快慢指针”思想很好解决。用两个指针slow与fast,两个指针同时遍历,slow每次走一步,fast走两步,这样两人…阿不…两指针中“慢慢”走的永远是“快快”的一半,也就是说,当fast走到末尾,slow恰好在链表的中间。怎么说,问题迎刃而解!一道经典面试题就在双指针的帮助下被你轻松干掉了!
public static ListNode middleNode(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
寻找倒数第K个元素
题述
输入一个链表,输出该链表中倒数第k个节点。
本题从1开始计数,即链表的尾节点是倒数第1个节点。
示例:
给定一个链表:1->2->3->4->5,和k = 2.
返回链表4->5.
题解
这也是经典的快慢双指针问题。我们还是一样,定义fast和slow两个指针。先将fast向后遍历到第K+1个结点的位置,slow这时候不动还是指向链表第一个节点,此时fast和slow的间距刚好为K。之后让两个指针同时遍历向后走,当fast走到链表尾部的空节点时,slow刚好在倒数第K的位置。
注意,链表长度可能小于K。因此寻找k位置时必须要判断fast是否为null。
打个比方:这种“正倒”的问题,可以想象成是一个横向装满水和油的密封柱体。假设容量为100,你放进去70的水,那剩下30就是油。交界处的竖线想象成“指针”,如果我告诉你这个“指针”左侧有60的水,问你油有多少,那自然是40了。emn这个比方有点奇怪,但是我自己可以get到那种正向与反向的关系,此处的快慢指针也是利用“容器底”和“分界线”K的关系来处理的,这里感觉比喻的不大恰当,只分享一下我脑瓜子里的奇思妙想,大家按自己好理解的方式去消化就好~
public static ListNode getKthFromEnd(ListNode head, int k) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && k > 0) {
fast = fast.next;
k--;
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
旋转链表
题述
题解
两种思路:
第一种:将整个链表反转成{5,4,3,2,1},然后再将前K和N-K两个部分分别反转,也就是{5,4}和{1,2,3},然后再拼接在一起就可以了。
第二种:先用双指针思想找到倒数K的位置,然后切开成{1,2,3}和{4,5}两个链,再颠倒拼接就可以了。还是要注意k有可能大于链表长度的问题,所以先获取一下链表长度len,然后k = k%len,如果k == 0,则不用旋转,直接返回头结点。否则执行以下逻辑:
1.快指针先走k步。
2.快慢指针一起走。
3.快指针走到链表尾部,此时慢指针刚好在要断开的位置。把快指针指向的节点连到原链表头部,慢指针指向的节点断开和下一节点的联系。
4.返回结束时慢指针指向节点的下一节点。
public static ListNode rotateRight(ListNode head, int k) {
if (head == null || k == 0) {
return head;
}
ListNode temp = head;
ListNode fast = head;
ListNode slow = head;
int len = 0;
while (head != null) {
head = head.next;
len++;
}
if (k % len == 0) {
return temp;
}
while ((k % len) > 0) {
k--;
fast = fast.next;
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
ListNode res = slow.next;
slow.next = null;
fast.next = temp;
return res;
}