【题目】
2.现在,判断的问题已经解决了,就剩下查找的问题了,同样的分三种情况:
最后,寻找两个链表的相交节点的方法:
测试:
判断两个链表(链表可以有环,可以无环)是否相交,若相交,则返回相交节点,不相交则返回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