← Back to list
Computing & Embedded
#자료구조#스택#큐#리스트#LIFO#132회#125회
Last updated · 2026-09-28

Linear Data Structures: Stack, Queue, and List

1. Overview

A. Definition

Data structures that store data laid out in a line (linear, one-dimensional), where each element is adjacent to its predecessor and successor 1:1, divided by input/output rule into stack (LIFO), queue (FIFO), and list (random access).

Unlike nonlinear structures such as trees and graphs, in a linear data structure the relation between elements is defined only by the simple "previous-next" order. Because each element has at most one preceding and one following element, the structure is intuitive and its implementation simple. Though it looks simple at a glance, the crux of this family is that where input/output is allowed gives rise to entirely different properties and uses. Stacks and queues deliberately restrict the access point (both ends or one end) to enforce a specific processing order, while lists allow access at any position without restriction.

Seen through the lens of this "access restriction," the three structures lie on a single spectrum. The one that restricts access most strongly is the stack (one end), next the queue (separating the two ends by role), and the one without restriction is the list. The stronger the restriction, the simpler the operations and the more a specific order is guaranteed, but flexibility drops; the weaker the restriction, the more flexible, but the higher the cost of finding or moving an element. The very reason these three are taught together lies in this contrast.

Paradoxically, restriction is power. Because the stack gives up middle access, it guarantees the "most recent first" order without separate sorting, and because the queue separates insertion and deletion points, it automatically preserves "first arrived first." To implement such an order with a list would require extra logic to manage the position each time. That is, a special structure that restricts access produces simpler and safer code for a specific problem, and this is why stacks and queues are kept separate even though a general-purpose list exists.

B. Background and Necessity

A program's algorithmic correctness and efficiency hinge on "in what order data is put in and taken out." For example, since a function call must have the last-called function finish first, LIFO is natural; since a printer queue must process the first-requested job first, FIFO is natural. Thus each problem has a required processing order, and a data structure is the tool that guarantees that order by its very structure.

Choosing a data structure is ultimately a design decision to minimize operation cost by matching the problem's access pattern. Even for the same data, a key operation becomes O(1) or O(n) depending on which structure it is placed in. Choosing the wrong structure breaks performance even when the logic is correct, while choosing one that matches the access pattern simplifies the code and improves performance. Linear data structures are the clearest starting point for this principle that "structure is the rule."

Moreover, stacks, queues, and lists are used not only in their own right but also as basic components of more complex data structures and algorithms. A tree's depth-first traversal is implemented with a stack and its breadth-first traversal with a queue, and hash-collision chaining is made with a linked list. Therefore, accurately understanding the properties of these three structures forms the foundation for all later study of data structures and algorithms.

Historically too, these structures have existed since the early days of computing. The stack was introduced at the hardware and language level to handle subroutine calls and returns, and the queue originated in job queues of the batch-processing era. Today's CPUs also manage function calls with a stack-pointer register, and the operating-system scheduler runs on a queue. That is, these three structures are abstract concepts and at the same time fundamental tools physically implemented in actual hardware and system software.

2. Overall Structure and the Stack

flowchart TB
  subgraph Linear["Linear data structures"]
    S["Stack (LIFO)"]
    Q["Queue (FIFO)"]
    L["List (random access)"]
  end
  S -->|"I/O at one end only"| U1["Function call, undo, DFS"]
  Q -->|"ends split by role"| U2["Scheduling, buffer, BFS"]
  L -->|"no restriction"| U3["General sequential management"]

The diagram above shows that the three structures branch by the degree of access restriction and lead to different uses. Now we examine each structure in depth, in order.

flowchart TB
  P["Push insert"] --> T(("Top"))
  T --> O["Pop delete"]

A stack is a LIFO (Last In First Out) structure where insertion and deletion occur only at one end (Top). Like stacking plates and taking them from the top, the most recently inserted data comes out first. Since insertion (push), deletion (pop), and top inspection (peek) all touch only the single Top point, each operation finishes in O(1). There is the constraint that middle elements cannot be accessed directly, but that very constraint guarantees the "process most recent first" order for free.

This LIFO property is powerful in "must go back" problems. Representatively, the function call stack directly expresses the nesting relation of call→return. If function A calls B and B calls C, C finishes first and returns in the order B, A, and this reverse-order return is exactly LIFO. That deep recursion overflows this stack and causes a stack overflow is the same principle. An editor's undo likewise must reverse the most recent action first, so it is implemented with a stack, and managing undo/redo as a pair of two stacks naturally supports both undo and redo.

