In the previous chapter, we discussed what DSA is and got familiar with algorithmic notations like Big O, Little o, and others. Now, let’s dive into data structures — one of the core pillars of DSA.
What Are Data Structures?
A data structure is a way of organizing data in a computer so that it can be used efficiently. Imagine you have a closet, and you want to keep all your clothes in an organized way — like folding shirts on one shelf and hanging jackets on another. Data structures work the same way for organizing information in a program.
Each data structure has a specific purpose and is better suited for particular kinds of tasks. For example, some data structures are great for storing data in order, while others are perfect for quickly finding a specific piece of information.
Types of Data Structures
Data structures can be classified into two major types:
- Primitive Data Structures: These are the basic types provided by a programming language. For example, integers, floats, characters, and pointers.
- Non-Primitive Data Structures: These are more complex and involve multiple values. They can be further classified as:
- Linear Data Structures: Data is arranged in a sequence (like a line). Common linear data structures include Arrays, Linked Lists, Stacks, and Queues.
- Non-Linear Data Structures: Data isn’t arranged in a sequence. Common examples include Trees and Graphs.
Let’s start with linear data structures and go through each one in detail.
Arrays
An Array is the simplest and most commonly used data structure. It is a collection of elements (values or variables), each identified by an index or a key. Arrays are usually used to store multiple items of the same type together.
Key Points About Arrays:
- Fixed Size: When you create an array, you need to specify how many elements it can hold. This is important because an array’s size cannot be changed later (in most languages).
- Indexed Access: You can access elements by their position or index in the array. For example,
array[0]
refers to the first element. - Efficient Reading: It’s quick and easy to access any element if you know its index.
Example:
int arr[5] = {10, 20, 30, 40, 50};
In the example above, arr[0]
is 10, arr[1]
is 20, and so on.
Pros:
- Arrays are very fast for accessing and modifying elements when you know their index.
Cons:
- Arrays are not dynamic. If you run out of space or need to add elements, you’re stuck.
Linked Lists
A Linked List is a linear data structure where elements (called nodes) are linked using pointers. Unlike arrays, Linked Lists can grow or shrink in size dynamically, which makes them more flexible.
Each node contains two parts:
- Data: The actual value.
- Pointer: A reference to the next node in the list.
Types of Linked Lists:
- Singly Linked List: Each node points to the next one in the sequence.
- Doubly Linked List: Each node has two pointers, one pointing to the next node and another pointing to the previous one.
- Circular Linked List: The last node points back to the first node, forming a circle.
Example:
struct Node {
int data;
struct Node* next;
};
Here, each Node
has an integer (data
) and a pointer (next
) that points to the next node.
Pros:
- Dynamic Size: You can keep adding nodes to a Linked List without worrying about size limitations.
Cons:
- Slower Access: You cannot access an element directly like you can in an array. To access the nth element, you need to traverse the list from the beginning.
Stacks
A Stack is a collection of elements where you can only add or remove elements from one end, called the top. It follows the LIFO (Last In, First Out) principle. Imagine a stack of books; the last book you place on top is the first one you’ll take out.
Key Operations in Stacks:
- Push: Add an element to the top.
- Pop: Remove the element from the top.
- Peek/Top: Look at the element on top without removing it.
Example:
stack.push(10); // Adds 10 to the stack
stack.pop(); // Removes the top element from the stack
Pros:
- Stacks are very useful in scenarios where you need to reverse things (like reversing a string).
Cons:
- Since you can only add or remove from the top, stacks are not good for accessing elements in the middle.
Queues
A Queue is another linear data structure but operates under the FIFO (First In, First Out) principle. Imagine standing in a queue at a ticket counter — the first person to stand in line is the first one to be served.
Key Operations in Queues:
- Enqueue: Add an element to the back of the queue.
- Dequeue: Remove an element from the front of the queue.
- Front: View the element at the front without removing it.
Example:
queue.enqueue(20); // Adds 20 to the back of the queue
queue.dequeue(); // Removes the front element
Pros:
- Queues are great for managing tasks in an ordered fashion, like processing tasks in a printer.
Cons:
- You can’t access elements in the middle. You have to follow the FIFO order.
Choosing the Right Data Structure
When choosing a data structure, always ask yourself:
- How do I need to access the data?
- Do I need to add or remove elements frequently?
- Do I need to search for elements quickly?
Each data structure has its strengths and weaknesses, so choosing the right one depends on the problem you’re trying to solve.
Wrapping Up
We’ve explored some of the key linear data structures: Arrays, Linked Lists, Stacks, and Queues. These structures are foundational and are used frequently in many real-world scenarios. Understanding how they work and when to use them will make your programming much more efficient.
In the next chapter, we’ll dive into non-linear data structures, such as Trees and Graphs, and see how they can be used to solve more complex problems.
Stay tuned!