数据结构—判断两个链表是否相交,寻找两个链表的相交节点

【题目】

判断两个链表(链表可以有环,可以无环)是否相交,若相交,则返回相交节点,不相交则返回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

点赞