From an implementation standpoint, a stack can be built with either an array or a linked list. The array-based version needs only one index pointing at Top, so it is simple and cache-efficient but has a maximum-size constraint, while the linked-list-based version has no size constraint but incurs pointer overhead per node. Either way, the push/pop interface and O(1) performance seen by the user are identical, and this "same outside, different inside" trait leads to the abstract data type (ADT) concept discussed later.

Stacks are also central in the computation and search domain. Parenthesis matching in expressions pushes opening parentheses and pops at closing parentheses to match pairs, and infix→postfix conversion and postfix evaluation also manage operators and operands with a stack. A graph/tree DFS (depth-first search) is implemented with an explicit stack or recursion (an implicit call stack), because "dig into one path to the end and backtrack when blocked" matches the stack's push/pop.

The fundamental reason a stack is used so widely across diverse problems is that the patterns of nesting and reverse-order processing recur throughout computing. Nesting of parentheses, nesting of function calls, nesting of HTML/XML tags, and backtracking of a search path all have the identical structure of "close the innermost (most recent) first." A stack captures this pattern in a single data structure, so problems that appear unrelated are in fact solved by the same solution.

Item Content
Principle LIFO — I/O only at Top
Operations push (insert), pop (delete), peek (inspect), all O(1)
Constraint No random access to middle elements
Uses Function call stack, undo, expression evaluation, DFS

3. Queue

A queue is a FIFO (First In First Out) structure that inserts (enqueue) at the rear and deletes (dequeue) at the front, like people lining up. Since the data that entered first exits first, it is used where a fair order (processing by arrival) is needed. If a stack is "recent first," a queue is "first come first served," and this difference completely separates the two structures' uses.

A queue's representative use is resource waiting and absorbing speed differences. In an operating system's job/process scheduling, the ready queue allocates the CPU by arrival order, and printer/network requests are processed by request order to maintain fairness without starvation. In particular, placing a queue between two modules whose production and consumption speeds differ makes it act as a buffer, letting a fast producer pile up data without waiting for a slow consumer. Keyboard input buffers, message queues, and streaming buffers all follow this principle. A graph's BFS (breadth-first search) is also implemented with a queue because its "visit nearby nodes in turn" order matches FIFO.

A queue as a buffer is central to the classic concurrency pattern of the producer-consumer problem. In a structure where multiple producers put data into the queue and multiple consumers take it out to process, the queue acts as a cushioning zone, absorbing both sides' speed variation and lowering coupling. This idea extends beyond a single program to distributed systems, becoming the root of the event-driven architecture that links microservices with message queues. Merely placing one queue between them creates a loose coupling in which producer and consumer need not know each other's existence, speed, or availability.

Implementing a queue with a plain array causes a problem. Repeated dequeues keep pushing front backward, so even when the front of the array is empty, rear reaches the array's end and can insert no more—a waste of space. To solve this, a circular queue is used, logically joining the array's end to its beginning to reuse the empty front space. A circular queue cycles the index with modular arithmetic, reusing a fixed-size array without waste. Further, a deque (Double-Ended Queue) allowing I/O at both ends and a priority queue (usually implemented with a heap) taking the highest-priority element first are representative variants of the queue, used respectively in algorithms such as sliding window and Dijkstra's shortest path.

The deque is interesting in that it is a superordinate concept encompassing both stack and queue. Using only one end makes it a stack, and inserting at one end and removing at the other makes it a queue, so one deque can substitute for both structures. In fact, this is why the standard libraries of several languages provide stacks and queues as deque implementations rather than as separate data types. This is a good example showing that data structures are not mutually independent but bound by containment/specialization relations.

Item Content
Principle FIFO — insert at rear, delete at front
Operations enqueue (insert), dequeue (delete), O(1)
Variants Circular queue, deque, priority queue
Uses Job scheduling, buffer, BFS

A priority queue is strictly not FIFO but "in priority order," which distinguishes it from a pure queue. It is nonetheless grouped with queues because its interface of "insert and take out one at a time (extract)" is the same. Making diverse variants by changing only the internal rule under the same abstract interface like this is a typical pattern of data-structure design.

Contrasting the difference between stack and queue in one sentence, the stack goes against time and the queue follows time. The stack processes the most recent event first, fitting "undo," while the queue processes the oldest event first, fitting "fair order." Thus a stack is used for undo and backtracking, and a queue for request processing and event delivery. Determining whether a problem is "recent first" or "first arrived first" becomes the decisive criterion for choosing one of the two structures.

4. List

