题目:
给两个链表,两个链表相交就返回相交的节点,如果不想交就为null
给两个head节点
需要时间复杂度O(n) 空间O(1)
简单方案:容器的办法 HashSet 循环第一个链表的所有链 循环第二个链表如果判定在set里面就说明有交点
两个链表相交情况图 不会有其他情况的! 单链表
复杂方案:
1 先写一个函数1,可以判断单个链表是否有环
快慢指针:1 快指针一次走2步 慢指针一次走1步
快指针走到空说明无环节点 直接返回null
快慢指针相交说明有环
2 然后慢指针不动 ,快指针回到头结点,每次走一步
慢指针每次走一步,他俩相遇的继是环开始的节点
不要管证明,记住就完了
2 主函数逻辑
1 先用 函数1 得到两个链表的入环节点loop1 和loop2
2 判断逻辑 如果loop1 和loop2 等于空 说明是图中1 或2的情况
这个时候 只要循环两条链路走到根节点 并计数
如果根节点是一个节点说明相交是图中情况2 根据计数可知道两 链表长度 ,长度相减 先让长链表走,然后两个链表一起走,第一个相交的就返回。
如果跟节点不相交说明是图中情况1返回null
3 如果有环 说明是情况3,4,5
1 先判断如果两个loop一样说明是4这种情况 切掉loop的下面环 就是情况2的样子,直接调用 之前写好的函数返回即可。
2 如果两个loop不一样说明是情况4或者情况5
这个时候 用任何loop 寻找next 如果最后碰到自己说明是3情况,如果碰到了另一个loop说明是情况5,返回任意loop即可
4 如果是2个链表 任意一个为空 另一个有环 那一定不相交 走完上面的逻辑只剩下这两种逻辑,直接返回null即可
代码:
package 算法;
/**
* 给两个链表,两个链表相交就返回相交的节点,如果不想交就为null
* 给两个head
* 需要时间复杂度O(n) 空间O(1)
*/
public class 两个链表相交问题 {
//====实现代码===========================================
//链表节点
public static class Node{
public Node next;
public int num;
public Node(int num) {
this.num = num;
}
}
/**
* 输入两个链表的头 判断两个链表是否相交 如果相交 返回相交节点 如果不想交 返回空
* @param head1
* @param head2
* @return
*/
public static Node intersectNode(Node head1 ,Node head2){
if (head1 == null||head2==null) {
return null;
}
//1 判断两条节点是否有环 有环输出入环节点
Node loop1 = linkLoop(head1);
Node loop2 = linkLoop(head2);
//2 如果两个都没环说明 可能相交 使用 无环策略判断 Y 字形
if (loop1 == null&&loop2==null) {
return focusStrategy1(head1,head2);
}
// 3 如果两个都有环 用两个都有环的策略
if (loop1 != null&&loop2!=null) {
return focusStrategy2(head1,head2,loop1,loop2);
}
//判断 如果任何一个是有环 一个没还 则肯定不想交
return null;
}
/**
* 有环相交策略
* @param head1
* @param head2
* @param loop1
* @param loop2
* @return
*/
private static Node focusStrategy2(Node head1, Node head2, Node loop1, Node loop2) {
// 1 第一种情况 如果两个相交节点相等 链表是Y 下面是0 形状的 切掉下面的0然后 按照五环策略返回
if (loop1 == loop2) {
loop1.next=null;
return focusStrategy1(head1,head2);
}
// 2 这个时候 只剩 两种情况 如果 任何一个loop节点 循环向下找 找到的是另一个loop 那两个都是相交节点随便返回一个
// 如果没找到说明是单独的两个66 链结构 返回空
Node node = loop1.next;
while (node!=loop2&&node!=loop1){
node = node.next;
}
if (node == loop2) {
return loop1;
}
return null;
}
/**
* 无环相交策略
* @param head1
* @param head2
* @return
*/
private static Node focusStrategy1(Node head1, Node head2) {
//循环两个 节点到根节点 如果 根节点 一样说明是Y字形 说明有相交 不一样直接返回空
Node node1 = head1;
Node node2 = head2;
int n1=0;
int n2=0;
while (node1.next!=null){
node1 = node1.next;
n1++;
}
while (node2.next!=null){
node2 = node2.next;
n2++;
}
//不相等肯定不想交没交点
if (node1 != node2) {
return null;
}
//判断n1 和 n2 哪个大 大的先走步长 然后就一样大了 然后一起走 走到第一个相交的就是焦点
node1 = head1;
node2 = head2;
if (n1 >n2) {
n1 = n1-n2;
while (n1!=0){
n1--;
node1 = node1.next;
}
}
if (n1 <n2) {
n2= n2-n1;
while (n2!=0){
n2--;
node2 = node2.next;
}
}
while (node1!=node2){
node1 = node1.next;
node2 = node2.next;
}
return node1;
}
/**
* 给头节点确定有没有环 如果有就返回入环节点
* @param head
* @return
*/
public static Node linkLoop(Node head){
//快慢指针:1 快指针一次走2步 慢指针一次走1步
//快指针走到空说明无环节点 直接返回null
//快慢指针相交说明有环
//2 然后慢指针不动 ,快指针回到头结点,每次走一步
//慢指针每次走一步,他俩相遇的继是环开始的节点
//不要管证明,记住就完了
if (head.next == null||head.next.next == null) {
return null;
}
Node slow = head.next;
Node fist = head.next.next;
while (fist!=null){
//比较两个指针内存地址一不一样 一样说明有环
if (fist==slow) {
break;
}
slow = slow.next;
if (fist.next != null&&fist.next.next!=null) {
fist = fist.next.next;
}else {
fist =null;
}
}
//如果快指针是null说明无环
if (fist == null) {
return null;
}
//有环就找入环节点
fist = head;
while (fist!=slow){
slow = slow.next;
fist = fist.next;
}
return fist;
}
//====实现代码===========================================
//====对数器测试用代码===========================================
/**
* 图中情况1 链表test数据返回
* @return
*/
public static Node[] testNode1(){
Node[] nodes = new Node[2];
Node head1 = new Node(1);
Node node1 = new Node(2);
Node node2 = new Node(3);
head1.next=node1;
node1.next=node2;
Node head2 = new Node(4);
Node node3 = new Node(5);
Node node4 = new Node(6);
head2.next=node3;
node3.next=node4;
nodes[0]=head1;
nodes[1]=head2;
return nodes;
}
/**
* 图中情况2 链表test数据返回
* @return
*/
public static Node[] testNode2(){
Node[] nodes = new Node[2];
Node head1 = new Node(1);
Node node1 = new Node(2);
Node node2 = new Node(3);
head1.next=node1;
node1.next=node2;
Node head2 = new Node(4);
Node node3 = new Node(5);
Node node4 = new Node(6);
head2.next=node3;
node3.next=node4;
//相交
Node node5 = new Node(7);
Node node6 = new Node(8);
node2.next = node5;
node4.next = node5;
node5.next = node6;
nodes[0]=head1;
nodes[1]=head2;
return nodes;
}
/**
* 图中情况3 链表test数据返回
* @return
*/
public static Node[] testNode3(){
Node[] nodes = new Node[2];
Node head1 = new Node(1);
Node node1 = new Node(2);
Node node2 = new Node(3);
head1.next=node1;
node1.next=node2;
Node head2 = new Node(4);
Node node3 = new Node(5);
Node node4 = new Node(6);
head2.next=node3;
node3.next=node4;
//制造环
Node node7 = new Node(7);
Node node8 = new Node(8);
node2.next = node7;
node7.next = node8;
node8.next = node2;
Node node9 = new Node(9);
Node node10 = new Node(10);
node4.next = node9;
node9.next = node10;
node10.next = node4;
nodes[0]=head1;
nodes[1]=head2;
return nodes;
}
/**
* 图中情况4 链表test数据返回
* @return
*/
public static Node[] testNode4(){
Node[] nodes = new Node[2];
Node head1 = new Node(1);
Node node1 = new Node(2);
Node node2 = new Node(3);
head1.next=node1;
node1.next=node2;
Node head2 = new Node(4);
Node node3 = new Node(5);
Node node4 = new Node(6);
head2.next=node3;
node3.next=node4;
//相交
Node node5 = new Node(7);
Node node6 = new Node(8);
node2.next = node5;
node4.next = node5;
node5.next = node6;
//在加个环
Node node9 = new Node(9);
Node node10 = new Node(10);
Node node11 = new Node(11);
node6.next = node9;
node9.next = node10;
node10.next = node11;
node11.next = node6;
nodes[0]=head1;
nodes[1]=head2;
return nodes;
}
public static Node[] testNode5(){
Node[] nodes = new Node[2];
Node head1 = new Node(1);
Node node1 = new Node(2);
Node node2 = new Node(3);
head1.next=node1;
node1.next=node2;
Node head2 = new Node(4);
Node node3 = new Node(5);
Node node4 = new Node(6);
head2.next=node3;
node3.next=node4;
Node node7 = new Node(7);
Node node8 = new Node(8);
Node node9 = new Node(9);
Node node10 = new Node(10);
node2.next = node7;
node7.next = node4;
node4.next = node8;
node8.next =node9;
node9.next = node10;
node10.next = node2;
nodes[0]=head1;
nodes[1]=head2;
return nodes;
}
//====对数器测试用代码===========================================
public static void main(String[] args) {
Node[] nodes1 = testNode1();
Node n1 = intersectNode(nodes1[0],nodes1[1]);
if (n1 == null) {
System.out.println("第一种情况测试成功");
}else {
System.out.println("第一种情况测试失败");
}
Node[] nodes2 = testNode2();
Node n2 = intersectNode(nodes2[0],nodes2[1]);
if (n2.num == 7) {
System.out.println("第二种情况测试成功");
}else{
System.out.println("第二种情况测试失败");
}
Node[] nodes3= testNode3();
Node n3 = intersectNode(nodes3[0],nodes3[1]);
if (n3== null) {
System.out.println("第三种情况测试成功");
}else{
System.out.println("第三种情况测试失败");
}
Node[] nodes4 = testNode4();
Node n4 = intersectNode(nodes4[0],nodes4[1]);
if (n4.num == 7) {
System.out.println("第四种情况测试成功");
}else{
System.out.println("第四种情况测试失败");
}
Node[] nodes5 = testNode5();
Node n5 = intersectNode(nodes5[0],nodes5[1]);
if (n5.num == 3||n5.num == 6) {
System.out.println("第五种情况测试成功");
}else{
System.out.println("第五种情况测试失败");
}
}
}