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

Best approachTime O(1) per commandSpace O(n)

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).

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * 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)
  1. What do the sentinels save?
  2. Why do we need prev links?