单链表相交问题

两个单链表相交的一系列问题

  1. 单链表可能有环,也可能无环
  2. 两个单链表可能相交也可能不相交。
    若相交,请返回相交的第一个节点,若不相交请返回null

逻辑如下:
根据有环和无环可以分为三种情况:

1. 两个链表都无环,可能相交“Y”,可能不相交
2. 一个有环,一个无环,则不可能相交
3. 两个都有环,可能会出现三种情况 

如下如所示
在这里插入图片描述

class Node{
	int data;
	Node next;
	public Node(int data){
		this.data = data;
	}
}

1. 判断有环无环: 有环返回第一个进入环的节点,无环返回null

1.1 HashSet方法

将节点一个一个添加进HashSet中,出现重复的元素(不是节点的值,是节点本身)证明有环

	public static Node getLoopNode(Node head) {
        if(head == null)
            return head;
        HashSet<Node> set = new HashSet<>();
        Node cur = head;
        while(cur != null) {
            if(set.contains(cur))
                return cur;
            set.add(cur);
            cur = cur.next;
        }
        return null;
    }

1.2 快慢指针方法

若快指针(一次两步)等于慢指针(一次一步),则有环,此时让快指针从头开始,快慢指针再次重新移动直至相遇,返回相遇节点。

	public static Node getLoopNode1(Node head){
        if(head == null || head.next == null || head.next.next == null)
            return null;
        Node fast = head.next.next;
        Node slow = head.next;
        while(fast != slow){
            if( fast.next == null || fast.next.next == null)
                return null;
            fast = fast.next.next;
            slow = slow.next;
        }
        fast = head;
        while(fast != slow){
            fast = fast.next.next;
            slow = slow.next;
        }
        return fast;

    }

2. 根据有无环判断单链表相交情况

2.1 无环

在这里插入图片描述
方法一:暴力法
两次循环寻找第一个公共节点:T(m, n) = O( m n mn mn)

	public Node getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null)
            return null;
        HashSet<ListNode> set = new HashSet<>();
        ListNode cur = headA;
        while(cur != null){
            set.add(cur);
            cur = cur.next;
        }
        cur = headB;
        while(cur != null){
            if(set.contains(cur))
                return cur;
            cur = cur.next;
        }
        return null;
        
    }

方法二:哈希表发

	public Node getIntersectionNode(Node headA, Node headB) {
        if(headA == null || headB == null)
            return null;
        HashSet<ListNode> set = new HashSet<>();
        Node cur = headA;
        while(cur != null){
            set.add(cur);
            cur = cur.next;
        }
        cur = headB;
        while(cur != null){
            if(set.contains(cur))
                return cur;
            cur = cur.next;
        }
        return null;
        
    }

方法三: 双指针法

思路:
指针 pA 指向 A 链表,指针 pB 指向 B 链表,依次往后遍历
如果 pA 到了末尾,则 pA = headB 继续遍历
如果 pB 到了末尾,则 pB = headA 继续遍历
比较长的链表指针指向较短链表head时,长度差就消除了
如此,只需要将最短链表遍历两次即可找到位置

简而言之就是让两个指针走的路程长度一样多,第二次必定会相遇。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public Node getIntersectionNode(Node headA, Node headB) {
    if (headA == null || headB == null) return null;
    Node pA = headA, pB = headB;
    while (pA != pB) {
        pA = pA == null ? headB : pA.next;
        pB = pB == null ? headA : pB.next;
    }
    return pA;
}

	public static Node noLoop(Node head1, Node head2) {
		if (head1 == null || head2 == null) {
			return null;
		}
		Node cur1 = head1;
		Node cur2 = head2;
		int n = 0;
		while (cur1.next != null) {
			n++;
			cur1 = cur1.next;
		}
		while (cur2.next != null) {
			n--;
			cur2 = cur2.next;
		}
		if (cur1 != cur2) {
			return null;
		}
		cur1 = n > 0 ? head1 : head2;
		cur2 = cur1 == head1 ? head2 : head1;
		n = Math.abs(n);
		while (n != 0) {
			n--;
			cur1 = cur1.next;
		}
		while (cur1 != cur2) {
			cur1 = cur1.next;
			cur2 = cur2.next;
		}
		return cur1;
	}

