Data Structures and Operations

Because they offer methodical ways to arrange and store data in computer memory so that information may be retrieved and altered effectively, data structures are essential to computer science and engineering. An algorithm is a methodical process created to complete a task in a limited amount of time. What can be calculated at hand is greatly impacted by the combination of data organisation and suitable algorithms. You may construct data structures in any programming language and determine which ones are most suited for a particular programming request if you know how to do so.
Insertion (adding new data), deletion (removing data), and searching (finding specific data) are common operations that are essential to practically all data structures. These functions are essential to the use of data structures in real-world programming.
Here is an examination of typical data structures and how they work, supported where necessary by conceptual Python examples:
Arrays and Python Lists
One of the simplest data structures is an array. An “array” of values in Python is frequently represented naturally using the built-in list class. Even though Python provides more sophisticated constructs, the book concentrates on comprehending the fundamental actions on primitive kinds.
Key Operations and their Typical Performance:
Accessing Elements: Accessing data structure elements is essential, however the mechanism utilised depends on the structure.
Slicing, searching, and changing list components are possible.
Appending Elements: In data structures, appending elements means adding them at the end. The adding mechanism depends on the data structure. Python append() function is often used to add an element to lists.The process of appending elements to a list is often an O(1) amortised operation (data.append(value)).
Inserting Elements: Adding an element at a certain index k may take longer since it may need to move already-existing elements. O(n – k + 1) is its running time, where n is the list’s length.
Removing Elements:
- Data.pop(): This O(1) amortised operation removes and returns the final item.
- Data.pop(k): Usually O(n – k), this function returns and removes the item at index k.
- Data.remove(value): This O(n) operation eliminates the first instance of a given value since it necessitates first finding the value.
- Del data[k]:The item at index k is deleted using del data[k].
Searching Elements: Binary or linear search can be used to find an element in an array. Searching elements is a basic data structure action that finds a given item or key. The method and data format greatly affect search efficiency.
- In a linear search, every element is checked one after the other.
- Sorted arrays are more suited for binary search.
Reversing Elements: Reversing a sequence means making the first element the last, the second the second-to-last, etc. Several algorithms can do this basic task. Recursion can be used to reverse a sequence by swapping the first and last components and then reversing the rest.
Sorting: Rearranging elements in a collection into a nondecreasing order or creating a sorted replica of the sequence is a fundamental computing problem. The main purpose is to organise, manage, and store data for easy access and changes. Sorting optimises data searches, like the binary search algorithm, and makes data more understandable. Many advanced algorithms use sorting subroutines.
Other operations: Concatenation, multiplication/repetition, updating elements, and slicing are further operations discussed for arrays. Additionally, two-dimensional arrays are covered.
Conceptual Python Example for list operations
Example:
my_list = [5, 27, 37, 38]
# Accessing
print(f"Original list: {my_list}")
print(f"Element at index 1: {my_list[1]}")
# Appending
my_list.append(50)
print(f"After append(50): {my_list}")
# Inserting
my_list.insert(1, 15)
print(f"After insert(1, 15): {my_list}")
# Removing
my_list.pop() # Removes last element (50)
print(f"After pop(): {my_list}")
# To avoid ValueError: '20' is not in list, we will remove an existing element,
# Or, if the intention was to show the error, we'd keep it and explain the traceback.
# For a "correct program with output", let's assume we want valid operations.
my_list.remove(27) # Removes the value 27 (which now exists in the list)
print(f"After remove(27): {my_list}")
# 'del my_list' deletes the entire list variable.
# If you try to print it afterwards, you'll get a NameError.
# If the intention was to delete an element at an index, use del my_list[index].
# If the intention was to delete the entire list, then you cannot print it immediately after.
# Let's delete an element at a specific index to show its effect, or remove the 'del my_list' if it's meant to be gone.
# Assuming you wanted to delete an element at index 0 (which is now 5)
del my_list[0]
print(f"After del my_list[0]: {my_list}")
# If you actually meant to delete the entire list variable and thus no further operations on it:
# del my_list
# print(f"After del my_list: {my_list}")
Output:
Original list: [5, 27, 37, 38]
Element at index 1: 27
After append(50): [5, 27, 37, 38, 50]
After insert(1, 15): [5, 15, 27, 37, 38, 50]
After pop(): [5, 15, 27, 37, 38]
After remove(27): [5, 15, 37, 38]
After del my_list[0]: [15, 37, 38]
Linked Lists
Instead of storing data in consecutive memory regions, linked lists store it as nodes, each of which has an item and a reference (or pointer) to the one after it. In programming, they are a basic data structure.
Types of Linked Lists:
Singly Linked List: A singly linked list is a basic data structure with nodes. Each node in a singly linked list has a data element (value) and a reference (pointer) to the next node. Just one link permits forward-only list navigation. The series ends with the last node pointing to None or null. Linked lists dynamically resize without copy penalties and store elements at any memory address, unlike arrays.
Doubly Linked List: A doubly linked list is a linked data structure with a sequence of nodes, each with a data element and two link fields (references or pointers) pointing to the previous (prev) and next nodes. Better than forward-only singly linked lists, this dual-link structure permits backward and forward traversal.
Circular Linked List: Circular linked lists loop continuously from the last node’s next pointer to the first node. The tail node’s next reference points to the list’s head, not a null value as in a singly linked list. A circular doubly linked list can have a start member with a previous link to the last element. Circular linked lists are ideal for modelling cyclic data sets with no beginning or conclusion due to their structure.
Key Operations:
- Insertion is the process of adding a new node, which can be done at the start, finish, or a particular location.
- Deletion: Taking a node away, either from the start, finish, or a particular location.
- Searching: Moving through the list to find a particular node.
- Traversal: Going ahead or backward (for doubly linked lists) to all nodes in a predetermined order.
Conceptual Python Example (Node and Singly Linked List Structure)
Example:
Node Class
class Node:
def __init__(self, data):
self.data = data
self.next = None
# LinkedList Class
class LinkedList:
def __init__(self):
self.head = None
self.tail = None
# Insertion at Beginning
def insert_at_beginning(self, data):
new_node = Node(data)
if self.head is None:
# If the list is empty, the new node is both head and tail
self.head = new_node
self.tail = new_node
else:
# New node's next points to the current head
new_node.next = self.head
# New node becomes the new head
self.head = new_node
print(f"Inserted {data} at beginning.")
# Search
def search(self, key):
current = self.head
position = 0
while current:
if current.data == key:
print(f"Found {key} at position {position}.")
return True
current = current.next
position += 1
print(f"{key} not found.")
return False
# Display
def display(self):
elements = []
current = self.head
while current:
elements.append(current.data)
current = current.next
print(f"List: {elements}")
# Usage:
my_linked_list = LinkedList()
my_linked_list.insert_at_beginning(10)
my_linked_list.insert_at_beginning(5)
my_linked_list.display()
my_linked_list.search(10)
my_linked_list.search(20)
Output:
Inserted 10 at beginning.
Inserted 5 at beginning.
List: [5, 10]
Found 10 at position 1.
20 not found.
(This code demonstrates the conceptual implementations of insert_at_beginning and search methods, which are described as essential operations, as well as the structure of a Node and LinkedList class.)
Stacks
The Last-In, First-Out (LIFO) principle governs the linear data structure known as a stack. Accordingly, the final ingredient added is the first to be eliminated.
Key Operations:
- An item is added to the top of the stack by pushing (or queuing).
- Pop (or Dequeue): Takes the item from the top of the stack and puts it back.
- Peek (or Top): Brings the top object to the surface without taking it off.
Linked lists or arrays/Python lists can be used to create stacks. Applications include matching HTML elements and brackets, as well as reversing data.
Queues
The First-in, First-out (FIFO) principle governs queues, which are critical data structures. This is like a queue of people waiting, where newcomers join the back and service is supplied from the front. As an Abstract Data Type (ADT), queues support two operations: enqueue, which adds an element to the tail, and dequeue, which removes and returns it from the head. First (or peek) references the front element without removing it, isEmpty checks if the queue is empty, and isFull checks capacity.
Key Operations:
- Enqueue: Positions an item at the rear, or back, of the queue.
- Dequeue: Pulls the item out of the front of the queue and takes it out.
- The item at the front of the queue is retrieved using the peek (or front) method without being taken out.
Arrays/Python lists or linked lists can also be used to implement queues. Insertion and removal from the front and back are possible using a variant known as a Deque (Double-Ended Queue). Applications include CPU process scheduling and tree traversal with Breadth-First Search (BFS).
Trees
In contrast to linear data structures like arrays or linked lists, trees are nonlinear data structures that enable significantly quicker algorithm implementations. They are utilised in databases, file systems, and graphical user interfaces and offer a logical method of organising data.
One basic kind of tree is a binary tree. The Binary Search Tree (BST) is a key type in which all of a node A’s left successors have key values less than A, while all of its right descendants have key values larger than or equal to A.
Key Operations on Trees:
Insertion: Insertion adds a node or element to a data structure. Insertion efficiency and manner rely on the data structure.
Inserting an element in linked lists requires reassigning pointers from existing nodes to the new one. A single linked list can easily add an element to the head by establishing a new node, setting its next link to the current head, and changing the list’s head.
Deletion: Deletion is a fundamental data structure operation, but its complexity varies. Maintaining data structure integrity and characteristics requires removing an element or node.
Finding Nodes: A data structure’s node can be found by its key value, a data field used for searching and other operations. This procedure varies greatly by data structure. Finding a node in a Binary Search Tree (BST) uses the tree’s ordered structure to be efficient. Start searching at the root. Each current node compares the intended key value to its key.
Traversal: Going through every node in a certain order. Typical traversals consist of.
- Traversals that are pre-, in-, and post-order. Algebraic expressions can be parsed with the help of pre-order and post-order traversals.
- DFS (Depth-First Search) and BFS (Breadth-First Search).
- The following tree types are also mentioned: heaps, B-trees, B+ trees, and AVL trees (with rotations).
Graphs
Relationships between entities are depicted in graphs. They are made up of vertices, or nodes, and the edges that connect them.
Key Operations:
Graph Traversals: Systematic graph traversals examine all vertices and edges to study a graph. A traversal must visit all vertices and edges in linear time (O(n+m)) to be efficient. These techniques are essential for graph research, especially reachability, which includes identifying pathways between vertices.
- Depth-First Search (DFS): Investigates each branch as far as it can before turning around.
- Before going on to nodes at the next depth level, the Breadth-First Search (BFS) method investigates every neighbour node at the current depth.
Minimum Spanning Tree Algorithms: Prim’s and Kruskal’s algorithms find a subset of a connected, edge-weighted undirected graph’s edges that connect all its vertices with the least edge weight and no cycles. Graphs can be represented using adjacency lists, matrices, edge lists, and map structures.
Hash Tables
Key-value pairs are stored in hash tables so that values can be efficiently retrieved using their keys. The dict class in Python is an illustration of a map.
Key Operations:
- Adding a new key-value pair is known as insertion
- Eliminating a key-value pair is known as deletion. This is implemented by Python’s del M[k].
- Searching: Locating a value linked to a specific key.
- Collision Handling: Techniques like distinct chaining and open addressing (linear probing, quadratic probing, and double hashing) are crucial for effective hash table performance.
- The number of objects in the map is returned by len(M).
- Iteration across the map’s keys is possible using iter(M). Additionally supported by Python dictionaries are looping, membership testing, adding, editing, accessing, and generating.
Sets
An unordered grouping of distinct elements is called a set. A MutableSet abstract base class is offered by Python.A set is an unordered, duplicate-free collection. This implies that each set value is unique. The set class in Python illustrates this math notion. While sets can be added or erased, Python introduces frozenset, an immutable set type.
Key Operations:
- The function union (S | T or S |= T) returns or modifies a set that contains every element in either set S, set T, or both.
- The function intersection (S & T or S &= T) returns or modifies a set that contains elements that are shared by S and T.
- A new set containing entries from S but not from T is returned by the difference (S – T) function.
- A new set containing entries that are exactly in one of S or T is returned by the Symmetric Difference (S ^ T) function.
Designing effective programs and determining if a data structure or algorithm will work effectively for a given use case require an understanding of these activities and the intricacies that go along with them.