单链表是否相交(Java版)

题目:有两个带头结点的单链表L1,L2,判断它们是否相交(它们中是否有相同的结点)

解题思路:
L1,L2中任何一个链表为空它们不相交
因为单链表中可能有环,所以分3种情况讨论
1、L1,L2中都没有环,那么可以通过判断它们中最后一个结点是否相同,来判断它们是否相交
2、L1,L2中一个有环,一个没有环,那么它们一定不相交

3、L1,L2中都有环,那么只有当它们的环相同时,它们才相交,当它们在进入环之前有相同的结点时,它们交点的个数大于环中结点的个数,当它们进入环时才有第一个相同的结点时,它们交点的个数等于于环中结点的个数,这两种情况下,都可以通过判断它们环的入口结点是否相同来判断它们是否相交。


ADT定义:

//单链表的结点类
class LNode{
	//为了简化访问单链表,结点中的数据项的访问权限都设为public
	public int data;
	public LNode next;
}

算法实现:

public class LinkListUtli {
	// 判断单链表是否有环
	public static boolean hasCircle(LNode L) {
		if (L == null)
			return false;// 单链表为空时,单链表没有环
		if (L.next == null)
			return false;// 单链表中只有头结点,而且头结点的next为空,单链表没有环
		LNode p = L.next;// p表示从头结点开始每次往后走一步的指针
		LNode q = L.next.next;// q表示从头结点开始每次往后走两步的指针
		while (q != null) // q不为空执行while循环
		{
			if (p == q)
				return true;// p与q相等,单链表有环
			p = p.next;
			q = q.next.next;
		}
		return false;
	}

	// 当单链表中没有环时返回null,有环时返回环的入口结点
	public static LNode searchEntranceNode(LNode L) {
		if (L == null)
			return null;// 单链表为空时,单链表没有环
		if (L.next == null)
			return null;// 单链表中只有头结点,而且头结点的next为空,单链表没有环
		LNode p = L.next;// p表示从头结点开始每次往后走一步的指针
		LNode q = L.next.next;// q表示从头结点开始每次往后走两步的指针
		while (q != null) // q不为空执行while循环
		{
			if (p == q)
				break;// p与q相等,单链表有环
			p = p.next;
			q = q.next.next;
		}
		if (q == null)
			return null;

		// 这里之所以没有向上面一样,先让p,q走一步再进入循环判断,是因为头结点可能就是环的入口结点
		q = L;
		while (q != null) {
			if (p == q)
				return p;// 返回环中入口结点
			p = p.next;
			q = q.next;
		}
		return null;
	}
    //判断两个单链表是否相交
	public static boolean isIntersect(LNode L1, LNode L2) {
		if (L1 == null || L2 == null)
			return false;// L1,L2中任何一个链表为空它们不相交
		// L1,L2中都没有环
		if (!(hasCircle(L1) || hasCircle(L2))) {
			LNode p = L1;
			LNode q = L2;
			while (p.next != null) {
				p = p.next;
			}
			while (q.next != null) {
				q = q.next;
			}
			if (p == q)
				return true;// 两个无环单链表,最后一个结点相同时,它们相交
			return false;
		}
		// L1,L2中都有环
		else if (hasCircle(L1) && hasCircle(L2)) {
			if (searchEntranceNode(L1) == searchEntranceNode(L2))
				return true;// 两个有环单链表,它们环的入口结点相同时,它们相交
			return false;
		}
		// L1,L2中一个有环,一个没有环
		else {
			return false;// 两个单链表,一个有环,一个无环,它们一定不相交
		}
	}

}


拓展:有两个带头结点的单链表L1,L2,判断它们是否相交,并求出首次相交的结点


解题思路:
当L1与L2相交时,若L1与L2中都没有环,那么求出两个单链表中结点个数之差的绝对值dCount,让结点个数多的单链表从头结点开始往下走dCount步,让结点个数少的单链表从头结点开始往后走,然后同时让两个单链表每次往后走一步,直到他们相等,那么这个结点就是首次相交的结点。若L1与L2中都有环,求出单链表L1与L2从头结点到环入口结点的结点总个数count,求出两者之差的绝对值,让count大的单链表从头结点开始往下走dCount步,让count大的的单链表从头结点开始往后走,然后同时让两个单链表每次往后走一步,直到他们相等,那么这个结点就是首次相交的结点。


算法实现:

public class LinkListUtli {
	// 判断单链表是否有环
	public static boolean hasCircle(LNode L) {
		if (L == null)
			return false;// 单链表为空时,单链表没有环
		if (L.next == null)
			return false;// 单链表中只有头结点,而且头结点的next为空,单链表没有环
		LNode p = L.next;// p表示从头结点开始每次往后走一步的指针
		LNode q = L.next.next;// q表示从头结点开始每次往后走两步的指针
		while (q != null) // q不为空执行while循环
		{
			if (p == q)
				return true;// p与q相等,单链表有环
			p = p.next;
			q = q.next.next;
		}
		return false;
	}

