两个链表第一个公共子节点
链表只能有一个后继节点
哈希和集合
遍历第一个链表,将所有元素全部存到Map中。遍历第二个方法 当Hash中存在了每个元素,那么就找到了公共子节点。
/**
* 方法1:通过Hash辅助查找
*
* @param pHead1
* @param pHead2
* @return
*/
public static ListNode findFirstCommonNodeByMap(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
ListNode current1 = pHead1;
ListNode current2 = pHead2;
HashMap<ListNode, Integer> hashMap = new HashMap<ListNode, Integer>();
while (current1 != null) {
hashMap.put(current1, null);
current1 = current1.next;
}
while (current2 != null) {
if (hashMap.containsKey(current2)){
return current2;
}
current2 = current2.next;
}
return null;
}
双指针
由图可知,两个链表的后方是相同的,那么如果我链表长度长的先出发,走到长度差之后,再一起走,是不是就能遇到公共子节点了。
/**
* 方法5:通过差值来实现
*
* @param pHead1
* @param pHead2
* @return
*/
public static ListNode findFirstCommonNodeBySub(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
ListNode current1 = pHead1;
ListNode current2 = pHead2;
int l1 = 0, l2 = 0;
while (current1 != null) {
current1 = current1.next;
l1++;
}
while (current2 != null) {
current2 = current2.next;
l2++;
}
current1 = pHead1;
current2 = pHead2;
int sub = l1 > l2 ? l1 - l2 : l2 - l1;
if (l1 > l2) {
int a = 0;
while (a < sub) {
current1 = current1.next;
a++;
}
}
if (l1 < l2) {
int a = 0;
while (a < sub) {
current2 = current2.next;
a++;
}
}
while (current2 != current1) {
current2 = current2.next;
current1 = current1.next;
}
return current1;
}
拼接字符串方法
原理:AB BA字符串 如果有公共子节点,一定会在后方出现相同元素
A: 0 - 1 - 2 - 3 - 4 - 5
B: a- b - 4 - 5
AB:0-1-2-3-4-5-a-b-4-5
BA:a-b-4-5-0-1-2-3-4-5
right_a 和 right_b 是一样的
/**
* 寻找第一个公共子节点 拼接
*
* @param head 第一个链表
* @param head2 第二个链表
*/
public static ListNode findFirstWithStr(ListNode head, ListNode head2) {
if (head == null || head2 == null) return null;
ListNode p1 = head;
ListNode p2 = head2;
while (p1 != p2) {
p1 = p1.next;
p2 = p2.next;
if (p1 != p2) {
if (p1 == null) {
p1 = head2;
}
if (p2 == null) {
p2 = head;
}
}
}
return p1;
}
注意:为什么if (p1 != p2),因为如果出现两个不相交的链表,会出现死循环,但是如果加了该判断,两个链表一定会出现都为null的情况
双指针专题
寻找倒数第K个节点
输入一个链表,输出该链表中倒数第k个节点。本题从1开始计数,即链表的尾节点是倒数第1个节点。
1 -> 2 -> 3 -> 4 -> 5, k=2
通过快慢指针,fast指针先走k个单位,但实际位置在k+1,此时slow指针和fast指针同时移动,因为fast相单于多移动了一步,所以在后面时也需要多走一步,所以while中的条件为fast != null
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;
}
链表旋转
倒数第k个元素的方式
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;
}
注意:第二次while (fast.next != null)的时候,因为我们要遍历到最后一个节点 而不是最后一个节点的后一个节点,所以要注意这里的判断条件,包括后面的res节点取的是slow节点的next节点,因为此时相当于是倒数第k-1个节点
注意点:
什么时候是 node != null 什么时候是 node.next != null
如果是node != null:
- 节点通常会遍历到最后一个节点的后一个几点,最后的节点为null。如果是快慢指针,慢指针会走到中间节点。如寻找倒数第K个节点时。
- 对于偶数寻找到中间节点。
如果是node.next != null:
- 节点会遍历到最后一个节点,此时节点为lastNode。如果是快慢指针,慢指针会停留在最后一个节点的前一个节点。如翻转链表:找倒数第K个节点时。
- 对于技术寻找到中间节点时。
当快慢指针的快指针每次走两格时,一般会将两个条件一起添加。如果出现对next.next先赋值在做操作时,记得判空。