文章目录
82. 删除排序链表中的重复元素 II
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head == null) {
return head;
}
ListNode dummy = new ListNode(0, head);
ListNode cur = dummy;
// 比较的是: cur.next.val 和 cur.next.next.val 的值
while (cur.next != null && cur.next.next != null) {
if (cur.next.val == cur.next.next.val) {
int x = cur.next.val;
while (cur.next != null && cur.next.val == x) {
// 跳过重复的元素
cur.next = cur.next.next;
}
} else {
cur = cur.next;
}
}
return dummy.next;
}
}
- 时间:O(n)
- 空间:O(1)
剑指 Offer II 022. 求链表中环的入口节点(判断链表中是否有环)
给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
思路:当发现 slow 与 fast 相遇时,我们再额外使用一个指针 ptr。起始,它指向链表头部;随后,它和 slow 每次向后移动一个位置。最终,它们会在入环点相遇。
具体结论的得出过程见:https://leetcode-cn.com/problems/c32eOV/solution/lian-biao-zhong-huan-de-ru-kou-jie-dian-vvofe/
public class Solution01 {
// 判断链表中是否有环
public boolean hasCycle(ListNode head) {
if (head == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
return true;
}
}
return false;
}
}
public class Solution {
// 有环,并且找到入环的节点
public ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}
ListNode slow = head, fast = head;
while (fast != null) {
slow = slow.next;
if (fast.next != null) {
fast = fast.next.next;
} else {
return null;
}
if (slow == fast) {
ListNode ptr = head;
while (ptr != slow) {
slow = slow.next;
ptr = ptr.next;
}
return ptr;
}
}
return null;
}
}
- 时间:O(n)
- 空间:O(1)
剑指 Offer II 077. 链表排序
https://leetcode-cn.com/problems/7WHec2/
给定链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
// 自底向上归并排序
class Solution {
public ListNode sortList(ListNode head) {
if (head == null) {
return head;
}
// 求得 length 长度
int length = 0;
ListNode node = head;
while (node != null) {
length++;
node = node.next;
}
// dummyHead -> head
// prev -> curr
ListNode dummyHead = new ListNode(0, head);
for (int subLength = 1; subLength < length; subLength <<= 1) {
ListNode prev = dummyHead, curr = dummyHead.next;
while (curr != null) {
ListNode head1 = curr;
for (int i = 1; i < subLength && curr.next != null; i++) {
curr = curr.next;
}
ListNode head2 = curr.next;
curr.next = null;
curr = head2;
for (int i = 1; i < subLength && curr != null && curr.next != null; i++) {
curr = curr.next;
}
ListNode next = null;
if (curr != null) {
next = curr.next;
curr.next = null;
}
//合并 subLength
ListNode merged = merge(head1, head2);
prev.next = merged;
while (prev.next != null) {
prev = prev.next;
}
curr = next;
}
}
return dummyHead.next;
}
public ListNode merge(ListNode head1, ListNode head2) {
ListNode dummyHead = new ListNode(0);
ListNode temp = dummyHead, temp1 = head1, temp2 = head2;
while (temp1 != null && temp2 != null) {
if (temp1.val <= temp2.val) {
temp.next = temp1;
temp1 = temp1.next;
} else {
temp.next = temp2;
temp2 = temp2.next;
}
temp = temp.next;
}
if (temp1 != null) {
temp.next = temp1;
} else if (temp2 != null) {
temp.next = temp2;
}
return dummyHead.next;
}
}
- 时间:O(NlogN)
- 空间:O(1)
剑指 Offer II 025. 链表中的两数相加
使用栈或者逆置链表,依次相加
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Stack<Integer> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
ListNode curr01 = l1;
while (curr01 != null) {
stack1.push(curr01.val);
curr01 = curr01.next;
}
ListNode curr02 = l2;
while (curr02 != null) {
stack2.push(curr02.val);
curr02 = curr02.next;
}
int carry = 0;
ListNode newHead = new ListNode(-1);
ListNode headNext = null;
// 这里判断 carry != 0 是因为 可能存在 [5] [6] 这种情况
while (!stack1.isEmpty() || !stack2.isEmpty() || carry != 0) {
int a = stack1.isEmpty() ? 0 : stack1.pop();
int b = stack2.isEmpty() ? 0 : stack2.pop();
int curr = a + b + carry;
// 一种简洁写法:
// carry = cur / 10;
// cur %= 10;
// 然后直接 new ListNode && 头插即可
if (curr >= 10) {
headNext = new ListNode(curr % 10);
carry = 1;
} else {
headNext = new ListNode(curr);
carry = 0;
}
// 头插
headNext.next = newHead.next;
newHead.next = headNext;
}
return newHead.next;
}
}
- 时间:O(max(m+n))
- 空间:O(m+n)
剑指 Offer 25. 合并两个排序的链表
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null && l2 != null) {
return l2;
}
if (l1 != null && l2 == null) {
return l1;
}
if (l1 == null && l2 == null) {
return null;
}
ListNode newHead = new ListNode(-1);
ListNode curr = newHead;
ListNode curr1 = l1;
ListNode curr2 = l2;
while (curr1 != null && curr2 != null) {
if (curr1.val <= curr2.val) {
// 尾插 :即可
curr.next = curr1;
curr1 = curr1.next;
} else {
curr.next = curr2;
curr2 = curr2.next;
}
curr = curr.next;
}
curr.next = curr1 == null ? curr2 : curr1;
return newHead.next;
}
}
- 时间:O(m+n)
- 空间:O(1)
剑指 Offer 35. 复杂链表的复制
https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/
思路:
(1)方法一:回溯 + 哈希表
class Solution {
Map<Node, Node> cachedNode = new HashMap<Node, Node>();
public Node copyRandomList(Node head) {
if (head == null) {
return null;
}
if (!cachedNode.containsKey(head)) {
Node headNew = new Node(head.val);
cachedNode.put(head, headNew);
headNew.next = copyRandomList(head.next);
headNew.random = copyRandomList(head.random);
}
return cachedNode.get(head);
}
}
- 时间,空间都是 O(n)
(2)利用链表特性
class Solution {
public copyRandomList.Node copyRandomList(copyRandomList.Node head) {
if (head == null) {
return null;
}
// 对于链表 A->B->C,我们可以将其拆分为 A -> A' -> B -> B' -> C -> C'
// 最后整理即可
for (copyRandomList.Node node = head; node != null; node = node.next.next) {
copyRandomList.Node nodeNew = new copyRandomList.Node(node.val);
nodeNew.next = node.next;
node.next = nodeNew;
}
for (copyRandomList.Node node = head; node != null; node = node.next.next) {
copyRandomList.Node nodeNew = node.next;
nodeNew.random = (node.random != null) ? node.random.next : null;
}
copyRandomList.Node headNew = head.next;
for (copyRandomList.Node node = head; node != null; node = node.next) {
copyRandomList.Node nodeNew = node.next;
node.next = node.next.next;
nodeNew.next = (nodeNew.next != null) ? nodeNew.next.next : null;
}
return headNew;
}
}
- 时间:O(n)
- 空间:O(1)