	// 当单链表中没有环时返回null,有环时返回环的入口结点
	public static LNode searchEntranceNode(LNode L) {
		if (L == null)
			return null;// 单链表为空时,单链表没有环
		if (L.next == null)
			return null;// 单链表中只有头结点,而且头结点的next为空,单链表没有环
		LNode p = L.next;// p表示从头结点开始每次往后走一步的指针
		LNode q = L.next.next;// q表示从头结点开始每次往后走两步的指针
		while (q != null) // q不为空执行while循环
		{
			if (p == q)
				break;// p与q相等,单链表有环
			p = p.next;
			q = q.next.next;
		}
		if (q == null)
			return null;

		// 这里之所以没有向上面一样,先让p,q走一步再进入循环判断,是因为头结点可能就是环的入口结点
		q = L;
		while (q != null) {
			if (p == q)
				return p;// 返回环中入口结点
			p = p.next;
			q = q.next;
		}
		return null;
	}

	// 判断两个单链表是否相交
	public static boolean isIntersect(LNode L1, LNode L2) {
		if (L1 == null || L2 == null)
			return false;// L1,L2中任何一个链表为空它们不相交
		// L1,L2中都没有环
		if (!(hasCircle(L1) || hasCircle(L2))) {
			LNode p = L1;
			LNode q = L2;
			while (p.next != null) {
				p = p.next;
			}
			while (q.next != null) {
				q = q.next;
			}
			if (p == q)
				return true;// 两个无环单链表,最后一个结点相同时,它们相交
			return false;
		}
		// L1,L2中都有环
		else if (hasCircle(L1) && hasCircle(L2)) {
			if (searchEntranceNode(L1) == searchEntranceNode(L2))
				return true;// 两个有环单链表,它们环的入口结点相同时,它们相交
			return false;
		}
		// L1,L2中一个有环,一个没有环
		else {
			return false;// 两个单链表,一个有环,一个无环,它们一定不相交
		}
	}

	// 求两个链表首次相交的结点
	public static LNode searchFirstIntersectNode(LNode L1, LNode L2) {
		if (isIntersect(L1, L2))
			return null;// 单链表L1与L2不相交时,返回null
		if (!hasCircle(L1)) // L1中没有环时,L1与L2中都没有环
		{
			int count1 = 1;// 单链表L1中结点的个数,包括头结点
			int count2 = 1;// 单链表L2中结点的个数,包括头结点
			LNode p = L1;
			LNode q = L2;
			while (p.next != null) {
				p = p.next;
				count1++;
			}
			while (q.next != null) {
				q = q.next;
				count2++;
			}
			if (count1 >= count2)// 单链表L1比单链表L2长或两者一样长时
			{
				int dCount = count1 - count2;// 单链表L1比单链表L2多出的结点的个数
				p = L1;
				q = L2;
				while (dCount > 0) {
					p = p.next;
				}
				while (p != null) {
					if (p == q)
						return p;
					p = p.next;
					q = q.next;
				}
			} else {
				int dCount = count2 - count1;// 单链表L2比单链表L1多出的结点的个数
				p = L1;
				q = L2;
				while (dCount > 0) {
					q = q.next;
				}
				while (q != null) {
					if (p == q)
						return p;
					p = p.next;
					q = q.next;
				}
			}
		} else // L1有环时,此时L1与L2都有环
		{
			int count1 = 1;// 单链表L1中从头结点到环的入口结点之间所有结点的个数,包括头结点与环的入口结点
			int count2 = 1;// 单链表L2中从头结点到环的入口结点之间所有结点的个数,包括头结点与环的入口结点
			LNode p = L1;
			LNode q = L2;
			while (p != searchEntranceNode(L1)) {
				p = p.next;
				count1++;
			}
			while (q != searchEntranceNode(L2)) {
				q = q.next;
				count2++;
			}
			if (count1 >= count2)// 单链表L1比单链表L2从头结点到环的入口结点之间所有结点的个数长或两者一样长时
			{
				int dCount = count1 - count2;// 单链表L1比单链表L2从头结点到环的入口结点之间所有结点的个数多出的结点的个数
				p = L1;
				q = L2;
				while (dCount > 0) {
					p = p.next;
				}
				while (p != null) {
					if (p == q)
						return p;
					p = p.next;
					q = q.next;
				}
			} else {
				int dCount = count2 - count1;// 单链表L2比单链表L1从头结点到环的入口结点之间所有结点的个数多出的结点的个数
				p = L1;
				q = L2;
				while (dCount > 0) {
					q = q.next;
				}
				while (q != null) {
					if (p == q)
						return p;
					p = p.next;
					q = q.next;
				}
			}
		}
		return null;
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值