[算法] - 两个单链表相交的一系列问题【链表中最难的题目】

两个单链表相交的一系列问题
【 题目】 给定两个可能有环也可能无环的单链表, 头节点head1和head2。 请实现一个函数, 如果两个链表相交, 请返回相交的 第一个节点。 如果不相交, 返回null
【 要求】 如果两个链表长度之和为N, 时间复杂度请达到O(N), 额外空间复杂度请达到O(1)。

前言:

肯定不可能一个有环,一个无环,然后相交了。

首先,我们判断是有环还是无环,如果有环,返回入环的第一个节点。无环null。

简单方法:采用hashset,不断next往里面赋值,如果hashset里有重复的node出现,则这个节点就是,如果next出现了null就无环。

省空间的做法:快慢指针当快指针与慢指针相遇,则有环,此时将快指针=head,然后快指针和慢指针都变成1步长,再相遇的地方就是入环节点。中途两个指针如果遇到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;
	}

第二点学习:如果两个无环,找相交

先判断是否相交,两个都遍历到最后一个非null的node,如果相等则相交,如果不等肯定不相交。

相交之后,把长的那一个,先走完(长链表-短链表)插值的长度,然后和慢的一起走,当第一次相遇,返回。

 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) {//当cur1变成最后一个就出来了
			n++;//cur的总数
			cur1 = cur1.next;
		}
		while (cur2.next != null) {
			n--;//得到差值
			cur2 = cur2.next;
		}
		if (cur1 != cur2) {//如果两个无环链表的最后一个节点不相等,那么不相交
			return null;
		}
		cur1 = n > 0 ? head1 : head2;//谁长谁的head变成cur1
		cur2 = cur1 == head1 ? head2 : head1;//谁短谁的head变成cur2
		n = Math.abs(n);
		while (n != 0) {//先走两个链表的差值
			n--;
			cur1 = cur1.next;
		}
		while (cur1 != cur2) {//然后一起走,第一次相交的时候就是那个点
			cur1 = cur1.next;
			cur2 = cur2.next;
		}
		return cur1;
	}

第三点:如果两个有环,怎么求第一个点,以下是三大情况。

第一种:不相交,

第二种:环外相交,

第三种:如你所见,如果遇到了,那么两个点都是最近的点,任意返回一个都可以,

如果两个环的入环节点相等,就是第二个,不是就是1或3。区分1和3,选一个链表的入环节点开始next遍历,如果在再次遇到这个节点之前没有遇到另一个入环节点就是1,如果相遇了就是3,此时任选一个返回就好了。

做法:直接以入环节点为终止条件,其本身就是一个无环相交就第一个节点的问题。

	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;
			}
//情况2,和单链表的相交差不多,原先是遇到 null结尾处终止 ,现在是loop处就终止
			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 {//看能不能遇到loop2
			cur1 = loop1.next;
			while (cur1 != loop1) {//如果都转回自己,还没有遇到loop2,就是情况一
				if (cur1 == loop2) {
					return loop1;
				}
				cur1 = cur1.next;
			}
			return null;
		}
	}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值