Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 5

Data Structure Operations: Time Complexity

Recommended resources

Hello! Welcome to the fifth and final lesson in our foundational module on complexity analysis.

In our last session, you learned to compare the common complexity classes, from the blazing-fast to the prohibitively slow . You now have a mental model for what makes an algorithm "efficient" or "inefficient."

Today, we will ground that theoretical knowledge in practice. This lesson's objective is to identify the time complexity of basic operations on arrays, hash tables, and linked lists. These three data structures are the fundamental building blocks for solving a vast majority of coding interview problems. Knowing their performance characteristics is not just helpful; it's a prerequisite for success.

We will examine each data structure, focusing on the "Big O" of its core operations:

  • Accessing an element
  • Searching for an element
  • Inserting an element
  • Deleting an element

Let's begin.

1. Arrays and Dynamic Arrays

Arrays are collections of elements stored in a contiguous block of memory. This memory layout is the key to their performance. In languages like Python, Java, Go, and Rust, the list-like types you use daily (Python list, Java ArrayList, Go slice, Rust Vec<T>) are implemented as dynamic arrays. They automatically resize when they run out of space.

First, watch this short segment for a visual explanation of how arrays are stored and how their basic operations perform.

Top 8 Data Structures for Coding Interviews

This video from NeetCode clearly explains how an array's contiguous memory model affects its operational complexity.

Watch from 00:11 to 02:03. Focus on how memory layout dictates the speed of access versus insertion/deletion in the middle.

Let's break down those complexities:

  • Access (by index):

    • Why? Since elements are in a contiguous block, the memory address of any element can be calculated directly from its index: address = start_address + index * element_size. This is a single arithmetic operation, hence constant time.
  • Search (by value):

    • Why? In an unsorted array, there's no way to know where a specific value is located. In the worst case, you must check every single element from beginning to end.
  • Insertion / Deletion (at the end): amortized

    • Why? Adding to the end is usually just placing an element in the next available memory slot. However, if the dynamic array is full, it must be resized. This involves allocating a new, larger block of memory and copying all elements over. While this single operation is , it happens infrequently. When the cost is averaged over many insertions, the time per insertion becomes constant. This is known as amortized constant time. Your experience with system capacity planning likely gives you an intuitive sense of this pattern of smooth performance punctuated by occasional, expensive scaling operations.
  • Insertion / Deletion (at the beginning or middle):

    • Why? To maintain the contiguous nature of the array, inserting or deleting an element at index i requires shifting all subsequent elements (from i to n-1) one position to the right or left. In the worst case (at index 0), this means shifting all elements.

The following resource provides clear tables summarizing these costs for Python lists and Java ArrayLists.

Time complexity of array/list operations [Java, Python]

To see how these complexities manifest in common programming languages, review the cheat sheets in this article from 'YourBasic'.

Read the section 'Expensive list operations' and examine the tables 'Expensive Java ArrayList methods' and 'Expensive Python list operations'. Note how operations at arbitrary indices i often depend on n - i.


2. Hash Tables (Hash Maps)

Hash tables (often called hash maps, dictionaries, or map in Go) are the go-to data structure for near-instant lookups. They store data as key-value pairs. Their power comes from a hash function, which converts a key into an index in an underlying array.

Watch this segment to get a high-level overview.

Top 8 Data Structures for Coding Interviews

This next clip from the NeetCode video introduces hash maps and their remarkable efficiency.

Watch from 04:11 to 05:57. Pay attention to the core idea: mapping a flexible key to an array index to achieve O(1) operations.

Let's formalize the complexities:

  • Access / Search, Insertion, Deletion: Average Case:

    • Why? A good hash function computes the index for a given key in constant time. This allows you to jump directly to the correct location in the underlying array to find, add, or remove the key-value pair. This is why hash maps are fundamental to high-performance systems, from in-memory caches like Redis to the internal workings of databases and routing tables.
  • Access / Search, Insertion, Deletion: Worst Case:

    • Why? The ideal performance depends on the hash function distributing keys evenly across the array. A collision occurs when two different keys map to the same index. Simple collision resolution strategies, like storing colliding elements in a list at that index (called "chaining"), mean you might have to search through this list. In the worst-case scenario, all keys collide into the same bucket, degrading performance to a linear search: . Modern hash map implementations in languages like Rust, Go, and Python are highly optimized to make this worst-case scenario extremely rare in practice.