A list is a general-purpose linear structure that, placing no restriction on access position, allows insertion, deletion, and inspection at any position. If stacks and queues are special-purpose structures that enforce order, a list is a general-purpose container that handles order freely. In fact, since stacks and queues can be seen as lists specialized by imposing access restrictions, the list corresponds to the most general form of a linear structure. However, under the single name "list" the implementation splits largely into two, and because that difference makes performance opposite, it becomes the crux of practical choice.

An array list places elements side by side in a contiguous memory space. Knowing only the index reaches an element immediately by adding an offset to the start address, so random access is O(1), and because memory is contiguous the CPU cache-hit rate is high, so traversal performance is also good. However, inserting or deleting an element in the middle requires pushing or pulling all following elements by one slot, costing O(n). There is also the reallocation cost of, when capacity fills, allocating a larger array and copying the whole thing.

A linked list has each node holding, along with data, the address (pointer) of the next node, so the nodes are connected by pointers even when scattered throughout memory. Insertion and deletion just fix the pointers of the neighboring nodes to splice in or out, so it is O(1) when the position is known, and there is no reallocation as the size grows and shrinks dynamically. Instead, finding the element at a specific ordinal requires sequential movement from the first node following pointers, so access is O(n), accompanied by the memory overhead of storing a pointer per node and cache inefficiency.

In sum, the principle is "array list if inspection/traversal dominates, linked list if middle insertion/deletion is frequent." For example, read-centric data that is frequently searched and traversed favors an array, while a queue or history log where elements come and go constantly favors a linked list. A linked list is further divided into a singly linked list pointing in one direction only, a doubly (bidirectional) linked list pointing both ways, and a circular linked list whose end connects to the beginning; the doubly linked list is advantageous for reverse traversal and deletion of a specific node.

However, it must be noted that on modern hardware, performance cannot be pronounced by this theoretical complexity alone. Even if a linked list's insertion is O(1), if nodes scattered in memory cause frequent cache misses, it can in practice be slower than the sequential access of an array that fits the cache well. So for small data where element-moving cost is not large, or when traversal is frequent, there are many cases in practice where the array list, seemingly disadvantageous in theory, is actually faster. Complexity analysis is an essential starting point, but the final judgment must consider data scale, access pattern, and hardware characteristics together.

Implementation Access Insert/Delete Traits
Array list O(1) O(n) Contiguous memory, cache efficiency, reallocation cost
Linked list O(n) O(1)* Pointer-linked, dynamic size, memory overhead

* O(1) when the insertion/deletion position is already known; including the search to find the position it is O(n).

5. Comparison and Cases

The difference among the three structures ultimately arises from the single axis of "how much access is restricted." Stacks and queues restrict the access point to structurally guarantee processing order (LIFO/FIFO) while giving up random access, whereas the list gains random access while setting down the property of order guarantee. That is, the trade of "what is guaranteed and what is given up" separates the three structures.

Category Stack Queue List
I/O rule LIFO FIFO Arbitrary
Access point Top only Front/Rear Sequential or index
Key operation cost push/pop O(1) enqueue/dequeue O(1) Access vs insert opposite
Representative use DFS, undo, expressions BFS, buffer, scheduling General sequential management

Understanding this trade shows that the question "which structure is best" itself does not hold. Each structure is merely a tool optimized for a specific access pattern; there is no absolute superiority. Demanding random access of a stack or frequent front insertion of an array list is using a tool against its purpose, and the performance degradation that then appears is not a flaw of the data structure but a failure of choice.

As a concrete case, in a web browser the three structures coexist within one program. Back/forward manages visit history with two stacks (reversing from the most recent page), download/request processing puts requests into a queue by arrival order, and the list of open tabs is managed with a list because arbitrary addition/deletion is frequent. Thus even within one application, the access pattern differs by function, so different linear structures are used together.

As another case, in an operating system's process management the ready queue (FIFO or priority queue) sets the execution order, and each process's function calls manage local variables and return addresses with a call stack. As a performance-side case, frequently inserting at the front of a list of 100,000 elements with an array list accumulates O(n) moves each time and slows down, but switching to a linked list makes each insertion close to O(1), greatly improving perceived performance. This one choice governs the program's responsiveness.

There is a case in the opposite direction too. If randomly inspecting some data by index happens tens of thousands of times per second, in a linked list each inspection becomes an O(n) sequential search that can paralyze the system, but in an array list it finishes immediately in O(1). Thus the point that even for the same data the optimal structure flips to the opposite depending on which operation dominates is the core lesson of list choice, and it is also why stacks, queues, and lists are learned together.

6. Deep Dive: Abstract Data Type (ADT) and the Extension Perspective

