Skip to content

Commit

Permalink
feat: added notes for heap
Browse files Browse the repository at this point in the history
  • Loading branch information
Rishabh672003 committed Sep 7, 2024
1 parent e17145d commit 56ab723
Showing 1 changed file with 174 additions and 0 deletions.
174 changes: 174 additions & 0 deletions Data-Structures/Trees/11.heap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
### Heap (Priority Queue)

A **heap** is a specialized tree-based data structure used primarily for efficient access to the maximum or minimum element. It is often implemented as a binary tree and can be used to maintain a priority queue, which allows for quick retrieval of the highest or lowest priority element.

---

#### Key Concepts

1. **Purpose**

- Heaps are used to get the minimum or maximum element from a collection in constant time, O(1).
- Insertion and deletion of elements are performed in logarithmic time, O(log n).

2. **Types of Heaps**
- **Min-Heap**: The root node is the smallest element, and every parent node is less than or equal to its children.
- **Max-Heap**: The root node is the largest element, and every parent node is greater than or equal to its children.

---

### Heap Properties

1. **Structure Property**

- The heap is a **complete binary tree**, meaning all levels are completely filled except possibly the last level, which must be filled from left to right.
- This ensures efficient use of space and enables fast access to elements.

2. **Order Property**
- In a **min-heap**, the value of the root node is smaller than both its children.
- In a **max-heap**, the value of the root node is larger than both its children.

#### Example of a Max-Heap:

```
14
/ \
19 16
/ \
21 26
```

### Differences from Binary Search Tree (BST)

- **Duplicates**: Heaps can contain duplicate values, whereas BSTs do not allow duplicate nodes.
- **Ordering**: In a heap, only the parent-child relationship matters for ordering. In a BST, all nodes in the left subtree are smaller than the root, and all nodes in the right subtree are larger.

---

### Array Representation of Heaps

A heap is generally implemented using an array. Given the complete binary tree property, the elements can be stored in an array such that:

- The **root** is at index 1 (index 0 is typically unused).
- For any node at index i:
- The **left child** is at index 2i.
- The **right child** is at index 2i + 1.
- The **parent** is at index i // 2.

#### Example Array Representation:

For the tree:

```
14
/ \
19 16
/ \
21 26
```

The array representation would be:

```
0 1 2 3 4 5
-----------------------------
| | 14 | 19 | 16 | 21 | 26 |
-----------------------------
```

---

### Heap Operations

1. **Insert Operation (`push`)**

- Insert a new element at the end of the array.
- **Percolate up** (or bubble up) the element to restore the heap property by comparing the element with its parent and swapping if necessary.
- Time complexity: O(\log n), where n is the number of elements.

2. **Delete Operation (`pop`)**
- Remove the root element (either the minimum or maximum).
- Replace the root with the last element in the heap.
- **Percolate down** (or bubble down) the root element to restore the heap property by comparing with its children and swapping if necessary.
- Time complexity: O(log n).

---

### Python Implementation of Heap

```python
class Heap:
def __init__(self):
self.heap = [0] # Initialize heap with a dummy element at index 0

def push(self, val):
# Insert new value at the end
self.heap.append(val)
i = len(self.heap) - 1

# Percolate up
while i > 1 and self.heap[i] < self.heap[i // 2]:
self.heap[i], self.heap[i // 2] = self.heap[i // 2], self.heap[i]
i //= 2 # Move to the parent node

def pop(self):
if len(self.heap) == 1:
return None # Heap is empty

if len(self.heap) == 2:
return self.heap.pop() # Only one element

res = self.heap[1] # The root value (min or max)
self.heap[1] = self.heap.pop() # Replace root with the last element
i = 1

# Percolate down
while 2 * i < len(self.heap):
# Check if right child exists and is smaller than the left child
if 2 * i + 1 < len(self.heap) and self.heap[2 * i + 1] < self.heap[2 * i]:
# Swap with right child if needed
if self.heap[i] > self.heap[2 * i + 1]:
self.heap[i], self.heap[2 * i + 1] = self.heap[2 * i + 1], self.heap[i]
i = 2 * i + 1
else:
break
# Otherwise, swap with the left child
elif self.heap[i] > self.heap[2 * i]:
self.heap[i], self.heap[2 * i] = self.heap[2 * i], self.heap[i]
i = 2 * i
else:
break

return res

def heapify(self, arr):
# Convert any array into a heap
arr = [0] + arr # Add dummy element at index 0
self.heap = arr
cur = (len(self.heap) - 1) // 2 # Start with the first non-leaf node

while cur > 0:
# Percolate down
i = cur
while 2 * i < len(self.heap):
if 2 * i + 1 < len(self.heap) and self.heap[2 * i + 1] < self.heap[2 * i]:
if self.heap[i] > self.heap[2 * i + 1]:
self.heap[i], self.heap[2 * i + 1] = self.heap[2 * i + 1], self.heap[i]
i = 2 * i + 1
else:
break
elif self.heap[i] > self.heap[2 * i]:
self.heap[i], self.heap[2 * i] = self.heap[2 * i], self.heap[i]
i = 2 * i
else:
break
cur -= 1
```

---

### Time Complexities

- **Insertion (`push`)**: O(log n)
- **Deletion (`pop`)**: O(log n)
- **Heapify**: O(n) – It can convert any array into a heap efficiently.

0 comments on commit 56ab723

Please sign in to comment.