Brush the title from: Code Casual Recording
1. Chain list basic operations
707. Design Chain List
Design the implementation of the chain table. You can choose to use single or double-linked lists. A node in a single-chain table should have two attributes: val and next. val is the value of the current node, and next is the pointer/reference to the next node. If you want to use a two-way Chain table, you also need an attribute prev to indicate the last node in the chain table. Assume that all nodes in the chain table are 0-index.
Implement these functions in a linked list class:
get(index): Gets the value of the index node in the chain table. Returns -1 if the index is invalid.
addAtHead(val): Head insert
addAtTail(val): End Interpolation
addAtIndex(index,val): A node whose value is Val is added before the index node in the chain table. If index equals the length of the list, the node is appended to the end of the list. If the index is larger than the length of the list, no node will be inserted. If index is less than 0, the node is inserted in the header.
DeleteAtIndex: Deletes the index node in the chain table.
Example:
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); // Chain list becomes 1-> 2-> 3
linkedList.get(1); // Return 2
linkedList.deleteAtIndex(1); // Now the list is 1-> 3
linkedList.get(1); // Return 3
Questions:
class Node { int val; Node next; public Node(int val) { this.val = val; } } class MyLinkedList { Node head = null; int size = 0; public MyLinkedList() { } // Gets the value of the index node in the chain table. Returns -1 if the index is invalid. public int get(int index) { if (index + 1 > size || index < 0) return -1; Node cur = this.head; for (int i = 0; i < index; i ++) { cur = cur.next; } return cur.val; } // Head plug public void addAtHead(int val) { Node node = new Node(val); if (this.head == null) { this.head = node; } else { node.next = this.head; this.head = node; } size++; } // Tail insertion public void addAtTail(int val) { Node node = new Node(val); if (this.head == null) { this.head = node; } else { Node cur = this.head; while (cur.next != null) { cur = cur.next; } cur.next = node; } size++; } // Add a node with val ue before index nodes. public void addAtIndex(int index, int val) { // index is larger than the length of the list, nodes are not inserted if (index > size) return; // If index is less than or equal to 0, a node is inserted in the head. if (index <= 0) { addAtHead(val); return; } // If index equals the length of the list, the node is appended to the end of the list. if (index == size) { addAtTail(val); return; } // Insert in the middle Node node = new Node(val); Node prev = head; // prev finds the previous node to insert for (int i = 0; i < index - 1; i ++) { prev = prev.next; } node.next = prev.next; prev.next = node; size++; } // Delete the index node in the list. public void deleteAtIndex(int index) { if (index > size - 1 || index < 0) return; if (index == 0) { this.head = this.head.next; size--; return; } if (index == size- 1) { Node prev = this.head; for (int i = 0; i < index- 1; i ++) { prev = prev.next; } prev.next = null; size--; return; } Node prev = head; for (int i = 0; i < index - 1; i ++) { prev = prev.next; } prev.next = prev.next.next; size--; } }
2. Double Pointer Iteration
203. Remove Chain List Elements
Leetcode Link
Give you a header node of the list and an integer val. Delete all Nodes in the list. Val == Val node and returns a new header node.
Example 1:
Input: head = [1,2,6,3,4,5,6], val = 6
Output: [1,2,3,4,5]
Example 2:
Input: head = [], val = 1
Output: []
Example 3:
Input: head = [7,7,7,7], val = 7
Output: []
Questions:
Method 1:
class Solution { public ListNode removeElements(ListNode head, int val) { if (head == null) return head; ListNode prev = head; ListNode cur = prev.next; while (cur != null) { if (cur.val == val) { // delete prev.next = cur.next; cur = prev.next; } else { // Do not delete prev = cur; cur = prev.next; } } // Because cur is looking backwards from the second node, the head node has not yet determined whether to delete the node if (head.val == val) { return head.next; } else { return head; } } }
Method 2: Add virtual header node
class Solution { public ListNode removeElements(ListNode head, int val) { if (head == null) return head; // Virtual Header Node ListNode newHead = new ListNode(-1); newHead.next = head; ListNode prev = newHead; ListNode cur = prev.next; // iteration while (cur != null) { if (cur.val == val) { prev.next = cur.next; } else { prev = cur; } // Can be placed outside judgment cur = prev.next; } return newHead.next; } }
206. Inverse Chain List
Leetcode Link
Defines a function that inputs the head node of a chain table, inverts the chain table, and outputs the head node of the chain table after inversion.
Example:
Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
Questions:
Notice the initial value and the end of iteration condition
class Solution { public ListNode reverseList(ListNode head) { if (head == null) return null; // The original header node should point to null after inversion ListNode prev = null; ListNode cur = head; // If the curNext initial value is cur.next, the pointer crosses the boundary at the end ListNode curNext = cur; while (cur != null) { // Point curNext at the beginning of each iteration. Next curNext = cur.next; // Reverse again cur.next = prev; prev = cur; cur = curNext; // The cur at the end of each iteration is equal to curNext, so there is no pointer crossing } // The last iteration prev points to the end of the origin, cur and curNext are null return prev; } }
24. Nodes in a pairwise exchange chain table
Leetcode Link
Give you a list of chains, swap the adjacent nodes between them, and return the header node of the list after swapping. You must complete this topic without modifying the values within the nodes (that is, you can only exchange nodes).
Example 1:
Input: head = [1,2,3,4]
Output: [2,1,4,3]
Example 2:
Input: head = []
Output: []
Example 3:
Input: head = [1]
Output: [1]
Questions:
Because there is a relationship between the chain table and the front and back nodes, two adjacent nodes are swapped, affecting four nodes in total, one on the front and one on the back. There are three next among the four nodes, and the drawing pushes out the iteration order among the three pointers to solve the problem.
class Solution { public ListNode swapPairs(ListNode head) { if (head == null) return null; // Virtual Header Node ListNode newHead = new ListNode(-1); newHead.next = head; ListNode prev = newHead; // Ensure that two nodes can be exchanged before entering the loop, and if only one node remains, no exchange is required while (prev.next != null && prev.next.next != null) { // Two adjacent nodes ListNode node1 = prev.next; ListNode node2 = node1.next; // Update 3 Pointers prev.next = node2; node1.next = node2.next; node2.next = node1; // Update the previous node of the next iteration prev = node1; } // Return to Real Head Node return newHead.next; } }
3. Fast and slow pointers with double pointers
19. Delete the last N th node of the list of chains
Leetcode Link
Give you a list of chains, delete the last nth node of the list, and return to the header node of the list.
Example 1:
Input: head = [1,2,3,4,5], n = 2
Output: [1,2,3,5]
Example 2:
Input: head = [1], n = 1
Output: []
Example 3:
Input: head = [1,2], n = 1
Output: [1]
Questions:
The fast and slow pointers begin traversing at the same speed, at fixed distances from each other. When the fast pointer crosses the boundary, the slow pointer points to the previous node to be deleted, and then deletes the node.
class Solution { public ListNode removeNthFromEnd(ListNode head, int n) { // Using virtual by-nodes eliminates the need to deal with special cases where there is only one node ListNode newHead = new ListNode(-1); newHead.next = head; ListNode fast = newHead; ListNode slow = newHead; // Fast pointer goes to expected position first while (n >= 0 && fast != null) { fast = fast.next; n--; } // Go with the slow pointer until the fast pointer crosses the boundary while (fast != null) { slow = slow.next; fast = fast.next; } // At this point, the slow pointer points to the previous node to delete, deleting the node slow.next = slow.next.next; return newHead.next; } }
160. Intersect Chain List
Leetcode Link
Give you the header nodes headA and headB of two single-chain lists. Find out and return the starting node where the two single-chain lists intersect. Returns null if two linked lists do not have intersecting nodes.
Diagram showing the intersection of two chains starting at node c1:
Topic data ensures that no loops exist in the entire chain structure. Note that after the function returns the result, the chain list must maintain its original structure.
Questions:
If the two chains have intersecting nodes, traverse A node first, then B node to intersection node and B node to intersection node first, then traverse A node to intersection node through the same number of nodes;
So use two nodes to traverse at the same speed, starting from different lists, and then traversing another list. If there are additional nodes, they will meet, if there are no, the last two nodes will be null at the same time.
public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { ListNode nodeA = headA; ListNode nodeB = headB; while (nodeA != nodeB) { nodeA = nodeA.next; nodeB = nodeB.next; if (nodeA == null && nodeB == null) { // If the two lists do not intersect and are null after each iteration, the loop will be dead if no more executions are return ed return null; } if (nodeA == null) { nodeA = headB; } if (nodeB == null) { nodeB = headA; } } return nodeA; } }
Simplify:
public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { ListNode nodeA = headA; ListNode nodeB = headB; while (nodeA != nodeB) { nodeA = nodeA == null ? headB : nodeA.next; nodeB = nodeB == null ? headA : nodeB.next; } return nodeA; } }
141. Ring Chain List
Leetcode Link
Give you a head node of the list to determine if there are rings in the list.
If there is a node in the chain table that can be reached again by continuously tracking the next pointer, then there are rings in the chain table. To represent rings in a given list of chains, an integer pos is used inside the evaluation system to represent where the end of the chain is connected to the list (the index starts at 0). If pos is -1, there are no rings in the list. Note: pos is not passed as a parameter, just to identify the actual situation of the chain table.
Returns true if there are rings in the chain table. Otherwise, return false.
Example 1:
Input: head = [3,2,0,-4], pos = 1 Output: true Explanation: There is a ring in the chain table whose tail is connected to the second node.
Questions:
Analogue running, if two people start at the same starting point on a straight road at different speeds, they will not meet each other. If a straight road is followed by a loop, it must meet again.
public class Solution { public boolean hasCycle(ListNode head) { ListNode fast = head; ListNode slow = head; while (fast != null && fast.next != null) { // Fast pointer takes two steps, slow pointer takes one step fast = fast.next.next; slow = slow.next; // Meet, return to true if (fast == slow) return true; } // Come to the end (null), no meeting, return false return false; } }
142. Ring Chain List II
Leetcode Link
Given a list of chains, returns the first node where the list of chains begins to ring. If the list of chains is not looped, null is returned.
Questions:
After the fast and slow pointers encounter in the ring, the fast pointer starts from the head, the slow pointer starts from the encounter, takes one step at a time, and finally meets at the ring entry node.
public class Solution { public ListNode detectCycle(ListNode head) { ListNode fast = head; ListNode slow = head; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; if (fast == slow) { // First encounter: encounter in the ring fast = head; while (fast != slow) { fast = fast.next; slow = slow.next; } // Second encounter: Entrance ring return slow; } } return null; } }
If you don't understand it, you can also use a hash table to solve the problem:
public class Solution { public ListNode detectCycle(ListNode head) { Set<ListNode> set = new HashSet<>(); ListNode cur = head; while (cur != null) { if (set.contains(cur)) { // Find Entering Loop Node return cur; } else { // Node that first appears, added to set set.add(cur); } cur = cur.next; } // Not a ring list return null; } }
4. Summary
- Because there is no prev node in the head node, many questions need to be dealt with separately. With virtual head node, it is not necessary to deal with this situation separately, which is convenient and concise.
- Be aware of pointer crossings and special considerations