方法四:两个辅助栈
将两个链表元素分别入两个栈,出栈前先保存节点,直到两个节点不相等时则返回上一个节点

public static Node noLoop(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        Stack<Node> stack1 = new Stack<>();
        Stack<Node> stack2 = new Stack<>();
        Node cur1 = head1;
        Node cur2 = head2;
        while(cur1 != null){
            stack1.push(cur1);
            cur1 = cur1.next;
        }
        while(cur2 != null){
            stack2.push(cur2);
            cur2 = cur2.next;
        }
        //栈顶不相等,说明没有共同元素,返回null
        if(stack1.peek() != stack2.peek())
        	return null;
        while(!stack1.empty() && !stack2.empty()){
            Node temp = stack1.peek();
            stack1.pop();
            stack2.pop();
            if(stack1.peek() != stack2.peek()) //下一个不相等返回上一个
                return temp;
        }
        return null;
    }

2.2 一个有环一个无环

一个有环一个无环必定没有公共节点,返回空即可

2.3 都有环

在这里插入图片描述
有环分为三种情况:
逻辑:用是否有入环相同节点来划分
将情况划为两种情况:

  1. loop1 == loop2, 此时就是图中情况二,将环忽略(就是将loop1或者loop2作为结束节点),将这种情况化成了上面无环的情况(区别:这种情况必定有相同节点)
  2. loop1 != loop2, 此时就是让loop2作为结束节点,loop1作为开始节点,loop1开始遍历
    1. 若有loop1 == loop2的情况,那loop1或者loop2都是第一个开始节点,返回一个即可;
    2. 若到了结束节点loop2没有相等的情况,则返回null。
	public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
		Node cur1 = null;
		Node cur2 = null;
		if (loop1 == loop2) {  //装换成无环的结构链表,但是必有相交节点
			cur1 = head1;
			cur2 = head2;
			int n = 0;
			while (cur1 != loop1) {
				n++;
				cur1 = cur1.next;
			}
			while (cur2 != loop2) {
				n--;
				cur2 = cur2.next;
			}
			cur1 = n > 0 ? head1 : head2;
			cur2 = cur1 == head1 ? head2 : head1;
			n = Math.abs(n);
			while (n != 0) {
				n--;
				cur1 = cur1.next;
			}
			while (cur1 != cur2) {
				cur1 = cur1.next;
				cur2 = cur2.next;
			}
			return cur1;
		} else {
			cur1 = loop1.next;
			while (cur1 != loop1) {
				if (cur1 == loop2) {
					return loop1;
				}
				cur1 = cur1.next;
			}
			return null;
		}
	}

public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
		Node cur1 = null;
		Node cur2 = null;
		if (loop1 == loop2) {  //装换成无环的结构链表,但是必有相交节点
			cur1 = head1;
			cur2 = head2;
			while(cur1 != cur2){
				cur1 = cur1 == loop1 ? head2 : cur1.next;
				cur2 = cur2 == loop2 ? head1 : cur2.next;
			}
			return cur1;
		} else {
			cur1 = loop1.next;
			while (cur1 != loop1) {
				if (cur1 == loop2) {
					return loop1;
				}
				cur1 = cur1.next;
			}
			return null;
		}
	}

整体代码如下:

public class FindFirstIntersectNode {

	public static class Node {
		public int value;
		public Node next;

		public Node(int data) {
			this.value = data;
		}
	}

	//主逻辑
	public static Node getIntersectNode(Node head1, Node head2) {
		if (head1 == null || head2 == null) {
			return null;
		}
		Node loop1 = getLoopNode(head1);
		Node loop2 = getLoopNode(head2);
		// 都无环
		if (loop1 == null && loop2 == null) {
			return noLoop(head1, head2);
		}
		// 都有环
		if (loop1 != null && loop2 != null) {
			return bothLoop(head1, loop1, head2, loop2);
		}
		//一个有环一个无环
		return null;
	}

