linked list
A lot of repeated questions refer to: [Code Random Records] Double-pointer method for brushing questions
theoretical basis
Single list:
Double linked list:
Circular linked list:
Definition of singly linked list node:
public class ListNode {
public int val; // 节点的值
public ListNode next; // 指向的下一个节点
public ListNode() {
}
public ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
Remove linked list elements
Given a head node head of the linked list and an integer val, please delete all nodes Node.val == val
satisfying and return a new head node .
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
recursion:
public ListNode removeElements(ListNode head, int val) {
if (head == null) return head;
head.next = removeElements(head.next, val);
return head.val == val ? head.next : head;
}
Iteration: Find the previous node of the node to be deleted and perform the delete operation
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) return head;
ListNode node = head;
while (node != null && node.next != null) {
if (node.next.val == val) node.next = node.next.next;
else node = node.next;
}
return head.val == val ? head.next : head;
}
}
design linked list
Topic: 707. Designing Linked Lists
Dynamic Singly Linked List
Dynamic singly linked list : (virtual head node)
class ListNode {
int val;
ListNode next;
ListNode() {
}
ListNode(int val) {
this.val = val;
}
}
class MyLinkedList {
int size; // 链表元素个数
ListNode head; // 虚拟头节点
// 初始化链表
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
// 获取第 index 个元素的值
public int get(int index) {
if (index < 0 || index >= size) return -1;
ListNode cur = head; // 从头节点开始查找
// 包含虚拟头节点, 所以查找第 index+1 个节点
while (index-- >= 0) cur = cur.next;
return cur.val;
}
// 在链表最前面插入一个节点
public void addAtHead(int val) {
addAtIndex(0, val);
}
// 在链表的最后插入一个节点
public void addAtTail(int val) {
addAtIndex(size, val);
}
// 在第 index 个节点之前插入一个新节点
// 如果 index 为 0,那么新插入的节点为链表的新头节点
// 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果 index 大于链表的长度,则返回空
public void addAtIndex(int index, int val) {
if (index > size) return;
if (index < 0) index = 0;
size++;
// 找到要插入节点的前驱
ListNode pre = head;
while (index-- > 0) pre = pre.next;
// 插入操作
ListNode newNode = new ListNode(val);
newNode.next = pre.next;
pre.next = newNode;
}
// 删除第 index 个节点
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) return;
size--;
// 找到要删除节点的前驱
ListNode pre = head;
while (index-- > 0) pre = pre.next;
// 删除操作
pre.next = pre.next.next;
}
}
Dynamic doubly linked list
Dynamic doubly linked list : (virtual head node and virtual tail node)
class ListNode {
int val;
ListNode next, prev;
ListNode(int x) {
val = x;
}
}
class MyLinkedList {
int size;
ListNode head, tail;
public MyLinkedList() {
size = 0;
head = new ListNode(0); // 虚拟头节点
tail = new ListNode(0); // 虚拟尾节点
head.next = tail;
tail.prev = head;
}
public int get(int index) {
if (index < 0 || index >= size) return -1;
ListNode cur = head;
// 索引 < 一半从前往后找, 索引 > 一半从后前找
if (index < size / 2) {
for (int i = 0; i <= index; i++) cur = cur.next;
} else {
cur = tail;
for(int i = 0; i <= size - 1 - index; i++) cur = cur.prev;
}
return cur.val;
}
public void addAtHead(int val) {
ListNode newNode = new ListNode(val);
newNode.next = head.next;
head.next.prev = newNode;
head.next = newNode;
newNode.prev = head;
size++;
// addAtIndex(0, val);
}
public void addAtTail(int val) {
ListNode newNode = new ListNode(val);
newNode.next = tail;
newNode.prev = tail.prev;
tail.prev.next = newNode;
tail.prev = newNode;
size++;
// addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
if (index > size) return;
if (index < 0) index = 0;
// 找到要添加的前驱
ListNode pre = head;
while (index-- > 0) pre = pre.next;
// 添加操作
ListNode newNode = new ListNode(val);
newNode.next = pre.next; // 新节点的 next 指向
newNode.prev = pre; // 新节点的 prev 指向
pre.next.prev = newNode; // 后继的 prev 指向新
pre.next = newNode; // 前驱的 next 指向新
size++;
}
public void deleteAtIndex(int index) {
if(index >= size || index < 0) return;
// 找到要删除的前驱
ListNode pre = head;
while (index-- > 0) pre = pre.next;
// 删除操作
pre.next.next.prev = pre;
pre.next = pre.next.next;
size--;
}
}
static singly linked list
Static singly linked list : (virtual head node)
class MyLinkedList {
final int N = 1010;
int[] e = new int[N];
int[] ne = new int[N];
int h = 0; // 头节点
int idx = 1; // 有虚拟头节点
int size = 0; // 长度
public MyLinkedList() {
}
public int get(int k) {
if (k < 0 || k >= size) return -1;
int t = ne[h];
while (k -- > 0) t = ne[t];
return e[t];
}
public void addAtHead(int val) {
e[idx] = val;
ne[idx] = ne[h];
ne[h] = idx++;
size++;
}
public void addAtTail(int val) {
int t = h;
while (ne[t] != 0) t = ne[t];
e[idx] = val;
ne[idx] = ne[t];
ne[t] = idx++;
size++;
}
public void addAtIndex(int k, int val) {
if (k <= 0) addAtHead(val);
else if (k == size) addAtTail(val);
else if (k > 0 && k < size) {
k --;
int t = ne[h];
while (k -- > 0) t = ne[t];
e[idx] = val;
ne[idx] = ne[t];
ne[t] = idx++;
size++;
}
}
public void deleteAtIndex(int k) {
if (k < 0 || k >= size) return;
int t = h;
while (k -- > 0) t = ne[t];
ne[t] = ne[ne[t]];
size--;
}
}
reverse linked list
Title: 206. Reversing a Linked List
Double pointer:
public ListNode reverseList(ListNode head) {
ListNode cur = head, pre = null;
while (cur != null) {
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
recursion:
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode node = reverseList(head.next);
head.next.next = head;
head.next = null;
return node;
}
Head interpolation: space O(n)
public ListNode reverseList(ListNode head) {
ListNode res = null;
for (ListNode x = head; x != null; x = x.next)
res = new ListNode(x.val, res);
return res;
}
Exchange the nodes in the linked list two by two
Given a linked list, exchange the adjacent nodes in it two by two, and return the head node of the exchanged linked list. You must complete this exercise without modifying the values inside the nodes (i.e. only node swaps).
输入:head = [1,2,3,4]
输出:[2,1,4,3]
Iteration + virtual head node:
public ListNode swapPairs(ListNode head) {
ListNode vn = new ListNode(0, head); // 虚拟头节点
ListNode cur = vn, tmp = null;
while (cur != null && cur.next != null && cur.next.next != null) {
tmp = cur.next;
cur.next = cur.next.next;
tmp.next = cur.next.next;
cur.next.next = tmp;
cur = cur.next.next;
}
return vn.next;
}
recursion:
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) return head;
ListNode next = head.next;
head.next = swapPairs(next.next);
next.next = head;
return next;
}
Delete the last N node of the linked list
Given a linked list, delete the n
penultimate , and return the head node of the linked list.
Fast and slow pointer:
- The fast pointer moves n steps first, and then starts to move at the same time as the slow pointer
- When the fast pointer points to the last node, the slow pointer points to the node to be deleted (the previous node)
public ListNode removeNthFromEnd(ListNode head, int n) {
if (head == null || head.next == null) return null;
ListNode fast = head, slow = head;
while (n-- > 0) fast = fast.next; // 快指针先走 n 步
if (fast == null) return head.next; // 删除第一个节点
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return head;
}
Fast and slow pointer + virtual head node:
Generally using virtual head nodes does not need to deal with special cases
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode vn = new ListNode(0, head); // 虚拟头节点
ListNode slow = vn, fast = vn;
while (n-- > 0) fast = fast.next; // 快指针先走 n 步
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return vn.next;
}
recursion:
class Solution {
int cur = 0;
public ListNode removeNthFromEnd(ListNode head, int n) {
if (head == null) return null;
head.next = removeNthFromEnd(head.next, n);
cur++;
if (cur == n) return head.next;
return head;
}
}
linked list intersect
Given the head nodes headA
and headB
, please find and return the starting node where the two singly linked lists intersect. If there is no intersection between the two linked lists, return null
.
Double pointer:
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA, curB = headB;
int lenA = 0, lenB = 0;
// 求链表 A 的长度
while (curA != null) {
lenA++;
curA = curA.next;
}
// 求链表 B 的长度
while (curB != null) {
lenB++;
curB = curB.next;
}
curA = headA;
curB = headB;
// 链表 A 更长则让 A 多走, B 更长则让 B 多走,保证 A B 从同一位置开始比较
if (lenA > lenB) for (int i = 0; i < lenA - lenB; i++) curA = curA.next;
else for (int i = 0; i < lenB - lenA; i++) curB = curB.next;
// 逐个比较 A 和 B
while (curA != null) {
if (curA == curB) return curA;
curA = curA.next;
curB = curB.next;
}
return curA;
}
Clever approach:
- a goes to b, b goes to a, if there is an intersection point, it will inevitably meet
- If there is no intersection point, it will end up meeting at
null
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode h1 = headA, h2 = headB;
while (h1 != h2) {
h1 = (h1 == null) ? headB : h1.next;
h2 = (h2 == null) ? headA : h2.next;
}
return h1;
}
hash:
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Set<ListNode> set = new HashSet<>();
ListNode p = headA;
while (p != null) {
set.add(p);
p = p.next;
}
p = headB;
while (p != null) {
if (set.contains(p)) return p;
p = p.next;
}
return null;
}
Ring linked list: fast and slow pointer
Given the head node of a linked list head
, determine whether there is a ring in the linked list.
Fast and slow pointer:
public boolean hasCycle(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true;
}
return false;
}
hash:
public boolean hasCycle(ListNode head) {
Set<ListNode> set = new HashSet<>();
while (head != null && !set.contains(head)) {
set.add(head);
head = head.next;
}
return head != null;
}
Ring Linked List II**
Given the head node of a linked list head
, return the first node where the linked list starts entering the ring. Returns if the linked list is acyclic null
.
Fast and slow pointer judgment ring + find ring entry:
public ListNode detectCycle(ListNode head) {
ListNode slow = head, fast = head;
// 快慢指针判断是否有环
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
// 有环, 找环的起始点
fast = head; // 快指针从头开始
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null;
}
hash:
public ListNode detectCycle(ListNode head) {
Set<ListNode> set = new HashSet<>();
while (head != null && !set.contains(head)) {
set.add(head);
head = head.next;
}
return head;
}