微软亚院之编程判断俩个链表是否相交 给出俩个单向链表的头指针,比如 h1,h2,判断这俩个链表是否相交。 为了简化问题,我们假设俩个链表均不带环。 问题扩展: 1.如果链表可能有环列? 2.如果需要求出俩个链表相交的第一个节点?
1.先了解一下什么是有环链表
2.查找判断是否相交的方法
3.求出列表相交的第一个节点。
有环的链表指的是链表有环路,例如下面:A->B->C->D->B,这样遍历的时候B->C->D->B就形成一个环路一直循环。
环链表
上面的知识中讲解了如何判断单向链表是否含有环。有三种方式:
1.第一种方式很好理解,就是用散列表数据结构中来保存已遍历的节点,如果当前遍历的节点存在于上面的数据结构中,那说明存在环。 时间复杂度O(n)
2.指针反转:指能通过表尾部节点找到表头的链表。在有环的情况下,表指针会通过反转找到表头,那么遍历完成后,只需要判断最后一个节点是否是标头就行。时间复杂度为O(n),但是该方法破坏了表的结构,遍历结束后,需要还原。
3.快慢指针:fast指针移动2个位置,slow指针每次移动一个指针,如果在某一个位置中,快慢指针如果能相遇,说明有环。
链表相交
1.直接法:循环遍历链表1种所有节点,然后再判断该节点是否存在于链表2种。时间复杂度O(n2).
2.利用计数。有点复杂,需要消化。
以节点地址遍历第一个链表,保存所有地址到一个hash表中
遍历第二个链表,判断当前节点的地址是否存在于上面的hash表中
如果存在,则有相交
时间复杂度O(n).
3.利用最后一个节点:如果相交的话,那么最后一个节点肯定是相同的。那么每个链表只需要遍历到最后一个节点,然后比较2个最后节点的地址是否相同就ok啊。时间复杂度O(n).
总结:判断是否有环用方法3,是否相交用方法2。求第一个相交节点也就方便许多。
public class Banana { //保存相交节点 LinkedListNode bananaNode = null; /** * 判断链表是否有环 */ public boolean isLoop(LinkedListNode node) { LinkedListNode fastNode; LinkedListNode slowNode; // 初始化快慢指针 fastNode = node; slowNode = node; while (fastNode != null && fastNode.getNextNode() != null) { // fast指针每次前进2步 fastNode = fastNode.getNextNode().getNextNode(); // slow指针每次前进1步 slowNode = slowNode.getNextNode(); if (fastNode == slowNode) { return true; } } return false; } /** * 判断链表是否相交 */ public boolean isBanana(LinkedListNode node1, LinkedListNode node2) { // 首先判断链表是否存在环 boolean isLoop1 = isLoop(node1); boolean isLoop2 = isLoop(node2); if (isLoop1) { System.out.println("链表1存在环"); } if (isLoop2) { System.out.println("链表2存在环"); } // 只有当两者都不存在环或者都存在环才有相交的可能性。 List<Integer> hashList = new ArrayList<Integer>(); if (isLoop1 == isLoop2) { // 遍历第一个链表 while (node1 != null) { hashList.add(node1.hashCode()); node1 = node1.getNextNode(); } while (node2 != null) { if (hashList.contains(node2.hashCode())) { System.out.println("链表相交"); bananaNode = node2; return true; } node2 = node2.getNextNode(); } } return false; } public static void main(String[] args) { } }