【题目】
判断两个链表(链表可以有环,可以无环)是否相交,若相交,则返回相交节点,不相交则返回null
1.判断两个链表是否相交
【思路】
两个链表分三种情况:
①两个链表均为非环链表;
②两个链表一个为环,一个非环;
③两个链表均为环
第一种情况非环链表根据链表的定义,只需比较两个链表尾节点是否相同,若相同则一定相交,不同则一定不相交;
第二种情况由于链表的next指针永远只指向一个节点,所以若环链表与非环链表相交非环链表一定为环,所以此情况一定两个链表不相交;
第三种情况又可以分为三种情况:1.两个环链表不相交;2.两个环链表在结环点之前相交;3.两个链表在环上相交
此时只需找到某一链表b的结环节点,在另一链表a遍历自己至第二次到自己结环点的时候此时链表a一定遍历了自身所有节点,判断a链表有无节点与b链表相遇,若相遇,则两环链表相交,若不相遇,则说明一定不相交。
设计一个处理链表的工具类NodeListUtils
判断链表是否为环的方法:
/**
* 判断链表是否有环
* @param nodeList
* @return
*/
public static <T> boolean isRing(NodeList<T> nodeList) {
Node<T> head = nodeList.head;
Node<T> fast = head;
Node<T> slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
return true;
}
}
return false;
}
获取非环单链表尾节点的方法:
/**
* 获取单链表的最后一个节点
*
* @param nodeList
* @return
*/
public static <T> Node<T> getLastNode(NodeList<T> nodeList) {
if (nodeList == null || nodeList.head == null) {
return null;
}
Node<T> node = nodeList.head;
while (node.next != null) {
node = node.next;
}
return node;
}
获取结环点的方法:
/**
* 寻找链表的结环的节点
* @param nodeList
* @return
*/
public static <T> Node<T> getRingNode(NodeList<T> nodeList) {
Node<T> head = nodeList.head;
Node<T> fast = head;
Node<T> slow = head;
Node<T> newNode = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
while (slow != null) {
if (newNode == slow) {
return newNode;
}
newNode = newNode.next;
slow = slow.next;
}
}
}
return null;
}
最后,判断两个链表是否相交:
/**
* 判断两个链表是否相交
*
* @param list1
* @param list2
* @return
*/
public static <T> boolean isTouch(NodeList<T> list1, NodeList<T> list2) {
boolean b1 = isRing(list1);// 是否为环
boolean b2 = isRing(list2);// 是否为环
if (b1 && !b2 || !b1 && b2) {//一个链表为环一个不为环
return false;
}
if (!b1 && !b2) {//两个链表都不为环
if (getLastNode(list1) == getLastNode(list2)) {
return true;
}
return false;
} else {//两个链表都为环
Node<T> ringNode = getRingNode(list2);// 返回结环点
Node<T> node = list1.head;
boolean flag = false;
while (!flag || getRingNode(list1) != node) {
if (node == ringNode) {
return true;
}
if (getRingNode(list1) == node) {
flag = true;
}
node = node.next;
}
return false;
}
}
2.现在,判断的问题已经解决了,就剩下查找的问题了,同样的分三种情况:
①两个链表均为非环链表;
②两个链表一个为环,一个非环;
③两个链表均为环
第一种情况,两个非环链表,先比较两个链表的长度,较长的链表先走长度的差数,然后两个链表一起移动,若指针相遇则返回此节点,否则直到遍历完返回null;
第二种情况同样不可能相交,返回null;
第三种情况分为三种情况:1.两个环链表不相交;2.两个环链表在结环点之前相交;3.两个链表在环上相交
三-1返回null,三-2可以看作在结环点之前两个链表就是非环链表,所以可以复用第一种情况,三-3随意返回一个结环点就行
获取链表长度的方法,非环单链表返回长度,环链表返回到结环点的长度:
/**
* 非环单链表返回长度,
* 环链表返回到结环点的长度
* @param list
* @return
*/
public static <T> int length(NodeList<T> list) {
Node<T> ringNode = getRingNode(list);
Node<T> node = list.head;
int size = 0;
while (node != ringNode) {
node = node.next;
size++;
}
return size;
}
查找非环单链表的相交节点的方法:
/**
* 查找非环单链表的相交节点
*
* @param list1
* @param list2
* @return
*/
public static <T> Node<T> findSingleTouchNode(NodeList<T> list1,
NodeList<T> list2) {
int len1 = length(list1);
int len2 = length(list2);
if (len1 < len2) {
NodeList<T> temp = list2;
list2 = list1;
list1 = temp;
}
Node<T> node1 = list1.head;
Node<T> node2 = list2.head;
int i = Math.abs(len1 - len2);
while (i-- > 0) {
node1 = node1.next;
}
while (++i < Math.min(len1, len2)) {
if (node1 == node2) {
return node1;
}
node1 = node1.next;
node2 = node2.next;
}
return null;
}
最后,寻找两个链表的相交节点的方法:
/**
* 寻找两个链表的相交节点
*
* @param list1
* @param list2
* @return
*/
public static <T> Node<T> findTouchNode(NodeList<T> list1, NodeList<T> list2) {
boolean b1 = isRing(list1);// 是否为环
boolean b2 = isRing(list2);// 是否为环
if (b1 && !b2 || !b1 && b2) {
return null;
}
if (!b1 && !b2) {
return findSingleTouchNode(list1, list2);
} else {
if(getRingNode(list1) == getRingNode(list2)) {
return findSingleTouchNode(list1, list2);
}
Node<T> ringNode = getRingNode(list2);// 返回结环点
Node<T> node = list1.head;
boolean flag = false;
while (!flag || getRingNode(list1) != node) {
if (node == ringNode) {
return node;
}
if (getRingNode(list1) == node) {
flag = true;
}
node = node.next;
}
}
return null;
}
测试:
public static void main(String[] args) {
NodeList<Integer> nodeList = new NodeList<>();
NodeList<Integer> nodeList1 = new NodeList<>();
Node<Integer> node = new Node<Integer>(100);
Node<Integer> node2 = new Node<Integer>(200);
Node<Integer> node3 = new Node<Integer>(300);
Integer[] arr = {1,3,5,7,9};
Integer[] arr1 = {33,22,11,55};
nodeList.createNodeList(arr);
nodeList1.createNodeList(arr1);
nodeList.insert(5, node);
nodeList1.insert(4, node);
nodeList.insert(6, 30);
nodeList.insert(7, node2);
nodeList.insert(8, 80);
nodeList.insert(9, 90);
nodeList.insert(10, 99);
nodeList.insert(11, node3);
node3.next = node2;
Node<Integer> node4 = NodeListUtils.findTouchNode(nodeList, nodeList1);
System.out.println(node4);
System.out.println(NodeListUtils.isTouch(nodeList, nodeList1));
}
打印
Node [data=100]
true