链表(五) * 返回两个链表相交的第一个交点

问题:

给定两个链表的头结点,head1,head2,链表可能有环可能无环。这两个链表可能相交可能不相交。请实现一个函数,如果相交,返回相交的第一个节点,如果不相交返回null,如果链表长度分别为m,n要求时间复杂度O(m+n),额外空间复杂度O(1)

此问题可以拆分成三个子问题:

1.判断链表有环无环,有环,返回入环的第一个节点。无环,返回null

2.如果两个链表都无环,实现一个方法:返回两个无环链表相交的第一个节点(如果相交就为Y型结构)

3.如果两个链表都有环并且知道入环的第一个节点。返回相交第一个节点

其他情况链表不可能相交。

问题一与问题二都有两种解法:1.借助hashMap,hashSet额外空间复杂度O(n)或者2.借助指针变量额外空间复杂度O(1)

问题三:两个有环链表三种情况:不相交,入环前相交,入环后相交

loop为入环节点


问题一:方法1:

//判断链表有没有环,如果有返回入口节点
	//方法一:额外空间复杂度O(N),借助了hashMap(正确)
	public static ListNode EntryNodeOfLoop(ListNode pHead)
    {
		if(pHead==null) return null;
		HashSet<ListNode> hs=new HashSet<ListNode>();
		ListNode p=pHead;
		while(hs.add(p)) {
			if(p.next==null) return null;//遍历到最后一个节点都没有遇见重复的就返回空,并没有环
			else 
			p=p.next;
		}
		//如果p已经进入了hashSet,则hs.add(p)会返回false此时p即为入口的第一个节点
		return p;
    }

方法2:

//方法二,额外空间复杂度o(1)两个指针完成(正确)
	public static ListNode EntryNodeOfLoop2(ListNode pHead)
    {
		if(pHead==null||pHead.next==null||pHead.next.next==null) return null;	
		
		ListNode fast=pHead;
		ListNode slow=pHead;
		//快慢指针开始走,如果相遇,break
		while(fast!=null&&fast.next!=null) {
			fast=fast.next.next;
			slow=slow.next;
			if(fast==slow) break;	
		}
		//如果快指针走到终点都没有相遇就没有环
		if(fast==null||fast.next==null)
		return null;
		//快指针调整初始位置和速度,返回第二次相遇位置即入环节点
		fast=pHead;
		while(fast!=slow) {
			fast=fast.next;
			slow=slow.next;
		}
		return fast;
    }

问题二:

方法一:

//两个无环链表相交方法一借助hashSet 额外空间O(N)返回相交的第一个结点或者null(正确)
	public static ListNode Noloop1(ListNode head1,ListNode head2){
		if(head1==null||head2==null) return null;
		HashSet<ListNode>hs=new HashSet<ListNode>();
		ListNode p=head1;
		//将链表1所有节点放入hs中
		while(p!=null) {
			hs.add(p);
			p=p.next;
		}
		p=head2;
		while(p!=null) {
			if(hs.contains(p)) return p;
			p=p.next;
		}
		return null; 
	}

方法2:

//两个无环链表相交方法二 借助快慢指针额外空间O(1) 返回相交的第一个节点或者null(正确)
	//遍历一遍,分别得到两个链表长度,如果尾节点不一致,一定不相交。如果尾节点一致,需要在遍历一遍找到第一个相交位置
	//遍历第二遍时,长节点先走差值步,然后一起走,直到两个节点相遇
	public static ListNode Noloop2(ListNode head1,ListNode head2) {
		//
		if(head1==null||head2==null) return null;
		int length1=0,length2=0;
		ListNode p1=head1;
		ListNode p2=head2;
		while(p1.next!=null) {	
			p1=p1.next;
			length1++;
		}
		while(p2.next!=null) {	
			p2=p2.next;
			length2++;
		}
		//两个链表不相交
		if(p1!=p2) return null;
		
		length1=length1-length2;
		p1=length1>0?head1:head2;
		p2= p1==head1?head2:head1;//p1指向长链表
		length1=Math.abs(length1);
		while(length1!=0) {
			length1--;
			p1=p1.next;
		}
		//代码执行到这里两个链表一定有交点
		while(p1!=null) {
			if(p1==p2) return p1;
			p1=p1.next;
			p2=p2.next;
		}
		return null;
	}

问题三:

代码:

//loop为有环链表的入环节点
	//两个有环链表相交,返回相交的第一个节点或者null,额外空间O(1)不借助额外数据结构
	public static ListNode Bothloop(ListNode head1,ListNode loop1,ListNode head2,ListNode loop2) {
		//传入的参数是两个链表头结点,两个链表第一个入环节点
		//两个带环的链表有三种情况
		if(loop1==loop2) {//一定相交,找出第一个相交节点返回Y型结构加一个环
			//得到链表长度(head到入环位置),然后长链表走差值步,然后一起走到相交位置
			int length1=0;
			int length2=0;
			ListNode p1=head1;
			ListNode p2=head2;
			
			while(p1!=loop1) {
				length1++;
				p1=p1.next;
			}
			while(p2!=loop1) {
				length2++;
				p2=p2.next;
			}
			length1=length1-length2;
			p1= length1>0?head1:head2;//p1指向较长链表
			p2= p1==head1?head2:head1;
			length1=Math.abs(length1);
			while(length1!=0) {
				length1--;
				p1=p1.next;
			}
			while(p1!=loop1) {
				if(p1==p2) return p1;
				p1=p1.next;
				p2=p2.next;
			}
			return p1;
			
		}
		else {
			//loop1顺链表走,如果能遇到loop2返回loop1或者loop2为相交节点,
如果走回原loop1位置都没有遇到loop2即不相交
			ListNode p=loop1.next;
			while(p!=loop1) {
				if(p==loop2) return loop2;//相交返回loop1或者loop2
				p=p.next;
				
			}
			return null;//不相交
		}	
	}
	

得到两个链表,求相交节点:

//得到两个链表相交的第一个交点
	private static ListNode getIntersectNode(ListNode head1, ListNode head2) {
		if(head1==null||head2==null) return null;
		//判断是否有环 如果有返回入环节点
		ListNode loop1=EntryNodeOfLoop(head1);
		ListNode loop2=EntryNodeOfLoop(head2);
		
		//两个无环链表
		if(loop1==null&&loop2==null) {
			System.out.println(loop1+","+loop2);
			System.out.println("进入Noloop函数");
			return Noloop1(head1,head2);
			//Noloop2(head1,head2);
		}//两个有环链表
		else if(loop1!=null&&loop2!=null) {
			System.out.println("两个有环链表入环节点:"+loop1.value+","+loop2.value);
			return Bothloop(head1, loop1, head2, loop2);
		}
		return null;
	}

(end)


完整代码:

package ListNode;

import java.util.HashSet;

public class FirstInterNode {
	public static class ListNode{
		int value;
		ListNode next;
		ListNode(int a){this.value=a;}
	}
	//判断链表有没有环,如果有返回入口节点
	//方法一:额外空间复杂度O(N),借助了hashMap(正确)
	public static ListNode EntryNodeOfLoop(ListNode pHead)
    {
		if(pHead==null) return null;
		HashSet<ListNode> hs=new HashSet<ListNode>();
		ListNode p=pHead;
		while(hs.add(p)) {
			if(p.next==null) return null;//遍历到最后一个节点都没有遇见重复的就返回空,并没有环
			else 
			p=p.next;
		}
		//如果p已经进入了hashSet,则hs.add(p)会返回false此时p即为入口的第一个节点
		return p;
    }
	//方法二,额外空间复杂度o(1)两个指针完成(正确)
	public static ListNode EntryNodeOfLoop2(ListNode pHead)
    {
		if(pHead==null||pHead.next==null||pHead.next.next==null) return null;	
		
		ListNode fast=pHead;
		ListNode slow=pHead;
		//快慢指针开始走,如果相遇,break
		while(fast!=null&&fast.next!=null) {
			fast=fast.next.next;
			slow=slow.next;
			if(fast==slow) break;	
		}
		//如果快指针走到终点都没有相遇就没有环
		if(fast==null||fast.next==null)
		return null;
		//快指针调整初始位置和速度,返回第二次相遇位置即入环节点
		fast=pHead;
		while(fast!=slow) {
			fast=fast.next;
			slow=slow.next;
		}
		return fast;
    }
	
