一、两个链表的第一个公共结点
输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
数据范围: n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)

输入:
输入分为是3段,第一段是第一个链表的非公共部分,第二段是第二个链表的非公共部分,第三段是第一个链表和第二个链表的公共部分。 后台会将这3个参数组装为两个链表,并将这两个链表对应的头节点传入到函数FindFirstCommonNode里面,用户得到的输入只有pHead1和pHead2。
返回值:
返回传入的pHead1和pHead2的第一个公共结点,后台会打印以该节点为头节点的链表。
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
// 逻辑上,将 pHead2 "接到" pHead1 后面,将 pHead1 "接到" pHead2 后面
// 然后让两个指针分别遍历两个链表,二者步伐保持一致,最终终将在第一个公共节点相遇
ListNode p1 = pHead1, p2 = pHead2;
while (p1 != p2) {
p1 = p1 == null ? pHead2 : p1.next;
p2 = p2 == null ? pHead1 : p2.next;
}
return p1;
}
}
运行结果

二、链表相加
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
数据范围:0≤n,m≤1000000,链表任意值 0≤val≤9
要求:空间复杂度 O(n),时间复杂度 O(n)

import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
public ListNode addInList(ListNode head1, ListNode head2) {
// write code here
Deque<ListNode> stack1 = listToStack(head1);
Deque<ListNode> stack2 = listToStack(head2);
ListNode dummy = new ListNode(-1);
int carry = 0;
while (!stack1.isEmpty() || !stack2.isEmpty() || carry != 0) {
int v1, v2;
if (!stack1.isEmpty()) {
v1 = stack1.pop().val;
} else {
v1 = 0;
}
if (!stack2.isEmpty()) {
v2 = stack2.pop().val;
} else {
v2 = 0;
}
int total = v1 + v2 + carry;
carry = total / 10;
ListNode newNode = new ListNode(total % 10);
// 头插
newNode.next = dummy.next;
dummy.next = newNode;
}
return dummy.next;
}
private Deque<ListNode> listToStack(ListNode head) {
ListNode cur = head;
Deque<ListNode> stack = new ArrayDeque<>();
while (cur != null) {
stack.push(cur);
cur = cur.next;
}
return stack;
}
}
运行结果

三、单链表的排序
给定一个节点数为n的无序单链表,对其按升序排序。
数据范围:0<n≤100000。
要求:空间复杂度 O(n),时间复杂度 O(nlogn)

import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
public ListNode sortInList(ListNode head) {
// 空链表和只有一个节点的链表无需排序
if (head == null || head.next == null) {
return head;
}
// 找到切分点,从该点开始将链表一分为二
// 从该节点后面将链表一分为二。
ListNode splitPoint = findSplitPoint(head);
ListNode left = head, right = splitPoint.next;
splitPoint.next = null;
// 合并两个有序数组
return merge(sortInList(left), sortInList(right));
}
/**
* 最开始认为切分点应该是链表的中间节点。
* 实际运行时,遇到了栈溢出错误,说明这里发生了无限递归。
* 后续在 Debug 的过程中注意到如下的问题:
* 当链表长度为 2 时,中间节点就是第 2 个节点。
* 从它后面切开后 right 为空,left 仍然是原链表!
* 这就是导致无限递归的根本原因!
* 从长度为偶数的链表出发,注意到中间节点实际上属于右半部分。
* 所以切分点应该是其前一个节点。
* 这样从切分点后面切开,才能得到两个规模都严格小于原链表的子链表,从而保证递归能够最终到达终止条件。
*/
private ListNode findSplitPoint(ListNode head) {
// 让 fast 比 slow 先走一步,使 slow 最终停在合适的切分点
ListNode fast = head.next, slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
private ListNode merge(ListNode head1, ListNode head2) {
ListNode dummy = new ListNode(-1), k = dummy;
while (head1 != null && head2 != null) {
if (head1.val < head2.val) {
k.next = head1;
head1 = head1.next;
} else {
k.next = head2;
head2 = head2.next;
}
k = k.next;
}
k.next = head1 == null ? head2 : head1;
return dummy.next;
}
}
运行结果
