Undo/Redo are stack based (first in, last out) operation from principle, so you need some kind of stack based architecture to store actions.
With that being said, the way how you stack behaviour can differ based on other requirements of your application. Usual way is to have stack backed by array - you keep current index of last element and you add (remove) new stuff elements to at that index and increase (decrease) ot accordingly
But you can also implement this first-in-last-out behaviour in a different way, for example double linked list - you will keep reference to the last element of the list and either add new to the end (and update this reference) or remove last element (and also update the reference to the new last). Giving yourself stack-like behaviour
EDIT:
For redo you keep separate stack, where you push commands whenever you undo them, so you can use it later (pop from redo stack, do it, push to undo stack)
It is important to clear redo stack when you push new (e.g. not from redo stack) command to undo stack