History Query on Stack
Define your own stack structure to support the following three operations:
push
push a new element into the stackpop
pop a new element into the stackquery
query the state of the stack after operationi
Assume i = 0
is the initial state of the stack, when the stack is empty. All
other operations including query
itself is numbered as 1, 2, 3 and etc.
Design a data structure to support these actions and let's the time and space
as efficient as possible.
We can save the operation history and the operating number into a history list, once a query comes, we redo every thing from the beginning and get the status of stack. This solution cost a lot in query time and is not acceptable.
If you are using a array to store the element of stack. There are not many things we can do to improve the space and query cost. But if we open our mind and use a linked list to store elements, we can come up a quite good solution.
Using a singly linked list, we use the head as the top of stack. After each operation,
we save a pointer to the head in history. Then, when queried the i
th operation,
we just return the head saved in the history list at position i
. In fact,
reference to every element pushed in the stack will be kept and the singly linked list
along with the elements saved in the history list is constructed as a directed acyclic graph.
Following code shows how to do it:
import java.util.*;
class StackNode<T> {
T val;
StackNode<T> next;
StackNode(T val) {
this.val = val;
}
}
public class HistoryStack <T>{
StackNode<T> head = null;
ArrayList<StackNode<T>> history = new ArrayList<StackNode<T>>();
HistoryStack() {
history.add(head);
}
public void push(T val) {
StackNode<T> node = new StackNode<T>(val);
node.next = head;
head = node;
history.add(head);
}
public T pop() {
T ret = null;
if (head != null) {
ret = head.val;
head = head.next;
return ret;
}
history.add(head);
return ret;
}
public List<T> query(int time) {
if (time >= history.size()) return null;
StackNode<T> p = history.get(time);
LinkedList<T> status = new LinkedList<T>();
while (p != null) {
status.addLast(p.val);
p = p.next;
}
return status;
}
}
Note that we are using an ArrayList
to save the history so that get the i
th history
has constant time complexity. A normal linked list need an 's time cost to
access the i
th element which is no better than redo all the history. Also we can use
a linked list that each node at index i
has a pointer to the node i * 2
so that
we can access a node in . Added items in the history list won't be remove or changed,
so it is extremely suitable to similar methods that could improve accessing speed but may cost
a lot to remove or modify items in the list.