Deque Class with a Doubly Linked List
Problem
Build a double-ended queue. Commands: pushf X (add at the front) and pushb X (add at the back) print ok; popf and popb (remove from the front or back), peekf and peekb (look at the front or back) print the number, or empty if the deque is empty; size prints how many numbers are inside.
Constraints
- 1 <= commands <= 100
Examples
Input
pushb 1 pushf 0 peekb popf size
Output
ok ok 1 0 1
Solutions
With two sentinel nodes, every push and pop is the same few link changes, at both ends and for any size. All operations are O(1).
/*
* Problem : Run the commands.
* Input : Command lines.
* Output : Answer lines.
* Approach: Doubly linked deque
* Time : O(1) per command
* Space : O(n)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// Step 1: Each node knows the node before it and the node after it. Two sentinel nodes (head and tail) remove all special cases.
static class Node {
int value;
Node prev;
Node next;
Node(int value) {
this.value = value;
}
}
static class Deque {
private final Node head = new Node(0);
private final Node tail = new Node(0);
private int size;
Deque() {
head.next = tail;
tail.prev = head;
}
// Step 2: Insert a new node between two neighbours.
private void insertAfter(Node before, int value) {
Node node = new Node(value);
node.prev = before;
node.next = before.next;
before.next.prev = node;
before.next = node;
size++;
}
void pushFront(int value) {
insertAfter(head, value);
}
void pushBack(int value) {
insertAfter(tail.prev, value);
}
// Step 3: Remove a node by joining its two neighbours. The caller checks that the deque is not empty.
private int remove(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
size--;
return node.value;
}
int popFront() {
return remove(head.next);
}
int popBack() {
return remove(tail.prev);
}
int peekFront() {
return head.next.value;
}
int peekBack() {
return tail.prev.value;
}
int size() {
return size;
}
}
// This method solves the task. It receives the input and returns the answer.
static String[] solve(String[] lines) {
Deque deque = new Deque();
String[] out = new String[lines.length];
for (int i = 0; i < lines.length; i++) {
String[] part = lines[i].split(" ");
String name = part[0];
if (name.equals("pushf")) {
deque.pushFront(Integer.parseInt(part[1]));
out[i] = "ok";
} else if (name.equals("pushb")) {
deque.pushBack(Integer.parseInt(part[1]));
out[i] = "ok";
} else if (name.equals("size")) {
out[i] = String.valueOf(deque.size());
} else if (deque.size() == 0) {
out[i] = "empty";
} else if (name.equals("popf")) {
out[i] = String.valueOf(deque.popFront());
} else if (name.equals("popb")) {
out[i] = String.valueOf(deque.popBack());
} else if (name.equals("peekf")) {
out[i] = String.valueOf(deque.peekFront());
} else {
out[i] = String.valueOf(deque.peekBack());
}
}
return out;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
java.util.List<String> all = new java.util.ArrayList<>();
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (!line.isEmpty()) {
all.add(line);
}
}
String[] lines = all.toArray(new String[0]);
System.out.println(String.join("\n", solve(lines)));
}
}
Hints (2)
- What do the sentinels save?
- Why do we need prev links?