Concepts / Understanding Stacks, Queues, and Abstract Data Types

Understanding Stacks, Queues, and Abstract Data Types

An abstract data type is a set of possible values and a set of operations on those values, specified without saying how the values are represented or how the operations are implemented.

  • Programming
Interactive lab

Try it: Stack and Queue

The difference between a stack (last in, first out) and a queue (first in, first out).

How it works

  1. A stack adds (push) and removes (pop) at the same end, the top — the last item added leaves first.
  2. A queue adds (enqueue) at the back and removes (dequeue) from the front — the first item added leaves first.
  3. peek / front look at the next item without removing it.
  4. Adding to a full structure is an overflow; removing from an empty one is an underflow.

Default run (3 steps): Stack (bottom → top): 4, 7. 2 of 8 slots used. Choose an operation. Example: push 9. … Stack is now: 4, 7, 9. Size 3/8.

Simplified: Fixed capacity of 8 items so overflow can be shown; Python lists and collections.deque grow as needed.

Educational simulation

Loading the simulation…

The Question Before the Storage Question

Suppose you are writing a program that must keep a list of names in alphabetical order. You need to add names, remove names, and check whether a name is present. A natural first question is where the names should live in memory and how the list should be arranged internally. This article asks a different question first: what exactly is the list, apart from the way it happens to be stored?

Keep that question in mind as you read. The answer leads to a central idea in software engineering, the abstract data type, usually written ADT. Once you see what an ADT is, you will also see why the same list of names can be stored in very different ways without changing the rest of the program.

Tracing a Sorted List of Names

Worked example

Insert, delete, and find on an ordered list

The list starts as Ana, Ben, Dev, kept in alphabetical order. Apply three operations in sequence: insert Carl, delete Ben, then find Carl. Describe the list after each step and the result of the find.

  1. 1
    Starting value

    The list is Ana, Ben, Dev. Every element is in increasing order, so this is a valid value of the ordered list type.

  2. 2
    Insert Carl

    Carl belongs between Ben and Dev to keep the order, so the list becomes Ana, Ben, Carl, Dev. The insert operation is responsible for placing Carl in the right position.

  3. 3
    Delete Ben

    Removing Ben leaves Ana, Carl, Dev. The order is still increasing, so the result is still a valid value of the type.

  4. 4
    Find Carl

    Carl is in the list, so find reports that the name is present. The caller learns the answer without knowing how the list is stored.

Result

After the three operations the list is Ana, Carl, Dev and Carl is present. Each step was reasoned about only in terms of the list's values and operations.

Notice what this reasoning did not require. You could carry out every step on paper with a pencil and a sheet of names. You did not decide whether the names sit in a fixed block or a chain of linked objects, and you did not decide how insert moves items internally. That separation is deliberate, and the rest of the article builds on it.

Values Plus Operations

Definition

An abstract data type is a set of possible values and a set of operations on those values, specified without saying how the values are represented or how the operations are implemented.

The definition has two halves, and both matter. The first half names the values the type can take. The second half names the operations that act on those values. The definition is silent on everything else. It does not say whether the values sit in a chain of linked objects, in a fixed-size block, or somewhere else. It does not say how an operation works internally. It describes only what is possible and what can be done with it.

For the ordered list of strings, the set of values is every sequence of strings arranged in increasing order. The operations are inserting a new string, deleting a string, and finding a string in the list. Nothing in that description mentions arrays, linked objects, or pointers. Those details belong to a later decision, the implementation.

Key point

An ADT is defined by what its values are and what you can do with them. A concrete data structure is one specific way of storing those values and carrying out those operations.

One List, Several Representations

A linked list is a particular type of data structure, made up of objects linked together by pointers. Earlier, a linked list was used to store the ordered list of strings, and insert, delete, and find were implemented on that list. The source makes a striking observation: the same list could just as easily have been stored in an array or an ArrayList, and the same operations could still have been implemented on it.

The diagram below shows the same three names held in two representations. On the top row the names occupy numbered slots side by side, as in a contiguous array. On the bottom row each name sits in its own node, and each node points to the next one, as in a linked structure. The logical list is identical in both rows.

Slot 0AnaNode 1AnaSlot 1BenNode 2BenSlot 2DevNode 3Devnextnext
How does the same ordered list of strings look different when stored as a contiguous array versus as linked nodes, while its logical values and operations stay the same?

The implementations differ on the inside. An array-based insert and a linked-list insert do not work the same way: one may need to shift elements to make room, while the other rewires pointers between nodes. Yet their interfaces and their logical behavior stay the same. Callers still ask for an insert, and the list still ends up in the same order. This is the core of what makes an ADT useful: one abstract type can have several implementations.