	//两个无环链表相交方法一借助hashSet 额外空间O(N)返回相交的第一个结点或者null(正确)
	public static ListNode Noloop1(ListNode head1,ListNode head2){
		if(head1==null||head2==null) return null;
		HashSet<ListNode>hs=new HashSet<ListNode>();
		ListNode p=head1;
		//将链表1所有节点放入hs中
		while(p!=null) {
			hs.add(p);
			p=p.next;
		}
		p=head2;
		while(p!=null) {
			if(hs.contains(p)) return p;
			p=p.next;
		}
		return null; 
	}
	//两个无环链表相交方法二 借助快慢指针额外空间O(1) 返回相交的第一个节点或者null(正确)
	//遍历一遍,分别得到两个链表长度,如果尾节点不一致,一定不相交。如果尾节点一致,需要在遍历一遍找到第一个相交位置
	//遍历第二遍时,长节点先走差值步,然后一起走,直到两个节点相遇
	public static ListNode Noloop2(ListNode head1,ListNode head2) {
		//
		if(head1==null||head2==null) return null;
		int length1=0,length2=0;
		ListNode p1=head1;
		ListNode p2=head2;
		while(p1.next!=null) {	
			p1=p1.next;
			length1++;
		}
		while(p2.next!=null) {	
			p2=p2.next;
			length2++;
		}
		//两个链表不相交
		if(p1!=p2) return null;
		
		length1=length1-length2;
		p1=length1>0?head1:head2;
		p2= p1==head1?head2:head1;//p1指向长链表
		length1=Math.abs(length1);
		while(length1!=0) {
			length1--;
			p1=p1.next;
		}
		//代码执行到这里两个链表一定有交点
		while(p1!=null) {
			if(p1==p2) return p1;
			p1=p1.next;
			p2=p2.next;
		}
		return null;
	}
	//loop为有环链表的入环节点
	//两个有环链表相交,返回相交的第一个节点或者null,额外空间O(1)不借助额外数据结构
	public static ListNode Bothloop(ListNode head1,ListNode loop1,ListNode head2,ListNode loop2) {
		//传入的参数是两个链表头结点,两个链表第一个入环节点
		//两个带环的链表有三种情况
		if(loop1==loop2) {//一定相交,找出第一个相交节点返回Y型结构加一个环
			//得到链表长度(head到入环位置),然后长链表走差值步,然后一起走到相交位置
			int length1=0;
			int length2=0;
			ListNode p1=head1;
			ListNode p2=head2;
			
			while(p1!=loop1) {
				length1++;
				p1=p1.next;
			}
			while(p2!=loop1) {
				length2++;
				p2=p2.next;
			}
			length1=length1-length2;
			p1= length1>0?head1:head2;//p1指向较长链表
			p2= p1==head1?head2:head1;
			length1=Math.abs(length1);
			while(length1!=0) {
				length1--;
				p1=p1.next;
			}
			while(p1!=loop1) {
				if(p1==p2) return p1;
				p1=p1.next;
				p2=p2.next;
			}
			return p1;
			
		}
		else {
			//loop1顺链表走,如果能遇到loop2返回loop1或者loop2为相交节点,如果走回原loop1位置都没有遇到loop2即不相交
			ListNode p=loop1.next;
			while(p!=loop1) {
				if(p==loop2) return loop2;//相交返回loop1或者loop2
				p=p.next;
				
			}
			return null;//不相交
		}	
	}
	
	

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

				// 0->9->8->6->7->null
				ListNode head2 = new ListNode(0);
				head2.next = new ListNode(9);
				head2.next.next = new ListNode(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 ListNode(1);
				head1.next = new ListNode(2);
				head1.next.next = new ListNode(3);
				head1.next.next.next = new ListNode(4);
				head1.next.next.next.next = new ListNode(5);
				head1.next.next.next.next.next = new ListNode(6);
				head1.next.next.next.next.next.next = new ListNode(7);
				head1.next.next.next.next.next.next.next = head1.next.next.next; // 7->4

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

	}
	//得到两个链表相交的第一个交点
	private static ListNode getIntersectNode(ListNode head1, ListNode head2) {
		if(head1==null||head2==null) return null;
		//判断是否有环 如果有返回入环节点
		ListNode loop1=EntryNodeOfLoop(head1);
		ListNode loop2=EntryNodeOfLoop(head2);
		
		//两个无环链表
		if(loop1==null&&loop2==null) {
			System.out.println(loop1+","+loop2);
			System.out.println("进入Noloop函数");
			return Noloop1(head1,head2);
			//Noloop2(head1,head2);
		}//两个有环链表
		else if(loop1!=null&&loop2!=null) {
			System.out.println("两个有环链表入环节点:"+loop1.value+","+loop2.value);
			return Bothloop(head1, loop1, head2, loop2);
		}
		return null;
	}
	
}

 


版权声明:本文为qq_37290134原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。