Leetcode链接:剑指 Offer 52. 两个链表的第一个公共节点 - 力扣(LeetCode)
看到题目没有思路时首先考虑常用数据结构和算法思想。
常用数据结构:数组、链表、队列、栈、Map、Set、二叉树、堆等。
常用算法思想:查找、排序、双指针、递归、迭代、分支、贪心、回溯、动态规划等。
1.使用栈
思路:分别遍历两个链表,并将节点存入到栈中。两个栈同时依次比较顶元素是否相等,如果相等,用一个变量保存最新相等的节点(相等的节点就是公共节点),分别弹出顶元素,直到不相等的节点(不相等的节点就不是公共节点);如果不相等,说明不是公共节点,返回最新相等的节点,即第一个公共节点。
Java代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
class Solution {
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// 使用栈
Stack<ListNode> stackA = new Stack();
Stack<ListNode> stackB = new Stack();
// 遍历链表A,将节点存入栈A中
ListNode cur = headA;
while(cur != null){
stackA.push(cur);
cur = cur.next;
}
// 遍历链表B,将节点存入栈B中
cur = headB;
while(cur != null){
stackB.push(cur);
cur = cur.next;
}
// 依次比较栈A、栈B元素
ListNode res = null;
while(stackA.size() > 0 && stackB.size() > 0){
if(stackA.peek() == stackB.peek()){
res = stackA.pop();
stackB.pop();
}else{
break;
}
}
// 没有找到公共节点
return res;
}
}
用此方法,空间复杂度O(n),时间复杂度O(n)。
2.Set
思路:首先遍历一个链表,并将节点存入Set,然后遍历另一个链表,同时判断节点是否存在于Set,如果存在则说明找到第一个公共节点,可直接返回,如果都不存在,则说明无公共节点,返回null。
Java代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
class Solution {
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// 使用Set
Set<ListNode> set = new HashSet();
// 首先遍历链表A,并将节点存入到Set中
ListNode cur = headA;
while(cur != null){
set.add(cur);
cur = cur.next;
}
// 然后遍历链表B,判断链表是否存在
cur = headB;
while(cur != null){
if(set.contains(cur)){
// 找到第一个公共节点
return cur;
}
cur = cur.next;
}
// 没有找到公共节点
return null;
}
}
用此方法,空间复杂度O(n),时间复杂度O(n)。
3.两个链表拼接
A:0-1-2-3-4-5
B:a-b-4-5
AB:0-1-2-3-4-5-a-b-4-5
BA:a-b-4-5-0-1-2-3-4-5
可以看到使用A、B拼接后,后面的4-5就是公共链表,4就是第一个公共节点。道理是什么?从几何角度分析。
如果链表A、B存在公共节点,则以公共节点为分界点将两个链表分别分为left、right两部分,right_a与right_b相等,所以通过拼接,left_a+right_a+left_b与left_b+right_b+left_a相等,所以left_a+right_b+left_b后就是第一个公共节点。
可以通过新建两个链表,用两个指针同时遍历来完成要求,但是为了节省空间,可以使用双指针,在遍历完一个链表后,从另一个链表头开始遍历,达到相同的效果。
Java代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
class Solution {
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null){
return null;
}
// 使用链表拼接法
ListNode curA = headA;//先遍历链表A的指针
ListNode curB = headB;//先遍历链表B的指针
//只要curA和curB不同时到达结尾
while(!(curA == null && curB == null)){
if(curA == curB){
// 找到公共节点
return curA;
}
//如果A到达链表结尾,从B开始
if(curA == null){
curA = headB;
curB = curB.next;
}
// 如果B到达链表结尾,从A开始
else if(curB == null){
curB = headA;
curA = curA.next;
}
else{
curA = curA.next;
curB = curB.next;
}
}
// 没有找到公共节点
return null;
}
}
用此方法,空间复杂度为O(1),时间复杂度为O(n)。
4.差和双指针
思路:先遍历两个链表得到两个链表的长度,然后计算两个链表长度之差| lenA - lenB |,让长度大的链表先走| lenA - lenB |步,然后再一起遍历并判断是否相等,如果相等,则说明找到第一个公共节点返回,如果都不相等,返回null。
Java代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
class Solution {
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null){
return null;
}
// 使用差和双指针法
ListNode curA = headA;//遍历链表A的指针
int lenA = 0;
ListNode curB = headB;//遍历链表B的指针
int lenB = 0;
//统计链表A的长度
while(curA != null){
lenA++;
curA = curA.next;
}
//统计链表B的长度
while(curB != null){
lenB++;
curB = curB.next;
}
curA = headA;
curB = headB;
if(lenA > lenB){
// 链表A走 (lenA - lenB) 步
for(int i = 0; i < (lenA - lenB); i++){
curA = curA.next;
}
}else{
// 链表B走 (lenB - lenA) 步
for(int i = 0; i < (lenB - lenA); i++){
curB = curB.next;
}
}
// 同时遍历链表A和链表B
while(curA != curB){
curA = curA.next;
curB = curB.next;
}
// 要么是第一个公共节点,要么是null
return curA;
}
}
用此方法,空间复杂度O(1),时间复杂度O(n)。