Skip to content

Commit baf675a

Browse files
authored
Merge pull request #1055 from steam-bell-92/main
feat(python): Added Data Structures in Python #1038
2 parents 4f5a938 + 198b311 commit baf675a

File tree

5 files changed

+380
-2
lines changed

5 files changed

+380
-2
lines changed

docs/python/python-array.md renamed to docs/python/Data_Structures/python-array.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ print(c) # array('i', [1, 2, 3, 4, 5])
239239

240240
3. **Array Reversal**
241241

242-
**Q3:**** Write a Python program to reverse an array without using slicing or the reverse() method.
242+
**Q3:** Write a Python program to reverse an array without using slicing or the reverse() method.
243243

244244

245245
4. **Insertion Operation**
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Linked List in Python
2+
3+
A **Linked List** is a fundamental **linear data structure** where elements are **not** stored at contiguous memory locations (unlike arrays or Python lists). Instead, the elements, called **Nodes**, are linked together using **pointers** or **references**.
4+
5+
This structure allows for highly efficient **insertions** and **deletions** compared to arrays, where these operations can be slow.
6+
7+
---
8+
9+
## Structure of a Linked List
10+
11+
The linked list is built upon two core concepts:
12+
13+
1. **Node:** The basic building block, which contains:
14+
* **Data:** The value stored.
15+
* **Next Pointer (`next`):** The reference to the next node in the sequence.
16+
2. **Head:** A pointer to the very first node in the list. It is the entry point for all operations. The last node's `next` pointer always points to **`None`**.
17+
18+
### Real-Life Analogy
19+
20+
Think of a **treasure hunt or a chain of clues**. Each clue (**Node**) holds the information (**Data**) and a direction to the next clue (**Next Pointer**). You must follow the chain from the beginning (**Head**) to find the end.
21+
22+
---
23+
24+
## Python Implementation: The Node Class
25+
26+
Since Python doesn't have a built-in linked list type, we define its structure using classes.
27+
28+
The `Node` class defines the element structure.
29+
30+
```python
31+
class Node:
32+
"""Represents a single element in the linked list."""
33+
def __init__(self, data):
34+
# Store the data
35+
self.data = data
36+
# Initialize the pointer to the next node
37+
self.next = None
38+
```
39+
40+
## Python Implementation: The LinkedList Class
41+
42+
The `LinkedList` class manages the list, primarily by keeping track of the `head`.
43+
44+
```python
45+
class LinkedList:
46+
"""Manages the linked list and its head pointer."""
47+
def __init__(self):
48+
# Initialize the list's entry point
49+
self.head = None
50+
```
51+
52+
### Traversal: Printing the List
53+
54+
To traverse, we start at the head and loop until the current node becomes None, updating the current node with its next pointer in each iteration.
55+
56+
```python
57+
class LinkedList:
58+
# __init__ and Node class definition here
59+
60+
def print_list(self):
61+
current_node = self.head
62+
print("List:", end=" ")
63+
while current_node is not None:
64+
print(current_node.data, end=" -> ")
65+
current_node = current_node.next
66+
print("None")
67+
```
68+
***Example***
69+
70+
```python
71+
my_list = LinkedList()
72+
my_list.head = Node(10)
73+
second = Node(20)
74+
third = Node(30)
75+
76+
# Linking the nodes: 10 -> 20 -> 30 -> None
77+
my_list.head.next = second
78+
second.next = third
79+
my_list.print_list()
80+
```
81+
82+
**Output:**
83+
84+
```
85+
List: 10 -> 20 -> 30 -> None
86+
```
87+
88+
### Insertion Operations
89+
90+
#### 1. Insertion at the Beginning (Prepend)
91+
92+
It only requires updating the head pointer.
93+
94+
```python
95+
def insert_at_beginning(self, new_data):
96+
# Create a new node
97+
new_node = Node(new_data)
98+
99+
# Make the new node's next pointer point to the current head
100+
new_node.next = self.head
101+
102+
# Update the list's head to point to the new node
103+
self.head = new_node
104+
```
105+
106+
***Example***
107+
108+
```python
109+
my_list.insert_at_beginning(5)
110+
my_list.print_list()
111+
```
112+
113+
**Output:**
114+
115+
```
116+
List: 5-> 10 -> 20 -> 30 -> None
117+
```
118+
119+
#### 2. Insertion After a Node
120+
121+
```python
122+
def insert_after(self, prev_node, new_data):
123+
if prev_node is None:
124+
print("Previous node cannot be None.")
125+
return
126+
127+
# Create the new node
128+
new_node = Node(new_data)
129+
130+
# Set new node's next to the previous node's next
131+
new_node.next = prev_node.next
132+
133+
# Set the previous node's next to the new node
134+
prev_node.next = new_node
135+
```
136+
137+
***Example***
138+
139+
```python
140+
my_list.insert_after(my_list.head, 15)
141+
my_list.print_list()
142+
```
143+
144+
**Output:**
145+
146+
```
147+
List: 5-> 15-> 10 -> 20 -> 30 -> None
148+
```
149+
150+
### Deletion Operation
151+
152+
Deleting the Head
153+
154+
```python
155+
def delete_head(self):
156+
if self.head is None:
157+
return
158+
159+
# Move the head to the next node, which effectively "deletes" the old head
160+
self.head = self.head.next
161+
```
162+
163+
***Example***
164+
165+
```python
166+
my_list.delete_head() # Deletes head (5)
167+
my_list.print_list()
168+
```
169+
170+
**Output:**
171+
172+
```
173+
List: 15-> 10 -> 20 -> 30 -> None
174+
```
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Queue in Python
2+
3+
A **Queue** is a linear data structure that follows a specific order for all operations. This order is based on the **First-In, First-Out (FIFO)** principle.
4+
5+
Imagine a ticket line: the first person to join the line is the first person to be served and leave.
6+
7+
---
8+
9+
## Queue Operations
10+
11+
Queues have two distinct ends where operations occur: the **Rear** (for insertion) and the **Front** (for removal).
12+
13+
| Operation | Description | Analogy |
14+
| :--- | :--- | :--- |
15+
| **Enqueue** | Adds an element to the **Rear**. | Joining the back of the line. |
16+
| **Dequeue** | Removes an element from the **Front**. | Leaving the front of the line. |
17+
| **Peek** | Returns the front element without removing it. | Looking at the person next in line. |
18+
19+
---
20+
21+
## Queue Operations Using Python List
22+
23+
In the simplest Python implementation, we use a built-in **`list`** as the underlying storage. For a true FIFO queue:
24+
25+
* **Enqueue** is performed using `list.append()` (at the rear/end).
26+
* **Dequeue** is performed using `list.pop(0)` (from the front/start).
27+
* **Peek** is performed using list indexing `list[0]` (at the fromt/start).
28+
29+
### The Queue Class
30+
31+
```python
32+
class Queue:
33+
"""Implements a Queue using the FIFO principle with a Python list."""
34+
def __init__(self):
35+
# The storage container for the queue elements
36+
self._items = []
37+
38+
def is_empty(self):
39+
"""Check if the queue is empty."""
40+
return not self._items
41+
42+
def enqueue(self, data):
43+
"""Adds an element to the rear of the queue (list.append()). (O(1))"""
44+
self._items.append(data)
45+
print(f"Enqueued: {data}")
46+
47+
def dequeue(self):
48+
"""Removes and returns the front element (list.pop(0)). (O(N))"""
49+
if self.is_empty():
50+
raise IndexError("Error: Cannot dequeue from an empty queue.")
51+
52+
# pop(0) removes the first item (the front of the queue)
53+
return self._items.pop(0)
54+
55+
def peek(self):
56+
"""Returns the front element without removing it."""
57+
if self.is_empty():
58+
raise IndexError("Error: Cannot peek at an empty queue.")
59+
60+
# Access the first item (0 index)
61+
return self._items[0]
62+
```
63+
64+
***Example***
65+
66+
```python
67+
my_queue = Queue()
68+
my_queue.enqueue("Task 1")
69+
my_queue.enqueue("Task 2")
70+
my_queue.enqueue("Task 3")
71+
72+
print("\nFront element (Peek):", my_queue.peek())
73+
74+
served_item = my_queue.dequeue()
75+
print("Dequeued element:", served_item)
76+
77+
print("Queue Status:", my_queue._items)
78+
79+
my_queue.dequeue()
80+
my_queue.dequeue()
81+
82+
# my_queue.dequeue() # Uncommenting this line will raise an IndexError
83+
```
84+
85+
**Output**:
86+
87+
```
88+
Enqueued: Task 1
89+
Enqueued: Task 2
90+
Enqueued: Task 3
91+
92+
Front element (Peek): Task 1
93+
Dequeued element: Task 1
94+
Queue Status: ['Task 2', 'Task 3']
95+
```
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Stack in Python
2+
3+
A **Stack** is a linear data structure that follows a specific order for all operations. This order is based on the **Last-In, First-Out (LIFO)** principle.
4+
5+
Imagine a stack of plates: you can only add a new plate to the top, and you can only remove the plate that is currently on the top.
6+
7+
---
8+
9+
## Stack Operations
10+
11+
A stack has three primary operations, all of which occur at the **Top** of the stack:
12+
13+
1. `Push`: **Adds** an element to the top of the stack.
14+
2. `Pop`: **Removes** and returns the element from the top of the stack.
15+
3. `Peek`: **Returns** the element at the top.
16+
17+
| Operation | Description | Analogy |
18+
| :--- | :--- | :--- |
19+
| **Push** | Add element to the **Top** | Placing a plate on top of the stack |
20+
| **Pop** | Remove element from the **Top** | Taking the top plate off the stack |
21+
| **Peek** | View the element at the **Top** | Looking at the top plate |
22+
23+
---
24+
25+
## Stack Operations Using Python
26+
ListIn the simplest Python implementation, we use a built-in list as the underlying storage. For a true LIFO stack, all primary operations are performed at the end of the list:
27+
28+
* **Push** is performed using `list.append()`.
29+
* **Pop** is performed using `list.pop()`.
30+
* **Peek** is performed using list indexing `list[-1]`.
31+
32+
---
33+
## The Stack Class
34+
35+
```python
36+
class Stack:
37+
"""Implements a Stack using the LIFO principle with a Python list."""
38+
def __init__(self):
39+
# The storage container for the stack elements
40+
self._items = []
41+
42+
def is_empty(self):
43+
"""Check if the stack is empty."""
44+
return not self._items
45+
46+
def push(self, data):
47+
"""Adds an element to the top of the stack (list.append())."""
48+
self._items.append(data)
49+
print(f"Pushed: {data}")
50+
51+
def pop(self):
52+
"""Removes and returns the top element (list.pop())."""
53+
if self.is_empty():
54+
raise IndexError("Error: Cannot pop from an empty stack.")
55+
56+
# Pop removes the last item (the top of the stack)
57+
return self._items.pop()
58+
59+
def peek(self):
60+
"""Returns the top element without removing it."""
61+
if self.is_empty():
62+
raise IndexError("Error: Cannot peek at an empty stack.")
63+
64+
# Access the last item (-1 index)
65+
return self._items[-1]
66+
```
67+
68+
***Example***
69+
70+
```python
71+
my_stack = Stack()
72+
my_stack.push("A")
73+
my_stack.push("B")
74+
my_stack.push("C")
75+
76+
print("\nTop element (Peek):", my_stack.peek())
77+
78+
removed_item = my_stack.pop()
79+
print("Popped element:", removed_item)
80+
81+
print("Current size:", len(my_stack._items))
82+
83+
my_stack.pop()
84+
my_stack.pop()
85+
86+
# my_stack.pop() # Uncommenting this line will raise an IndexError
87+
```
88+
89+
**Output**:
90+
91+
```
92+
Pushed: A
93+
Pushed: B
94+
Pushed: C
95+
96+
Top element (Peek): C
97+
Popped element: C
98+
Current size: 2
99+
```

0 commit comments

Comments
 (0)