
题目
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
在节点 c1 开始相交。
题解1:(蛮方法)
在第1个链表中顺序遍历每个结点,每遍历到一个结点时,在2个链表中顺序遍历每一个结点(即嵌套循环)
代码实现
public class Solution {
public static ListNode getIntersectionNode1(ListNode headA, ListNode headB) {
// 外层循环,遍历第一个链表,每遍历一个结点,内层循环会遍历一遍第二个链表
while (headA != null) {
// 声明一个新节点,初始化为第二个链表头结点
ListNode nB = headB;
while (nB != null) {
if (headA == nB) {
return nB;
}
nB = nB.next;
}
headA = headA.next;
}
return null;
}
}
结点类
public class ListNode{ T val; ListNode next; public ListNode() { } public ListNode(T val) { this.val = val; } public ListNode(T val, ListNode next) { this.next = next; } }
测试代码
public class Test {
@org.junit.Test
public void test1() {
ListNode head1 = new ListNode<>("a1");
ListNode one = new ListNode<>("a2");
ListNode two = new ListNode<>("c1");
ListNode three = new ListNode<>("c2");
ListNode four = new ListNode<>("c3");
head1.next = one;
one.next = two;
two.next = three;
three.next = four;
ListNode head2 = new ListNode<>("b1");
ListNode first = new ListNode<>("b2");
ListNode second = new ListNode<>("b3");
head2.next = first;
first.next = second;
second.next = two;
ListNode intersectionNode = Solution.getIntersectionNode1(head1, head2);
System.out.println("两链表中的重合结点的值: " + intersectionNode.val);
}
}
复杂度分析
假设两链表长度为n,m
时间复杂度:嵌套遍历两个链表,则时间复杂度为O(nm);
空间复杂度:只声明了一个新结点,则空间复杂度为O(1);
题解2:(栈方法)
思路分析
若两个链表存在公共结点,那么从第一个重合结点开始,往后所以结点都是重合的,因此可以从两个链表尾部开始遍历比较它们的结点,即最后一个相等结点就是第一个重合结点
由于链表只能从头结点开始遍历,而我们要的是从尾结点开始,因此可以使用栈来存放链表结点(先进后出)。在遍历链表时把结点压入栈,最先进的是头结点最后出,最晚进的是尾结点最先出。
- 定义两个栈,存放两个链表结点
- 遍历两个链表,遍历过程中把结点压入栈
- 声明一个新结点,初始为null
- 用一个循环依次把两个栈的栈顶元素d出并赋值给新结点,循环终结条件为两个栈为空且栈顶元素相等
- 当循环终终止时,新结点存放的是最后一次循环栈d出的结点,此结点就是两链表第一个重合结点
实现代码
public class Solution {
public static ListNode getIntersectionNode2(ListNode headA, ListNode headB) {
// 声明两个栈,存放两个链表的结点
Stack
复杂度分析
假设两链表长度为n,m
时间复杂度:需要遍历两个链表,则时间复杂度为O(n+m);
空间复杂度:需要两个栈来存放两链表结点,则空间复杂度为O(n+m);
和蛮力法相比,这是一种用空间换时间的方法
题解3:(环入口方法)
思路分析
若两个链表存在公共结点,使其中一个链表的尾结点指向它的头结点,此时两链表会构成一个环形链表,则环的入口结点就是两链表的第一个重合结点,环的入口结点在以往文章以给出链表中环的入口节点,这里不再阐述
代码实现
public class Solution {
public static ListNode getIntersectionNode3(ListNode headA, ListNode headB) {
// 声明一个结点,存放链表A的头结点
ListNode nA = headA;
// 判断链表A是否null,若null则没有重合结点返回null
if (nA == null) {
return null;
}
// 遍历链表A到尾结点
while (nA.next != null) {
nA = nA.next;
}
// 让尾结点指向链表A的头结点,构成一个环形链表
nA.next = headA;
// 定义两个结点(快慢指针),初始为链表B头结点
ListNode fast = headB;
ListNode slow = headB;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
// 若fast等于slow,即两个结点相遇,则存在环
if (fast == slow) {
// 快结点赋值为链表B头结点
fast = headB;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
// 返回相同结点之前,需要恢复原有链表结构,即链表A尾结点不再指向A的头结点,而是为null
nA.next = null;
return slow;
}
}
nA.next = null;
return null;
}
}
复杂度分析
假设两链表长度为n,m
时间复杂度:在最初找其中一个链表尾结点时,需要遍历n或m此,故O(n)或O(m),当链表构成环时,在找相遇结点时,走过的距离不会超过链表总长度n+m(可以理解为循环的次数不会超过n+m),在找环入口结点时也一样。故总的时间复杂度为O(n)或O(m) + O(n+m) + O(n+m) = On(n+m)
空间复杂度:我们只使用了nA,fast,slow 三个结点。空间复杂度为O(1);
题解4(差值法)
思路分析
若两个链表存在公共结点,下面通过两链表长度相等和不相等进行讨论
当两链表长度相等时,以相同的速度同时遍历两链表,则两链表会同时到达尾结点(可以理解为同一起点出发),在遍历过程若两结点相等,此结点就是第一个重合结点
当两链表长度不相等时,以相同的速度同时遍历两链表,则长的链表会先到达尾结点(不是从同一起点出发),此时不能通过两结点是否相等来判断是否是第一个重合结点
综上,要想使遍历过程中两结点相等的结点就是第一个重合结点,需要让两链表从同一起点出发,方法如下
算出两链表的长度,并作长度差,先让长的链表遍历长度差的值,之后让两链表以相同的速度同时遍历,此时它们就是从同一起点出发
步骤
- 遍历两个链表得到它们的长度,并让两个长度相减得到长度差delta
- 定义一个指针p1初始化为较长的链表的头节点,p1先遍历delta步
- 再定义一个指针p2初始化为较短的链表的头节点
- 使这两个指针按照相同的速度遍历,当它们相遇时,相遇的节点就是第一个重合的节点
代码实现
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// 获取两个链表的长度
int lenA = getLength(headA);
int lenB = getLength(headB);
// 求两个链表的差值
int delta = Math.abs(lenA - lenB);
// 把长的链表的头结点赋给front结点,短的头结点赋给back结点
ListNode front = lenA > lenB ? headA : headB;
ListNode back = lenA > lenB ? headB : headA;
// front结点先遍历delta步
for (int i = 0; i < delta; i++) {
front = front.next;
}
while (front != back) {
front = front.next;
back = back.next;
}
return back;
}
public int getLength(ListNode head) {
// 链表长度,初始为0
int length = 0;
// 遍历链表,每次循环length+1
while (head != null) {
length++;
head = head.next;
}
return length;
}
}
复杂度分析
假设两链表长度为n,m
时间复杂度:需要遍历两个链表,则时间复杂度为O(n+m);
空间复杂度:声明了两个结点,则空间复杂度为O(1);
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)