Understanding stacks, queues, and lists more deeply requires the concept of the abstract data type (ADT). A stack is defined only by the specification of the operations "push, pop, peek" (what it does), and whether it is implemented with an array or a linked list looks identical to the user. That is, separating the interface (specification) from the implementation (internal storage) is the core of the ADT, and thanks to it, changing the internal implementation to meet performance needs keeps the code that uses it unchanged. That replacing a stack from array-based to linked-list-based does not change the call site is an example.

The standard libraries of actual programming languages also follow this principle. For example, Java's ArrayDeque is a deque implementation usable as both stack and queue, and LinkedList works as both list and queue. C++'s std::stack and std::queue are designed as adapters whose internal container (deque, list, etc.) can be swapped, directly showing the philosophy of separating ADT from implementation. In practice, the demand "a stack is needed" means "a LIFO interface is needed," and the concrete data type can be chosen to match performance characteristics.

From the extension perspective, these three structures are basic blocks for building nonlinear and composite data structures. A tree's traversal internally uses a stack (DFS) and a queue (BFS), and a hash table's collision resolution (chaining) links buckets with a linked list. Graph algorithms broadly stand on stacks and queues, and the priority queue is the heart of optimization algorithms such as Dijkstra and Prim. Therefore, firmly mastering linear structures amounts to the basic stamina that supports all later data structures and algorithms.

In the same vein, the latest large-volume processing technologies also share this root. The event pipeline of a stream-processing engine, the job queue of a task scheduler, and the buffer of a log-collection system all stand on a queue, and undo history and transaction rollback follow the stack idea. Even as scale and implementation change, the fundamental question "in what order to put in and take out" and its answer—the principles of LIFO, FIFO, and random access—do not change.

This ADT perspective is also directly linked to practical maintainability. When the interface and implementation are separated, one can start with a simple array-based version and, when data grows and insertion cost becomes a problem, replace the internal with a linked list or another structure, without touching the higher-level code that uses it at all. Good design keeps open "what it can be changed to later" rather than "what it uses now," and the ADT design of linear data structures is the best example for learning that principle.

From an information-management professional engineer's perspective, exam questions deepen beyond simple definition comparison to require arguing, by operation complexity and access pattern, "which structure is suitable for a specific problem situation and why." Therefore, in an answer, the key strategy is to explain each structure's principle (LIFO/FIFO/arbitrary), the time complexity of representative operations, and the trade-off of array vs. linked, woven together with actual application cases.

7. Considerations and Implications

  • Access-pattern-first design: Data-structure choice must start from the required processing order and access pattern. If LIFO is needed choose a stack, if FIFO a queue, and if random access/sequential management a list; for a list, further decide array/linked by whether access or insertion/deletion dominates. Fixing the structure first and then forcing the problem to fit leads to performance degradation.

  • Explicit judgment of the time-space trade-off: An array list's O(1) access and a linked list's O(1) insertion are a trade that cannot be obtained simultaneously. Considering data scale, read/write ratio, cache locality, and memory headroom together, one must decide which cost to bear. The criterion is not "which is faster" but "which operation dominates."

  • Robustness of boundary/exception handling: Boundary conditions such as a stack's overflow/underflow, a queue's full/empty, and a list's null pointer/boundary index must be handled robustly to secure production stability. In particular, since a recursion-based algorithm's call-stack depth limit leads directly to stack overflow, deep recursion needs a design that converts to an explicit stack or a loop.

  • Concurrency/scalability consideration: In a multithreaded environment, a shared queue/stack has race conditions, so a lock or lock-free structure is needed. In large-scale distributed environments it extends beyond an in-memory queue to message-queue middleware (Kafka, RabbitMQ, etc.), and even then the essential queue principle of FIFO and buffering is inherited as is. This is the point where understanding basic data structures leads to large-scale system design.

  • Habituating abstraction and implementation separation: Designing code to depend on the abstract interfaces of stack/queue/list rather than directly on concrete data types (array/linked list) makes it change-resistant, since only the implementation need be swapped when performance needs change. An attitude of designing on the premise that data-structure choice is not a one-time decision but a process re-examined as data scale and pattern change is needed.


In one line: A stack is LIFO with I/O only at Top, a queue is FIFO inserting at rear and deleting at front, and a list is a linear data structure allowing insertion, deletion, and access at any position; the difference among the three arises from "how much access is restricted," they must be chosen on the basis of processing order, access pattern, and the array vs. linked trade-off, and they become basic blocks composing composite data structures such as trees, graphs, and hashes.