链表结构
下面文章中的算法都会基于这个链表结构
public class ListNode {
int val;
ListNode next;
ListNode() {
}
ListNode(int val) {
this.val = val;
}
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
@Override
public String toString() {
return "ListNode{" + "val=" + val + '}';
}
}
工具类
用于生成链表结构,头插、尾插
public class LinkedListUtils {
ListNode head;
ListNode tail;
/**
* 头插
* @param val
*/
public void addFirst(int val){
if(head == null){
head = new ListNode(val);
return;
}
//新建一个node
ListNode node = new ListNode(val);
//指向头节点
node.next = head;
//换头
head = node;
}
/**
* 尾插
* @param val
*/
public void addLast(int val){
if(head == null && tail == null){
head = new ListNode(val);
tail = head;
return;
}
tail.next = new ListNode(val);
tail = tail.next;
}
public void addLast(ListNode node){
if(head == null && tail == null){
head = node;
tail = node;
return;
}
tail.next = node;
tail = tail.next;
}
/**
* 打印
*/
public void printList(){
ListNode tmp = head;
while (tmp != null){
System.out.print(tmp.val);
tmp = tmp.next;
}
}
}
判断一个链表是否有环
判断一个链表是否有环,如果有环,返回入环节点,如果没有返回null
方法一:使用HashSet结构,利用HashSet不可重复的特性
方法二:使用快慢指针
使用HashSet判断是否有环
import org.junit.Test;
import java.util.HashSet;
public class LinkListTest {
@Test
public void test(){
LinkedListUtils ringLinkedList = new LinkedListUtils();
ringLinkedList.addLast(1);
ringLinkedList.addLast(2);
ringLinkedList.addLast(3);
ListNode ringNode = new ListNode(4);
ringLinkedList.addLast(5);
ringLinkedList.addLast(ringNode);
ringLinkedList.addLast(6);
//放开注释为有环链表
//ringLinkedList.addLast(ringNode);
ListNode result = judgeLoopLinkedList(ringLinkedList.head);
System.out.println(result == null? "null" : result.toString());
}
private ListNode judgeLoopLinkedList(ListNode head){
HashSet<ListNode> nodeSet = new HashSet<>();
ListNode node = head;
while (node != null){
if(!nodeSet.add(node)){
return node;
}
node = node.next;
}
return null;
}
}
使用快慢指针判断是否有环
1)、快指针走两步,慢指针走一步
2)、相遇后,快指针回到头节点
3)、然后快指针走一步,慢指针也走一步,相遇节点为第一个相交节点
import org.junit.Test;
public class LinkListTest {
@Test
public void test(){
LinkedListUtils ringLinkedList = new LinkedListUtils();
ringLinkedList.addLast(1);
ringLinkedList.addLast(2);
ringLinkedList.addLast(3);
ListNode ringNode = new ListNode(4);
ringLinkedList.addLast(5);
ringLinkedList.addLast(ringNode);
ringLinkedList.addLast(6);
//放开注释为有环链表
//ringLinkedList.addLast(ringNode);
ListNode result = judgeLoopLinkedList(ringLinkedList.head);
System.out.println(result == null? "null" : result.toString());
}
private ListNode judgeLoopLinkedList(ListNode head){
if(head == null || head.next == null || head.next.next == null){
return null;
}
ListNode S = head.next;//慢指针走一步
ListNode F = head.next.next;//快指针走两步
while (S != F){
if(F.next == null || F.next.next == null){
return null;
}
F = F.next.next;
S = S.next;
}
//到这里表示成环,快指针回到头节点
F = head;
while (F != S){
//快慢指针都走一步
F = F.next;
S = S.next;
}
//快慢指针相遇
return F;
}
}
找相交节点
现有两个链表,不知道是否是带环链表,判断两个链表是否相交,如果相交返回相交节点,如果不相交返回null
相交情况分析
不管是有环还是无环,两个链表相交之可能是以下几种情况:
- 无环相交
- 有环相交,且入环节点相等
- 有环相交,但入环节点不相等
解决方案
- 判断是有环还是无环
- 如果是无环:分别找到end1、end2,并记录length1、length2,如果
end1 == end2,length较大的链表先走差值步,然后同时走,找到相等的节点,就是交点。如果end1 != end2,不相交
import org.junit.Test;
public class LinkedListTest {
@Test
public void test(){
ListNode crossNode = new ListNode(3);
LinkedListUtils linked1 = new LinkedListUtils();
linked1.addLast(1);
linked1.addLast(2);
linked1.addLast(crossNode);
linked1.addLast(4);
linked1.addLast(5);
linked1.addLast(6);
//linked1.printList();
LinkedListUtils linked2 = new LinkedListUtils();
linked2.addLast(7);
linked2.addLast(8);
linked2.addLast(9);
linked2.addLast(crossNode);
//不可以再添加节点,会导致linked1结构发生变化
//linked2.addLast(10);
//linked1.printList();
//linked2.printList();
ListNode node = findCrossNodeWithOutLoop(linked1.head, linked2.head);
System.out.println(node == null?null:node.toString());
}
/**
* 无环相交情况
* @param head1
* @param head2
* @return
*/
private ListNode findCrossNodeWithOutLoop(ListNode head1, ListNode head2){
if(head1 == null || head2 == null){
return null;
}
int n = 0;
ListNode end1 = head1;
ListNode end2 = head2;
while (end1.next != null){
n++;
end1 = end1.next;
}
while (end2.next != null){
n--;
end2 = end2.next;
}
//终点不相等,表示没有交点
if(end1 != end2){
return null;
}
end1 = n > 0 ? head1 : head2;
end2 = (end1 == head1) ? head2 : head1;
n = Math.abs(n);
while (n != 0){
n--;
end1 = end1.next;
}
while (end1 != end2){
end1 = end1.next;
end2 = end2.next;
}
return end1;
}
}
- 如果是有环:判断入环节点是否相等,如果相等,按无环处理(入环节点作为end1、end2)。 如果入环节点不相等,然后一个节点走一圈,如果相遇表示相交,否则不相交
import org.junit.Test;
public class LinkedListTest {
@Test
public void test(){
ListNode crossNode1 = new ListNode(4);
ListNode crossNode2 = new ListNode(9);
LinkedListUtils linked1 = new LinkedListUtils();
linked1.addLast(1);
linked1.addLast(2);
linked1.addLast(3);
linked1.addLast(crossNode1);
//注释下面一行,放开下面三行注释,为不相交的情况
linked1.addLast(crossNode2);
linked1.addLast(6);
linked1.addLast(crossNode1);
LinkedListUtils linked2 = new LinkedListUtils();
linked2.addLast(7);
linked2.addLast(8);
linked2.addLast(crossNode2);
//linked2.addLast(10);
//linked2.addLast(11);
//linked2.addLast(crossNode2);
ListNode cross1 = judgeLoopNode(linked1.head);
ListNode cross2 = judgeLoopNode(linked2.head);
ListNode node = findCrossNodeLoop(linked1.head, cross1, linked2.head, cross2);
System.out.println(node == null?null:node.toString());
}
/**
* 判断是否有环
* @param head
* @return
*/
private ListNode judgeLoopNode(ListNode head){
if(head == null || head.next == null || head.next.next == null){
return null;
}
ListNode F = head.next.next;
ListNode S = head.next;
while (F != S){
if(F.next == null || F.next.next == null){
return null;
}
F = F.next.next;
S = S.next;
}
F = head;
while (F != S){
F = F.next;
S = S.next;
}
return F;
}
/**
* 有环相交情况
* @param head1
* @param head2
* @return
*/
private ListNode findCrossNodeLoop(ListNode head1, ListNode cross1, ListNode head2, ListNode cross2){
ListNode end1 = null;
ListNode end2 = null;
if(cross1 == cross2){
end1 = head1;
end2 = head2;
int n = 0;
while (end1.next != cross1){
n++;
end1 = end1.next;
}
while (end2.next != cross2){
n--;
end2 = end2.next;
}
end1 = n > 0 ? head1 : head2;
end2 = (end1 == head1) ? head2 : head1;
while (n != 0){
end1 = end1.next;
n--;
}
while (end1 != end2){
end1 = end1.next;
end2 = end2.next;
}
return end1;
}else {
end1 = cross1.next;
while (end1.next != cross1){
if(end1 == cross2){
return cross2;
}
end1 = end1.next;
}
return null;
}
}
}
题解
import org.junit.Test;
public class LinkedListTest {
@Test
public void test(){
ListNode crossNode1 = new ListNode(4);
ListNode crossNode2 = new ListNode(9);
LinkedListUtils linked1 = new LinkedListUtils();
linked1.addLast(1);
linked1.addLast(2);
linked1.addLast(3);
linked1.addLast(crossNode1);
linked1.addLast(crossNode2);
linked1.addLast(6);
linked1.addLast(crossNode1);
LinkedListUtils linked2 = new LinkedListUtils();
linked2.addLast(7);
linked2.addLast(8);
linked2.addLast(crossNode2);
//获取入环节点,如果不是环返回null
ListNode cross1 = judgeLoopNode(linked1.head);
ListNode cross2 = judgeLoopNode(linked2.head);
ListNode node = null;
//无环
if(cross1 == null && cross2 == null){
node = findCrossNodeWithOutLoop(linked1.head, linked2.head);
}
//有环
if(cross1 != null && cross2 != null){
node = findCrossNodeLoop(linked1.head, cross1, linked2.head, cross2);
}
//一个有环,一个没环,肯定不相交
System.out.println(node == null?null:node.toString());
}
/**
* 判断是否有环
* @param head
* @return
*/
private ListNode judgeLoopNode(ListNode head){
if(head == null || head.next == null || head.next.next == null){
return null;
}
ListNode F = head.next.next;
ListNode S = head.next;
while (F != S){
if(F.next == null || F.next.next == null){
return null;
}
F = F.next.next;
S = S.next;
}
F = head;
while (F != S){
F = F.next;
S = S.next;
}
return F;
}
/**
* 无环相交情况
* @param head1
* @param head2
* @return
*/
private ListNode findCrossNodeWithOutLoop(ListNode head1, ListNode head2){
if(head1 == null || head2 == null){
return null;
}
int n = 0;
ListNode end1 = head1;
ListNode end2 = head2;
while (end1.next != null){
n++;
end1 = end1.next;
}
while (end2.next != null){
n--;
end2 = end2.next;
}
//终点不相等,表示没有交点
if(end1 != end2){
return null;
}
end1 = n > 0 ? head1 : head2;
end2 = (end1 == head1) ? head2 : head1;
n = Math.abs(n);
while (n != 0){
n--;
end1 = end1.next;
}
while (end1 != end2){
end1 = end1.next;
end2 = end2.next;
}
return end1;
}
/**
* 有环相交情况
* @param head1
* @param cross1
* @param head2
* @param cross2
* @return
*/
private ListNode findCrossNodeLoop(ListNode head1, ListNode cross1, ListNode head2, ListNode cross2){
ListNode end1 = null;
ListNode end2 = null;
if(cross1 == cross2){
end1 = head1;
end2 = head2;
int n = 0;
while (end1.next != cross1){
n++;
end1 = end1.next;
}
while (end2.next != cross2){
n--;
end2 = end2.next;
}
end1 = n > 0 ? head1 : head2;
end2 = (end1 == head1) ? head2 : head1;
while (n != 0){
end1 = end1.next;
n--;
}
while (end1 != end2){
end1 = end1.next;
end2 = end2.next;
}
return end1;
}else {
end1 = cross1.next;
while (end1.next != cross1){
if(end1 == cross2){
return cross2;
}
end1 = end1.next;
}
return null;
}
}
}