	public static Node getLoopNode(Node head) {
		if (head == null || head.next == null || head.next.next == null) {
			return null;
		}
		Node n1 = head.next; // n1 -> slow
		Node n2 = head.next.next; // n2 -> fast
		while (n1 != n2) {
			if (n2.next == null || n2.next.next == null) {
				return null;
			}
			n2 = n2.next.next;
			n1 = n1.next;
		}
		n2 = head; // n2 -> walk again from head
		while (n1 != n2) {
			n1 = n1.next;
			n2 = n2.next;
		}
		return n1;
	}

	public static Node noLoop(Node head1, Node head2) {
		if (head1 == null || head2 == null) {
			return null;
		}
		Node cur1 = head1;
		Node cur2 = head2;
		int n = 0;
		while (cur1.next != null) {
			n++;
			cur1 = cur1.next;
		}
		while (cur2.next != null) {
			n--;
			cur2 = cur2.next;
		}
		if (cur1 != cur2) {
			return null;
		}
		cur1 = n > 0 ? head1 : head2;
		cur2 = cur1 == head1 ? head2 : head1;
		n = Math.abs(n);
		while (n != 0) {
			n--;
			cur1 = cur1.next;
		}
		while (cur1 != cur2) {
			cur1 = cur1.next;
			cur2 = cur2.next;
		}
		return cur1;
	}

	public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
		Node cur1 = null;
		Node cur2 = null;
		if (loop1 == loop2) {
			cur1 = head1;
			cur2 = head2;
			int n = 0;
			while (cur1 != loop1) {
				n++;
				cur1 = cur1.next;
			}
			while (cur2 != loop2) {
				n--;
				cur2 = cur2.next;
			}
			cur1 = n > 0 ? head1 : head2;
			cur2 = cur1 == head1 ? head2 : head1;
			n = Math.abs(n);
			while (n != 0) {
				n--;
				cur1 = cur1.next;
			}
			while (cur1 != cur2) {
				cur1 = cur1.next;
				cur2 = cur2.next;
			}
			return cur1;
		} else {
			cur1 = loop1.next;
			while (cur1 != loop1) {
				if (cur1 == loop2) {
					return loop1;
				}
				cur1 = cur1.next;
			}
			return null;
		}
	}

	public static void main(String[] args) {
		// 1->2->3->4->5->6->7->null
		Node head1 = new Node(1);
		head1.next = new Node(2);
		head1.next.next = new Node(3);
		head1.next.next.next = new Node(4);
		head1.next.next.next.next = new Node(5);
		head1.next.next.next.next.next = new Node(6);
		head1.next.next.next.next.next.next = new Node(7);

		// 0->9->8->6->7->null
		Node head2 = new Node(0);
		head2.next = new Node(9);
		head2.next.next = new Node(8);
		head2.next.next.next = head1.next.next.next.next.next; // 8->6
		System.out.println(getIntersectNode(head1, head2).value);

		// 1->2->3->4->5->6->7->4...
		head1 = new Node(1);
		head1.next = new Node(2);
		head1.next.next = new Node(3);
		head1.next.next.next = new Node(4);
		head1.next.next.next.next = new Node(5);
		head1.next.next.next.next.next = new Node(6);
		head1.next.next.next.next.next.next = new Node(7);
		head1.next.next.next.next.next.next = head1.next.next.next; // 7->4

		// 0->9->8->2...
		head2 = new Node(0);
		head2.next = new Node(9);
		head2.next.next = new Node(8);
		head2.next.next.next = head1.next; // 8->2
		System.out.println(getIntersectNode(head1, head2).value);

		// 0->9->8->6->4->5->6..
		head2 = new Node(0);
		head2.next = new Node(9);
		head2.next.next = new Node(8);
		head2.next.next.next = head1.next.next.next.next.next; // 8->6
		System.out.println(getIntersectNode(head1, head2).value);

	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值