Linked List · 19 of 36
Deletion from a Circular Linked List
Take one kid out of a ring of kids holding hands, and make the two kids beside them hold hands so the ring stays closed.
- Simple way
- time O(n)
room O(1) - Clever way
- time O(n)
room O(1)
The problem, simply
You get the head of a circular linked list: the last box points back to the head, not to null. You also get a key. Remove the box that holds key and give back the head of the ring.
The ring must stay a ring. If you remove the head, the next box becomes the new head and the last box must now point to it. If the ring has only one box and you remove it, the answer is an empty list, null. If no box holds key, give back the ring unchanged.
A story to remember it
Picture it
A ring of five boxes. The last box, 5, points back to the head, 1. Removing 1 means 5 must point to 2, and 2 becomes the head.
- checking
Examples
Remove a middle box
- In
ring 1 2 3 4 5, key = 3- Out
ring 1 2 4 5
Box 2 now points to box 4. The head does not change.
Remove the head
- In
ring 1 2 3 4 5, key = 1- Out
ring 2 3 4 5
Box 5 now points to box 2, and 2 is the new head.
Remove the last box
- In
ring 1 2 3, key = 3- Out
ring 1 2
Box 2 now points back to the head, 1.
The only box
- In
ring 7, key = 7- Out
null
Nobody is left, so the list is empty.
Key not there
- In
ring 1 2 3, key = 9- Out
ring 1 2 3
Walk once around, find nothing, change nothing. Do not loop forever.
Way 1: the simple way
Treat the head as a special case.
- If the list is empty, return
null. - If the head holds
key: if it is the only box, returnnull. Otherwise walk around to find the last box, point it athead.next, and returnhead.next. - Otherwise walk with
prevandcurrfrom the head. Whencurrholdskey, setprev.next = curr.next. Stop whencurrcomes back to the head.
Deleting the head the simple way: a whole extra walk around the ring just to find the last box, 5.
- looking now
- seen
- rejected
class Solution {
Node deleteNode(Node head, int key) {
if (head == null) return null;
if (head.data == key) {
if (head.next == head) return null; // the only box
Node last = head;
while (last.next != head) last = last.next; // walk around to the last box
last.next = head.next; // close the ring without the head
return head.next;
}
Node prev = head, curr = head.next;
while (curr != head) {
if (curr.data == key) {
prev.next = curr.next; // skip over the box
return head;
}
prev = curr;
curr = curr.next;
}
return head; // key was not in the ring
}
}How long it takes: at most two trips around the ring, O(n). Extra room: two fingers, O(1).
Way 2: the clever way
Start checking at the second box and let the head be the last one you check. When you finally check the head, prev is already standing on the last box, which is exactly the box that must be rewired. One walk handles every case.
- If the list is empty, return
null. Putprevon the head andcurronhead.next. - If
currholdskey: ifcurr == prev, the ring had one box, so returnnull. Otherwise setprev.next = curr.next. Ifcurrwas the head, returncurr.nextas the new head, else returnhead. - If
curris the head and it did not match, you went all the way around. Returnhead. - Otherwise slide both one step and repeat.
Check the head last, so prev is already on the last box
01 / 07
We want to remove key 1, the head. Start with prev on the head and curr on the second box.
- key
- 1
- looking now
- checking
- seen
- done
- answer
- rejected
If the key were 3, the walk would stop at the second check with prev on 2, set 2.next = 4, and return the old head.
class Solution {
Node deleteNode(Node head, int key) {
if (head == null) return null;
Node prev = head, curr = head.next; // the head is checked last
while (true) {
if (curr.data == key) {
if (curr == prev) return null; // it was the only box
prev.next = curr.next; // hold hands over the gap
return curr == head ? curr.next : head;
}
if (curr == head) return head; // all the way round, no key
prev = curr;
curr = curr.next;
}
}
}How long it takes: at most one trip around the ring, O(n). Extra room: two fingers, O(1). Same big O as Way 1, but one walk instead of two when the head goes, and no special head branch.
Watch out for
- The one box ring.
prevandcurrare the same box, so returnnull. Writingprev.next = curr.nextthere would keep the box alive. - A key that is not there. A
while (curr != head)style loop is needed so you stop after one trip. A plainwhile (curr != null)never ends in a ring. - Duplicate keys. Way 2 checks the head last, so with duplicates it removes the first match after the head. If you must remove the first match starting at the head, check the head first as in Way 1.
- The copy trick (copy
head.nextinto the head, then deletehead.next) also removes a value, but it changes which box holds which value. If other code holds a reference to those boxes, that is a bug.
What interviewers ask next
- Delete at a position instead of a key. Walk
pos - 1steps withprev(or reach the last box first whenposis the head) and rewire the same way. - Delete a box when you are only handed that box, not the head. Copy the next box's value into it and skip the next box. That is
O(1), but it fails for a one box ring and moves values around. - Insert into a sorted circular list (LeetCode 708). Walk with
prevandcurruntil the new value fits between them, or you hit the spot where the values drop from largest back to smallest. - Why keep a pointer to the last box instead of the head? With
last, the head islast.next, so you can add at the front and at the back inO(1).
Remember this
Grown-up words: the ring of kids is a circular singly linked list, the kid on the left is the previous node, holding hands over the gap is prev.next = curr.next, and checking every kid once is one traversal of the cycle.