Interface Versus Implementation

The source describes this idea as data abstraction: separating the interface of a data structure from its implementation. The interface is the set of operations a caller can use, along with their logical behavior. The implementation is the concrete way those operations are carried out. Data abstraction keeps these two apart on purpose.

The diagram below reads from top to bottom. The calling program depends on the interface and nothing lower. The two implementations sit beneath the interface, and either one can fill the role. Because the arrow from the program stops at the interface, a change to the storage below it does not reach the program.

Calling programOrdered listinterfaceinsert, delete, findArray implementationLinked implementationdepends onrealizesrealizes
What does the client code depend on, and what can be swapped underneath without the client changing?

Think about the consequence. If a team writes its program against the abstract definition, switching the storage later is a change confined to the implementation. The rest of the program is untouched. The source states this directly: changing the implementation of an ADT does not affect the program as a whole, and this can make programs easier to debug and maintain.

Best practice

When writing code that uses a collection, call its operations by their logical meaning, such as insert, delete, and find, and avoid reaching into the internal storage directly. Code written this way can keep working when the storage is later swapped.

Stacks and Queues as Abstract Types

A stack follows a last-in, first-out rule, usually abbreviated LIFO. Its main operations are push, which adds an element, and pop, which removes the most recently added element. A queue follows a first-in, first-out rule, abbreviated FIFO. Its main operations are enqueue, which adds an element at the back, and dequeue, which removes the element that has waited longest.

What do you think happens?

Starting from an empty stack, push red, then push green, then pop. Which element is returned by the pop?

  • red

  • green

  • The stack is empty, so nothing is returned

Reveal answer
Answer:

green

A stack is last-in, first-out. Green was pushed most recently, so it is the first to be removed. Red stays in the stack.

Worked example

Enqueue and dequeue over time

Starting from an empty queue, enqueue x, enqueue y, dequeue, enqueue z, then dequeue. Track the contents after each operation and identify each element removed.

  1. 1
    Enqueue x

    The queue holds x, the only element at the front and back.

  2. 2
    Enqueue y

    y joins at the back, so the queue holds x then y from front to back.

  3. 3
    Dequeue

    The front element leaves first. x is removed, leaving y.

  4. 4
    Enqueue z

    z joins at the back, so the queue holds y then z.

  5. 5
    Dequeue again

    y is at the front, so it is removed, leaving z.

Result

The dequeues returned x and then y, and the queue now holds z. The order of removal matches the order of arrival.

Mistakes When Reading About ADTs

  • Common mistake

    Treating an ADT as one specific data structure

    What it looks like

    Saying the ordered list of strings is an array because the program happens to store it in one.

    Why it fails

    The ADT is only the values and operations. An array or a linked list is one way to realize it.

    Do this instead

    Name the ADT by its values and operations, then name the implementation separately.

  • Common mistake

    Believing the implementations must behave identically on the inside

    What it looks like

    Expecting an array insert and a linked-list insert to move the same pieces of memory in the same way.

    Why it fails

    The internal code for the operations differs between implementations. Only the interface and logical behavior are shared.

    Do this instead

    Compare implementations by their logical behavior, and expect their internal steps to differ.

  • Common mistake

    Putting implementation details into the description of the type

    What it looks like

    Defining the ordered list as a chain of linked objects connected by pointers.

    Why it fails

    That description commits to one representation, so it no longer describes the abstract type.

    Do this instead

    Describe the type as sorted sequences of strings with insert, delete, and find, and leave storage out.

  • Common mistake

    Assuming a stack and a queue differ only in name, when they remove elements in opposite orders

Classifying Parts of a Design

EASY

A team stores a sorted list of customer names. Its program calls only insert, delete, and find. Later, the team replaces the storage with a different implementation and the program runs unchanged. Identify which part of this description is the abstract data type, which part is the interface, and which part is the implementation. Then explain why the program did not need to change.

Hints
  • The ADT is the values and operations, with no storage detail.
  • The interface is the set of operations the caller uses and their logical behavior.
  • Ask whether the calling program ever refers to the internal storage.

Key Takeaways

  • An abstract data type is a set of possible values and a set of operations on those values, specified without saying how the values are represented or how the operations are implemented.
  • One ADT, such as the ordered list of strings, can be realized by several implementations, including a linked list or an array, and each can be used interchangeably.
  • Data abstraction separates the interface, which callers use, from the implementation, which carries out the operations, so a change to storage does not reach the program as a whole.
  • Stacks follow last-in, first-out and queues follow first-in, first-out; in both cases the structure's contents and removal order depend only on its operations.