For a more detailed look at how this works, the following reading is excellent.

Data Structures in JavaScript: Arrays, HashMaps, and Lists

This article provides a deeper, implementation-oriented explanation of hash maps, including collisions and resizing (rehashing).

Read the 'HashMaps' section. Focus on the concepts of a hash function, collisions, and how resizing (rehash) helps maintain amortized O(1) performance. The final summary table, 'HashMap Time Complexities', is a key takeaway.


3. Linked Lists

Unlike arrays, linked lists store elements in non-contiguous memory. Each element, or node, contains its data and one or more pointers (references) to other nodes. This fundamental difference in structure leads to a completely different performance profile.

This last video segment will introduce the concept.

Top 8 Data Structures for Coding Interviews

Now, let's look at linked lists and see how their node-and-pointer structure contrasts with arrays.

Watch from 02:03 to 04:11. Note the trade-off: what do you gain by giving up contiguous memory, and what do you lose?

The video touches on a key point: changing pointers is cheap. Let's analyze the complexities, making a distinction between a singly linked list (each node points to next) and a doubly linked list (each node points to next and previous).

  • Access (by index) / Search (by value):

    • Why? There is no way to jump to the i-th element. You must start at the head (the first node) and traverse the list one node at a time by following the next pointers until you reach the desired position or find the value.
  • Insertion / Deletion (at the beginning/head):

    • Why? This only requires updating a few pointers: creating a new node and making it the new head, or making the head's next node the new head. No element shifting is needed.
  • Insertion / Deletion (at the end/tail):

    • Singly Linked List: to delete, to insert (if you don't hold a tail pointer). To append, you must traverse to the last node. To delete the last node, you must traverse to the second-to-last node to update its next pointer.
    • Doubly Linked List (with a tail pointer): . A direct pointer to the last element allows for constant-time additions. The previous pointer allows you to find the second-to-last element in constant time for deletion. This is a significant advantage.
  • Insertion / Deletion (in the middle):

    • Why? The overall operation is dominated by the time it takes to find the node to insert after or delete, which is an traversal. Once found, the pointer manipulation itself is an operation.

The following resource provides excellent tables that summarize these differences.

Data Structures in JavaScript: Arrays, HashMaps, and Lists

This reading explores linked lists in more detail, clearly distinguishing between singly and doubly linked list performance.

Read the sections 'Singly Linked Lists' and 'Doubly Linked Lists'. Pay close attention to the time complexity tables for each. Notice how adding a previous pointer and a last reference in a doubly linked list improves tail operations from O(n) to O(1).


4. Summary and Cheat Sheets

Understanding the trade-offs between these three data structures is critical.

  • Need fast access by index? Use an array.
  • Need fast insertion/deletion at the ends? Use a doubly linked list.
  • Need fast access by a unique key? Use a hash table.

This image provides a direct visual comparison of Array Lists vs. Linked Lists.

Time Complexity Comparison: Linked List vs. Array List Operations
A comparison of common operations for Array Lists and Linked Lists. Green (O(1)) signifies a strength, while red (O(n)) signifies a weakness.

And for a comprehensive overview of all major data structures and algorithms, this cheat sheet is an invaluable resource to bookmark.

Big-O Cheat Sheet: Data Structures and Algorithms Complexity
A comprehensive poster summarizing the time and space complexities for common data structure operations and sorting algorithms. A perfect reference for interview preparation.

Conclusion

You have now analyzed the performance of the most essential data structures in computer science. Internalizing these complexities is a major step toward thinking like an algorithm designer. When you face a problem, your first thought should often be, "What data structure is best suited for the operations I need to perform?"

Key Takeaways:

  • Arrays: access by index. for search, insertion, and deletion in the middle due to element shifting.
  • Hash Tables: average time for search, insertion, and deletion. They are the champion of fast lookups. Their worst case is , but this is rare in practice.
  • Linked Lists: insertion and deletion at the head (and tail for doubly linked lists). for access and search because they require traversal.

Next Steps

With this foundational module complete, you are now equipped with the tools for complexity analysis. In our next lesson, we will move to the "Arrays and Strings" module. You will start solving common interview problems by applying patterns like the two-pointer technique, putting your new knowledge of array complexities into direct, practical use.