This commit is contained in:
2024-12-07 21:07:38 +01:00
parent 2fded76a5c
commit a9676272f2
120 changed files with 15925 additions and 1 deletions

View File

@ -0,0 +1,77 @@
---
date: 02.09.2024
type: theoretical
---
$\mathcal{O}$
## Complexity
- Time Complexity: amount of time an algorithm takes to complete as a function of the size of its input.
- Space Complexity: amount of memory it uses as a function of the size of its input.
### Big O
- **Formal Definition:**
Big O notation, $\mathcal{O}(f(n))$, describes the upper bound of the time or space complexity of an algorithm in terms of the input size $n$. It characterizes the worst-case scenario of an algorithm's growth rate.
Formally, a function $T(n)$ is said to be $\mathcal{O}(f(n))$ if there exist positive constants $c$ and $n_0$ such that for all $n \geq n_0$:
$$
0 \leq T(n) \leq c \cdot f(n).
$$
This means that $T(n)$ will grow at most as fast as $f(n)$, up to a constant multiple $c$, for sufficiently large $n$.
![Big O cheatsheet](Big%20o.png)
### Informally:
Big-O notation provides an upper limit on:
- **Time**: How long an algorithm will take.
- **Space**: How much memory it will require.
It helps compare algorithms by focusing on their growth rate and ignoring constant factors.
### Other Notations for Growth Rates
1. **Big-O ($\mathcal{O}(f(n))$)**:
- Describes the **upper bound** (worst-case) of $T(n)$.
- Informally: $T(n)$ grows no faster than $f(n)$.
- Formal Definition:
$$
T(n) \text{ is } \mathcal{O}(f(n)) \iff \exists c > 0, n_0 > 0 \text{ such that } T(n) \leq c \cdot f(n) \text{ for all } n \geq n_0.
$$
2. **Big-Omega ($\Omega(f(n))$)**:
- Describes the **lower bound** (best-case) of $T(n)$.
- Informally: $T(n)$ grows at least as fast as $f(n)$.
- Formal Definition:
$$
T(n) \text{ is } \Omega(f(n)) \iff \exists c > 0, n_0 > 0 \text{ such that } T(n) \geq c \cdot f(n) \text{ for all } n \geq n_0.
$$
3. **Big-Theta ($\Theta(f(n))$)**:
- Describes the **exact bound** (tight bound) of $T(n)$.
- Informally: $T(n)$ grows exactly as fast as $f(n)$.
- Formal Definition:
$$
T(n) \text{ is } \Theta(f(n)) \iff T(n) \text{ is } \mathcal{O}(f(n)) \text{ and } T(n) \text{ is } \Omega(f(n)).
$$
4. **Little-O ($o(f(n))$)**:
- Describes a **loose upper bound** of $T(n)$.
- Informally: $T(n)$ grows strictly slower than $f(n)$.
- Formal Definition:
$$
T(n) \text{ is } o(f(n)) \iff \forall c > 0, \exists n_0 > 0 \text{ such that } T(n) < c \cdot f(n) \text{ for all } n \geq n_0.
$$
Think of growth rates as vehicles on a highway:
- $\mathcal{O}(f(n))$: $T(n)$ cannot go faster than $f(n)$.
- $\Omega(f(n))$: $T(n)$ cannot go slower than $f(n)$.
- $\Theta(f(n))$: $T(n)$ moves at the exact same speed as $f(n)$.
- $o(f(n))$: $T(n)$ moves slower than $f(n)$ and never catches up.

View File

@ -0,0 +1,41 @@
---
date: 09.09.2024
type: theoretical
---
Divide and Conquer is a powerful algorithmic paradigm used to solve complex problems by breaking them down into smaller, more manageable sub-problems.
## Steps in Divide and Conquer
1. **Divide:**
- The problem is divided into smaller subproblems that are similar to the original but smaller in size.
- This division continues until the subproblems become simple enough to be solved directly.
2. **Conquer:**
- Solve the smaller subproblems recursively. If the subproblems are small enough (base cases), solve them directly without further recursion.
3. **Combine:**
- Combine the solutions of the subproblems to get the solution to the original problem.
### Example: Merge Sort
**Merge Sort** is a classic example of the Divide and Conquer approach:
1. **Divide:**
- Split the unsorted array into two halves recursively until each subarray contains a single element.
2. **Conquer:**
- Merge two sorted halves by comparing elements from each half in sorted order.
3. **Combine:**
- Combine the sorted halves to form a single sorted array.
The time complexity of Merge Sort is $\mathcal{O}(n \log n)$ because the array is repeatedly divided in half ($\log n$ divisions) and each division requires a linear amount of work ($\mathcal{O}(n)$) to merge.
### Other Examples
- **Quick Sort:** Uses Divide and Conquer by selecting a "pivot" element and partitioning the array into elements less than and greater than the pivot.
- **Binary Search:** Recursively divides a sorted array in half to search for an element, reducing the search space by half each time.
- **Strassen's Matrix Multiplication:** An algorithm that reduces the time complexity of matrix multiplication from $\mathcal{O}(n^3)$ to approximately $\mathcal{O}(n^{2.81})$ by recursively breaking down matrices.

View File

@ -0,0 +1,139 @@
---
date: 11.09.2024
type: theoretical
---
**travelling salesperson**
## Definition
- Approach to design algorithms
- Optimal solution in polynomial time
- Usually used alongside [Divide and Conquer](Divide%20and%20Conquer.md)
- Used to better the time [complexity](Complexity.md), but usually worsens the space complexity
### How do we create a DP soluton?
- Characterize optimal solution
- Recursively define the value of an optimal solution
- Compute the value
- Construct an optimal solution **from the computed information**
## Problem discussion
### Calculating Fibonacci numbers
- Recursion is extremely bad -> PF was wrong
- Because the branches are too many
- We compute f(x) where f(x) has already been computed
- Memoization!
#### Approaches
- Linear time $\mathcal{O}(n)$ - **Bottom to top**
```pseudo
F[0] = 1
F[1] = 1
for i in {2, .., n} do
F[i] = F[i 1] + F[i 2]
end for
return F[n]
```
Compute the entire array and just return the result.
- Memoization $\mathcal{O}(n)$ - **Top to bottom**
```javascript
function fibMemo(index, cache) {
cache = cache || [];
if (cache[index]) return cache[index];
else {
if (index < 3) return 1;
else {
cache[index] = fibMemo(index - 1, cache) + fibMemo(index - 2, cache);
}
}
return cache[index];
}
```
### [Rod cutting](https://www.geeksforgeeks.org/problems/rod-cutting0840/1)
Given a rod of length N inches and an array of prices, price[]. price[i] denotes the value of a piece of length i. Determine the maximum value obtainable by cutting up the rod and selling the pieces.
#### Optimal solution
- Let the left part have a length $l$
- Then $R[n] = P[l] + R[n-l]$
- Where $P[l]$ is the price
- First cut of length should be the maximal one
### Knight Collecting Rewards
>Knight Collecting rewards
Input: Integers $n, m$, table of rewards of size $n \times m$
Question: What is the maximum reward that the knight can get for its journey.
#### Strategy
- We look at the bounds defined by the problem statement
- We know that the knight can get into the cell $(n-1, m-1)$ either from $(n-2,m-3)$ or $(n-3,m-2)$
- Hence, we have to know the most rewarding path to these two points from $(0,0)$
#### Approaches
- $\mathcal{O}(n*m)$
```
Reward(int n, int m, matrix A)
Create matrix R of size n × m with −∞ values
R[0, 0] = A[0, 0]
for i in {0, .., n 1} do
for j in {0, .., m 1} do
R[i, j] = A[i, j] + max R[i 1, j 2], R[i 2, j 1]
end for
end for
return R[n-1, m-1]
```
This doesn't tell us how we got there, but it does give us max rewards.
We can easily include path memory by making elements be a linked list or create a separate table blah blah.
### [Longest common non-contiguous sequence](https://www.geeksforgeeks.org/longest-common-subsequence-dp-4/)[^1]
> LCS
Input: Two sequences X = [x1, x2, . . . xm], Y = [y1, y2, . . . , Yn].
Question: Longest common subsequence
#### Strategy
![[LCS.png]]
Or this formula:
$$
c[i,j] = \begin{cases}
0, & \text{if } i=0 \text{ or } j=0\\
c[i-1,j-1]+1, & \text{if } i,j>0 \text{ and } x_i = y_i\\
max\{c[i,j-1], c[i-1], j\}, & \text{if } i,j>0 \text{ and } x_i \neq y_i\\
\end{cases}
$$
### [Dominating set](https://www.geeksforgeeks.org/dominant-set-of-a-graph/)[^2] in a path
>Input: A path $P$ with a specified positive cost for each vertex.
Output: Choose a subset of vertices $S$ with a minimum cost such that for each $v \in P$: either $v \in S$ or there is u such that $u \in S$ and $u, v$ are neighbors.
#### Strategy
- Identify some subproblems
- $A[i]$ cost of the cheapest set $S_i \subset P_i$ which dominates all vertices in $P_i$ <- not great
- $A[i,0]$ equals the cost of the cheapest $S_i$ which dominates $P_i$ and $v_i \notin S_i$
- $A[i,1]$ equals the cost of the cheapest
[^1]: **contiguous** - next or near in time or sequence
[^2]: [Subset of the vertices of the graph $G$, such that any vertex of $G$ is in it, or has a neighbor in it](https://en.wikipedia.org/wiki/Dominating_set)

View File

@ -0,0 +1,157 @@
---
type: theoretical
---
Can be utilized in algorithms. Here's a rundown of a couple of popular graph algorithms along with their use case and [Complexity](Complexity.md). TSP is [NP](P%20vs.%20NP.md), but it can be approximated/solved using a graph algorithm!
![](graph-algorithms-infographic.gif)
# Graph Algorithms
## Shortest Path Algorithms
### Dijkstra's Algorithm
Finds the shortest path from a source vertex to all other vertices in a graph with non-negative edge weights.
1. Initialize distances:
- Set the distance to the source vertex as 0 and all others as infinity.
2. Use a priority queue (min-heap) to extract the vertex with the smallest distance.
3. For each neighboring vertex, calculate the tentative distance via the current vertex:
- If the tentative distance is smaller than the known distance, update it.
4. Repeat until all vertices are processed.
- Using a priority queue: $O((V + E) \log V)$, where $V$ is the number of vertices and $E$ is the number of edges.
![](Dijkstra01.gif)
### Floyd-Warshall Algorithm
Computes shortest paths between all pairs of nodes in a weighted graph.
1. Initialize distances with edge weights.
2. Iteratively update distances by considering each node as an intermediate point.
3. Handle both positive and negative edge weights (no negative cycles allowed).
- **Complexity**: $O(V^3)$, where $V$ is the number of vertices.
![Floyd-Warshall Algorithm](218610439-933d939c-a9e1-489d-a0a2-da8cf7bf51c1.gif)
---
## Minimum Spanning Tree Algorithms
### Kruskal's Algorithm
Finds the minimum spanning tree (MST) of a graph.
1. Sort all edges by weight.
2. Add edges to the MST in increasing order of weight.
3. Skip edges that form a cycle (using Union-Find data structure :) ).
- **Complexity**: $O(E \log E)$, where $E$ is the number of edges.
![](Animation%20of%20Kruskal's%20Algorithm.gif)
### Prim's Algorithm
Constructs the minimum spanning tree (MST) of a graph.
1. Start with an arbitrary node.
2. Repeatedly add the smallest edge that connects a node in the MST to a node outside it.
3. Continue until all nodes are included.
- $O(E + V \log V)$ using a priority queue, where $V$ is the number of vertices and $E$ is the number of edges.
![Prim's Algorithm](4e486f5e-8437-4e2b-8964-5d5860208502_1650942396.093046.gif)
---
## Graph Traversal Algorithms
### Depth-First Search (DFS)
Explores as far as possible along each branch before backtracking.
- Detects cycles.
- Topological sorting.
- Pathfinding in mazes.
- **Complexity**: $O(V + E)$.
![](1_WR4AtjT_nhwSOtAW99Yd5g.gif)
---
### Breadth-First Search (BFS)
explores all neighbors at the current depth before moving to the next level.
- Finding shortest paths in unweighted graphs.
- Level-order traversal of trees.
- **Complexity**: $O(V + E)$.
![](bfs.gif)
![](dfs-vs-bfs.gif)
---
## Topological Sorting
Arranges the vertices of a directed acyclic graph (DAG) in a linear order such that for every directed edge $(u, v)$, $u$ comes before $v$.
- Perform a DFS and use a stack to store the vertices in reverse order of completion.
- **Complexity**: $O(V + E)$.
![](anim.gif)
---
## Maximum Flow Algorithms
### Ford-Fulkerson Algorithm
**Finds the maximum flow in a flow network.
1. Initialize the flow to 0.
2. While there is an augmenting path, increase the flow along the path.
3. Repeat until no augmenting paths remain.
- **Complexity**: $O(E \cdot \text{max\_flow})$.
![](FordFulkerson.gif)
---
### Edmonds-Karp Algorithm
A refinement of the Ford-Fulkerson algorithm that uses BFS to find augmenting paths.
- **Complexity**: $O(V \cdot E^2)$.
![](56e7f380-cd33-11eb-90b2-658e8d102a95.gif)
---
## Grid-Based Algorithms
### A* Algorithm
Finds the shortest path in a weighted grid using a heuristic.
1. Start from the source node.
2. Use a priority queue to explore the most promising paths first.
3. Use a heuristic to guide the search toward the goal.
- **Complexity**: Depends on the heuristic but generally better than BFS for weighted grids.
![](A.gif)
---
## Graph Coloring
Assigns colors to vertices such that no two adjacent vertices share the same color.
- **Algorithms**:
- Greedy coloring: $O(V^2)$.
- Backtracking for exact coloring.
---

View File

@ -0,0 +1,98 @@
---
type: mixed
---
## Definition of a Graph
A data structure that consists of:
- A set of vertices (or nodes).
- A set of edges, which connect pairs of vertices.
### Types of Graphs
1. **Directed Graph**:
- Edges have a direction, going from one vertex to another.
- Example: Websites linked via hyperlinks.
2. **Undirected Graph**:
- Edges have no direction; they simply connect two vertices.
- Example: Social networks where a connection is mutual.
3. **Weighted Graph**:
- Each edge is assigned a weight or cost.
- Example: Road networks where edge weights represent distances.
## Binary Tree
A hierarchical data structure where each node has at most two children, referred to as the left and right child. This structure is foundational for tasks such as expression parsing and hierarchical data representation.
- Maximum of two children per node.
- Recursive structure: subtrees are binary trees themselves.
![](Pasted%20image%2020241203231354.png)
---
## Binary Search Tree (BST)
A Binary Search Tree is a binary tree that maintains sorted order:
- For any node:
- All values in the left subtree are smaller.
- All values in the right subtree are larger.
- Enables $O(\log n)$ search, insertion, and deletion.
![](binary-search-tree-sorted-array-animation.gif)
---
## Balanced Tree (AVL Tree)
A self-balancing binary search tree where:
- The balance factor (difference in heights of left and right subtrees) of any node is at most one.
- Rotations are used to maintain balance during insertions and deletions.
- Guaranteed $O(\log n)$ operations.
- Ensures balanced growth for fast access.
![](YieLsCqeuV-avlbal.gif)
---
## Grids as Graphs
Can be modeled as a graph:
- Vertices: Represent grid cells.
- Edges: Represent valid movements between cells (e.g., up, down, left, right).
![](Pasted%20image%2020241203231638.png)
---
## Famous Graph Problems
Solved using [Graph Algorithms](Graph%20Algorithms.md).
### Shortest Path Problems
- Find the minimum distance or cost to travel between two nodes.
- **We can use**:
- Dijkstra's Algorithm (if non-negative weights).
- Bellman-Ford Algorithm (handles negative weights).
- Floyd-Warshall Algorithm (if we want all-pairs shortest paths).
### Minimum Spanning Tree (MST)
- Find a subset of edges that connects all vertices with the minimum total weight.
- **We can use**:
- Kruskal's Algorithm.
- Prim's Algorithm.
### Topological Sorting
- Arrange the vertices of a DAG in linear order such that for every directed edge $(u, v)$, $u$ appears before $v$.
### Max Flow / Min Cut
- Find the maximum flow possible in a network from a source to a sink.
- Ford-Fulkerson Algorithm.
- Edmonds-Karp Algorithm.
### Traveling Salesman Problem (TSP)
- Find the shortest tour that visits each vertex exactly once and returns to the starting point.
### Pathfinding in Grids
- Find the shortest path in a grid-based graph.
- **We can use**:
- A* Algorithm (heuristic-based).
- Breadth-First Search (BFS) for unweighted grids.
---

View File

@ -0,0 +1,82 @@
---
type: theoretical
---
![Video](https://www.youtube.com/watch?v=mdQzAp7gSns)
![Video 2](https://youtu.be/pQsdygaYcE4)
## Definition
- Class P
- The set of decision problems that can be solved by a deterministic Turing machine in polynomial time[^1]. These are problems considered to be efficiently solvable.
- $O(\log n)$
- The set of decision problems for which a given solution can be verified in polynomial time by a deterministic Turing machine[^2]. NP stands for "nondeterministic polynomial time."
- :LiLoaderPinwheel: Does $\mathbf{P = NP}$?
- This is one of the most important open questions in computer science. It asks whether every problem whose solution can be quickly verified can also be quickly solved.
## Understanding P and NP
![](Pasted%20image%2020241203234032.png)
### Class P Problems
- Solvable in polynomial time.
- Algorithms exist that can find a solution efficiently as the input size grows.
- **As seen in**:
- Prime Testing: Determining if a number is prime.
- Shortest Path: Finding the shortest path in a graph (e.g., Dijkstra's algorithm).
- Sorting Algorithms: Such as Quick Sort and Merge Sort.
### Class NP Problems
- Solutions can be verified in polynomial time.
- No known polynomial-time algorithms to solve all NP problems.
- **As in**:
- Subset Sum: Determining if a subset of a given set of integers sums up to a target integer.
- 3-SAT: Determining if a Boolean formula in conjunctive normal form with at most three literals per clause is satisfiable.
- Hamiltonian Cycle: Determining if a Hamiltonian cycle exists in a graph.
## NP-Complete Problems
- The hardest problems in NP. A problem is NP-Complete if:
- It is in NP.
- Every problem in NP can be reduced to it in polynomial time[^3].
- Implications: If any NP-Complete problem is solvable in polynomial time, then $P = NP$.
- Traveling Salesperson Problem (Decision Version): Determining if there's a tour shorter than a given length.
- Clique Problem: Finding a complete subgraph (clique) of a certain size in a graph.
- Vertex Cover: Determining if there exists a set of vertices covering all edges.
## Problems
### Subset Sum Problem
- **Input**: A set of integers and a target sum.
- **Question**: Is there a subset whose sum equals the target?
- **Approach**:
- **Exponential Time**: Checking all possible subsets.
- **Dynamic Programming**: Pseudo-polynomial time algorithm when numbers are small.
### 3-SAT Problem
- **Input**: A Boolean formula in 3-CNF (Conjunctive Normal Form).
- **Question**: Is there a truth assignment that satisfies the formula?
- **Importance**: The first problem proven to be NP-Complete (Cook-Levin theorem).
## Strategies
- Find solutions close to optimal in polynomial time.
- Practical methods that find good-enough solutions without guaranteeing optimality (heuristics).
- Restricting the problem to a subset where it becomes solvable in polynomial time.
## NP vs. NP-Complete Differences
|**Aspect**|**NP**|**NP-Complete**|
|---|---|---|
|**Definition**|Problems whose solutions can be verified quickly.|Hardest problems in NP. All NP problems reduce to them.|
|**Relation to P**|Contains P (P ⊆ NP).|If any NP-complete problem is in P, P = NP.|
|**Ease of Solution**|Some problems may have unknown solution methods.|Believed to be computationally difficult to solve.|
|**Examples**|Subset Sum, 3-SAT|TSP, Clique, Vertex Cover|
![](Pasted%20image%2020241203234600.png)
[^1]: a theoretical computing machine that uses a predetermined set of rules to determine its actions.
[^2]: a theoretical model that, unlike a deterministic Turing machine, can make "guesses" to find solutions more efficiently.
[^3]: a method of converting one problem to another in polynomial time, preserving the problem's computational complexity.
[^4]: the complements of NP-Complete problems, where verifying a "no" instance is in NP.

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -0,0 +1,86 @@
---
type: mixed
---
## Prefix Function ($\pi$)
The prefix function is a tool used in pattern matching algorithms, particularly in the **Knuth-Morris-Pratt (KMP) algorithm**. It is designed to preprocess a pattern to facilitate efficient searching.
### Definition
For a string $P$ of length $m$, the prefix function $\pi[i]$ for $i = 1, 2, \ldots, m$ is the length of the longest proper prefix of the substring $P[1 \ldots i]$ that is also a suffix of this substring.
### Key Points
1. A proper prefix of a string is a prefix that is not equal to the entire string. [^1]
2. $\pi[i]$ helps skip unnecessary comparisons in pattern matching by indicating the next position to check after a mismatch.
3. $\pi[1] = 0$ always, since no proper prefix of a single character can also be a suffix.
### Example
For the pattern $P = "ababcab"$:
- $P[1] = "a"$: $\pi[1] = 0$.
- $P[1 \ldots 2] = "ab"$: No prefix matches the suffix, so $\pi[2] = 0$.
- $P[1 \ldots 3] = "aba"$: Prefix "a" matches suffix "a", so $\pi[3] = 1$.
- $P[1 \ldots 4] = "abab"$: Prefix "ab" matches suffix "ab", so $\pi[4] = 2$.
- Continue similarly to compute $\pi[i]$ for the entire pattern.
---
## Knuth-Morris-Pratt (KMP) Algorithm
The KMP algorithm is a pattern matching algorithm that uses the prefix function $\pi$ to efficiently search for occurrences of a pattern $P$ in a text $T$.
### Key Idea
When a mismatch occurs during the comparison of $P$ with $T$, use the prefix function $\pi$ to determine the next position in $P$ to continue matching, rather than restarting from the beginning.
### Steps
1. Compute the prefix function $\pi$ for the pattern $P$.
2. Search:
- Compare $P$ with substrings of $T$.
- If theres a mismatch at $P[j]$ and $T[i]$, use $\pi[j]$ to shift $P$ rather than restarting at $P[1]$.
3. The algorithm runs in $O(n + m)$ time [complexity](Complexity.md), where $n$ is the length of $T$ and $m$ is the length of $P$.
---
## Rabin-Karp Algorithm
The Rabin-Karp algorithm is another pattern matching algorithm, notable for using hashing to identify potential matches.
### Key Idea
Instead of comparing substrings character by character, the algorithm compares hash values of the pattern and substrings of the text.
### Steps
1. Compute the hash value of the pattern $P$ and the first substring of the text $T$ of length $m$.
2. Slide the window over $T$ and compute hash values for the next substrings in constant time using a rolling hash. [^2]
3. If the hash value of a substring matches the hash value of $P$, compare the actual strings to confirm the match.
### Hash Function
The hash function is typically chosen such that it is fast to compute and minimizes collisions:
$$
h(s) = (s[1] \cdot p^{m-1} + s[2] \cdot p^{m-2} + \ldots + s[m] \cdot p^0) \mod q,
$$
where:
- $p$ is a base (e.g., a small prime number),
- $q$ is a large prime to avoid overflow.
### Complexity
- Best Case: $O(n + m)$, where $n$ is the length of the text and $m$ is the length of the pattern.
- Worst Case: $O(nm)$ due to hash collisions.
---
## KMP v.s. Rabin-Karp
| Feature | Knuth-Morris-Pratt (KMP) | Rabin-Karp |
| ------------- | ------------------------ | --------------------------------------------------- |
| Technique | Prefix function | Hashing |
| Preprocessing | Compute $\pi$ array | Compute hash of $P$ |
| Efficiency | $O(n + m)$ | $O(n + m)$ (best), $O(nm)$ (worst) |
| Use Case | Best for exact matches | Useful for multiple patterns or approximate matches |
| | | |
_This graphic is too AI generated for me_ -> Use KMP when looking for a pattern, use RK when multiple patterns
---
## Footnotes
[^1]: A proper prefix of a string $s$ is any prefix of $s$ that is not equal to $s$ itself. For example, proper prefixes of "abc" are "", "a", and "ab".
[^2]: A rolling hash computes the hash of a new substring by updating the hash of the previous substring, avoiding the need to recompute from scratch.

View File

@ -0,0 +1,126 @@
---
type: mixed
---
In algorithmics, a **recurrence relation** is often used to describe the time complexity of recursive algorithms. It expresses how the running time of an algorithm depends on the size of the input and the cost of recursive calls.
- **Example**: Consider the merge sort algorithm, which divides the input of size $n$ into two halves, recursively sorts each half, and then merges the two sorted halves. Its recurrence relation is:
$$
T(n) = 2T\left(\frac{n}{2}\right) + O(n),
$$
where:
- $2T\left(\frac{n}{2}\right)$ represents the two recursive calls on halves of the input.
- $O(n)$ represents the time to merge the two halves.
---
## Solving Recurrence Relations for Time Complexity
To determine the time complexity of an algorithm, we solve the recurrence relation to find an explicit formula for $T(n)$.
### 1. **Backtracking**
This involves repeatedly substituting the recurrence relation into itself until a pattern emerges.
- **Example**: For the recurrence $T(n) = 2T\left(\frac{n}{2}\right) + O(n)$:
- First substitution:
$$
T(n) = 2\left[2T\left(\frac{n}{4}\right) + O\left(\frac{n}{2}\right)\right] + O(n)
= 4T\left(\frac{n}{4}\right) + 2O\left(\frac{n}{2}\right) + O(n).
$$
- Second substitution:
$$
T(n) = 8T\left(\frac{n}{8}\right) + 4O\left(\frac{n}{4}\right) + 2O\left(\frac{n}{2}\right) + O(n).
$$
- General pattern:
$$
T(n) = 2^k T\left(\frac{n}{2^k}\right) + O\left(n \log n\right).
$$
- When $k = \log n$, the base case $T(1)$ is reached, so:
$$
T(n) = O(n \log n).
$$
---
### 2. **Master Theorem**
The **Master Theorem** provides a direct way to analyze recurrence relations of the form:
$$
T(n) = aT\left(\frac{n}{b}\right) + O(n^d),
$$
where:
- $a$ is the number of recursive calls,
- $b$ is the factor by which the input size is divided,
- $d$ is the exponent of the non-recursive work. [^1]
The solution depends on the comparison of $a$ with $b^d$:
1. **Case 1 ($a < b^d$)**: The work outside recursion dominates.
$$
T(n) = O(n^d).
$$
2. **Case 2 ($a = b^d$)**: The work is evenly distributed.
$$
T(n) = O(n^d \log n).
$$
3. **Case 3 ($a > b^d$)**: The recursion dominates.
$$
T(n) = O(n^{\log_b a}).
$$
- **Example**: For $T(n) = 2T\left(\frac{n}{2}\right) + O(n)$:
- $a = 2$, $b = 2$, $d = 1$.
- Compare $a$ with $b^d$: $2 = 2^1$, so it falls under **Case 2**.
- Solution:
$$
T(n) = O(n \log n).
$$
---
### 3. **Substitution Method**
This involves guessing a solution and proving it correct using **mathematical induction**.
- **Example**: For $T(n) = 2T\left(\frac{n}{2}\right) + O(n)$, guess $T(n) = O(n \log n)$:
1. Assume $T(k) = O(k \log k)$ for $k < n$ (inductive hypothesis).
2. Substitute into the recurrence:
$$
T(n) = 2T\left(\frac{n}{2}\right) + O(n).
$$
Using the hypothesis:
$$
T\left(\frac{n}{2}\right) = O\left(\frac{n}{2} \log \frac{n}{2}\right) = O\left(\frac{n}{2} (\log n - 1)\right).
$$
Simplify:
$$
T(n) = 2 \cdot O\left(\frac{n}{2} \log n\right) + O(n) = O(n \log n).
$$
3. Conclusion: The guess is correct.
---
## Applications in Algorithm Design
1. **Divide and Conquer**: Many divide-and-conquer algorithms, like merge sort, quicksort, and binary search, have their time complexity described by recurrence relations.
2. **Dynamic Programming**: Recurrences are also used to describe subproblem dependencies in dynamic programming algorithms.
3. **Graph Algorithms**: Recurrences appear in graph traversal techniques and optimization algorithms (e.g., shortest paths).
---
## Tips for Mastering Recurrence Relations
1. Understand the **nature of recursion** in the algorithm (e.g., how input is divided, base cases, etc.).
2. Identify the **dominant term** in the recurrence to estimate growth.
3. Use tools like **backtracking**, the **Master Theorem**, or **substitution** to solve recurrences efficiently.
4. Practice interpreting recurrences in terms of algorithm behavior.
---
### Backlinks
- [Recurrence relations](Discrete%20Structures/Recurrence%20relations.md)
- [Complexity](Complexity.md)
[^1]: The Master Theorem simplifies solving divide-and-conquer recurrences by comparing the relative growth of recursion and non-recursive work.

View File

@ -0,0 +1,39 @@
---
date: 02.09.2024
type: mixed
---
## Sorting algorithms and their [complexity](Complexity.md)
| **Algorithm** | **Time Complexity** |
| ------------- | ------------------------------------------------------------- |
| Selection | $\mathcal{O}(n^2)$ |
| Insertion | $\mathcal{O}(n^2)$ |
| Bubble | $\mathcal{O}(n^2)$ |
| Quick | $\mathcal{O}(n^2)$ worst case, $\mathcal{O}(n\log n)$ average |
| Merge | $\mathcal{O}(n\log n)$ |
| Heap | $\mathcal{O}(n\log n)$ |
### Selection Sort
![Selection sort](Selection-sort-0.png)
![](selection_sort.gif)
### Insertion sort
![](insertion_sort.gif)
### Bubble sort
![](bubble_sort.gif)
### Quick sort
![](quick_sort.gif)
### Merge sort
![](merge_sort.gif)
Cooler visual:
![](Merge-sort.gif)
### Heap sort
![](Heap_sort.gif)

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,478 @@
---
excalidraw-plugin: parsed
tags:
- excalidraw
type: mixed
---
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠== You can decompress Drawing data with the command palette: 'Decompress current Excalidraw file'. For more info check in plugin settings under 'Saving'
# Code Block
The code provided is an implementation designed to solve the *Volcano Research* problem efficiently using an AVL tree data structure. Heres a step-by-step explanation of how the code works and how it solves the problem:
### Problem Recap
Martin and Szymon want to know which species live at specific distances from a volcano based on given queries. The solution needs to determine which species are present at a given distance and then provide the k-th species name from the alphabetically sorted list of species at that distance. If there are fewer species than `k` at that distance, the output should be `-`.
### High-Level Overview of the Solution
1. **Data Representation**:
- The code uses structures (`Species` and `Event`) to store the information about species and events.
- An AVL tree (a self-balancing binary search tree) is used to keep track of species that are currently within the queried distance range efficiently.
2. **Input Parsing and Initialization**:
- The input consists of species data (name and range) and queries (distance and index).
- The code reads the input and populates two arrays: one for species (`Species` array) and one for events (`Event` array).
3. **Event Generation**:
- The code generates three types of events for each species:
- `START` event: marks the beginning of the range where the species can be found.
- `END` event: marks the end of the range.
- `QUERY` event: represents each query (distance and k value).
- These events are stored in a list and sorted based on their distance and type to handle them in an ordered manner.
4. **Processing Events**:
- The code processes each event in order of distance and type (using the `compareEvents` function for sorting):
- **`START` Event**: The species becomes active at this distance, so it is added to the AVL tree.
- **`END` Event**: The species goes out of range, so it is removed from the AVL tree.
- **`QUERY` Event**: The code checks the AVL tree to see if there are enough species within the distance range to answer the query.
5. **AVL Tree Operations**:
- **Insertion and Deletion**: The AVL tree is updated when species start or end at certain distances. This keeps the tree balanced, ensuring efficient lookups.
- **Finding the k-th Species**: When a query is processed, the code checks the size of the tree (`getSize`) to see if there are at least `k` species available. If so, it finds the k-th species using an in-order traversal (`findKth`).
6. **Output Generation**:
- The code collects the results for each query and outputs the appropriate species name if found, or `-` if fewer than `k` species are present.
### Detailed Explanation of the Code
1. **Data Structures**:
- `Species` Structure: Contains the name, start, and end distances, and a unique ID (`speciesId`) for each species.
- `Event` Structure: Represents different events (START, END, QUERY) with associated data like distance, species ID, and query parameters.
2. **Sorting Species by Name**:
- The `speciesPtrs` array is used to sort species alphabetically. Each species is assigned an ID based on this sorted order, which will help in quickly finding and outputting species names in alphabetical order.
3. **Events Sorting**:
- The events are sorted first by distance and then by type (`START`, `QUERY`, `END`), ensuring that they are processed in the correct order for efficient tree management.
4. **AVL Tree Functions**:
- The code uses several AVL tree functions to manage the tree:
- `insert` adds a species ID to the tree.
- `erase` removes a species ID when the species goes out of range.
- `findKth` finds the k-th smallest element in the tree, which corresponds to the species alphabetically.
- `getSize` returns the current number of species in the tree.
5. **Efficient Query Handling**:
- As events are processed in sorted order, the AVL tree always contains only the species that are active at the current distance, ensuring that queries are handled efficiently without scanning the entire dataset.
6. **Final Output**:
- The `answers` array collects the results for each query. The code uses the species ID from the AVL tree to look up the species name using the `speciesPtrs` array and prints it. If there are fewer species than `k` at the queried distance, it outputs a `-`.
### Why This Approach Works Efficiently
- **Event-based Approach**: By treating species ranges and queries as events, the solution efficiently processes the data in a linear sweep, avoiding unnecessary iterations over all distances.
- **AVL Tree**: The self-balancing nature of the AVL tree ensures that insertion, deletion, and k-th smallest element retrieval are all efficient (O(log n)), keeping the solution within time limits even for the maximum input size.
- **Memory Management**: The code efficiently uses dynamic memory to handle the large constraints of up to 300,000 species and queries.
### Complexity Analysis
- **Time Complexity**: Sorting events takes \(O(m \log m)\), where \(m = 2n + q\). AVL tree operations (insert, delete, and findKth) all take \(O(\log n)\), ensuring that the solution runs efficiently within the constraints.
- **Space Complexity**: The memory usage is primarily dominated by storing species, events, and the AVL tree nodes, which is handled efficiently within the provided memory limit.
In summary, the code uses an event-driven approach combined with an AVL tree to maintain a dynamic set of species efficiently, allowing for quick lookups and updates to answer the queries in the required time and space constraints.
# Code Block 1
An AVL tree is a self-balancing binary search tree (BST), named after its inventors Adelson-Velsky and Landis. The AVL tree maintains its height to ensure that all operations (insertion, deletion, lookup, etc.) are performed in \( O(\log n) \) time. This efficiency is achieved by maintaining a balance factor at each node and rebalancing the tree when necessary. Let's go through the details step by step, including augmentation.
### 1. Structure of an AVL Tree
An AVL tree is a binary search tree where each node contains:
- **Value/Key**: The value stored in the node.
- **Left and Right Pointers**: Pointers to the left and right child nodes.
- **Height**: The height of the node (i.e., the number of edges on the longest path from the node to a leaf).
- **Balance Factor**: The balance factor is the difference in height between the left and right subtrees of the node:
\[
\text{Balance Factor} = \text{Height of Left Subtree} - \text{Height of Right Subtree}
\]
An AVL tree ensures that the balance factor of every node is either \(-1\), \(0\), or \(1\). If any node's balance factor falls outside this range, the tree is rebalanced to restore this property.
### 2. Operations in an AVL Tree
The main operations in an AVL tree are insertion, deletion, and lookup. Lets go through each:
#### a. Insertion
1. **Standard BST Insertion**: Insert the node like in a regular BST, placing it in its appropriate position based on the value.
2. **Update Heights**: Traverse back up the tree, updating the height of each node.
3. **Check and Fix Balance**: After updating heights, check the balance factor of each node. If it goes out of the range \([-1, 1]\), perform rotations to rebalance.
#### b. Rotations for Rebalancing
There are four types of rotations:
1. **Right Rotation (Single)**:
- Applied when the left subtree of a node becomes too tall.
- Rotate the subtree right, making the left child the new root of the subtree.
2. **Left Rotation (Single)**:
- Applied when the right subtree of a node becomes too tall.
- Rotate the subtree left, making the right child the new root of the subtree.
3. **Left-Right Rotation (Double)**:
- Applied when the left subtree's right subtree is too tall.
- Perform a left rotation on the left child, then a right rotation on the node.
4. **Right-Left Rotation (Double)**:
- Applied when the right subtree's left subtree is too tall.
- Perform a right rotation on the right child, then a left rotation on the node.
#### c. Deletion
1. **Standard BST Deletion**: Remove the node like in a standard BST (with handling for nodes having 0, 1, or 2 children).
2. **Update Heights**: Update the heights of the nodes as you traverse back up.
3. **Check and Fix Balance**: If the balance factor goes out of range, perform the appropriate rotations to restore balance.
#### d. Lookup/Search
Since the AVL tree is a balanced BST, lookup/search operations are similar to a regular BST and run in \( O(\log n) \) time.
### 3. AVL Tree Augmentation
Augmentation involves adding extra information or functionality to the AVL tree beyond just storing values and maintaining balance. This is useful for answering more complex queries efficiently. One common augmentation is adding the **size** of the subtree at each node.
#### Augmenting with Subtree Size
In the AVL tree, we augment each node with an additional field:
- **Size**: The number of nodes in the subtree rooted at this node.
The size field is updated during insertion, deletion, and rotations, just like the height. The size of a node is:
\[
\text{Size} = 1 + \text{Size of Left Subtree} + \text{Size of Right Subtree}
\]
This augmentation allows us to:
1. **Find the k-th smallest element** efficiently.
2. **Count the number of elements within a range**.
3. **Rank of an element** (position in the sorted order).
##### Finding the k-th Smallest Element
To find the k-th smallest element:
1. Compare \( k \) with the size of the left subtree (\( \text{Size of Left Subtree} + 1 \)).
- If \( k \) equals this value, the current node is the k-th smallest.
- If \( k \) is smaller, the k-th smallest element is in the left subtree.
- If \( k \) is larger, adjust \( k \) to \( k - (\text{Size of Left Subtree} + 1) \) and search in the right subtree.
This approach allows finding the k-th smallest element in \( O(\log n) \) time due to the balanced nature of the tree.
### 4. Example of an Augmented AVL Tree in Practice
Lets walk through an example of how the augmented AVL tree can be used to find the k-th smallest element:
#### Example Tree (Balanced AVL)
```
15 (5)
/ \
10 (3) 20 (1)
/ \
5 (1) 12 (1)
```
- Numbers in parentheses represent the size of the subtree rooted at that node.
- To find the 3rd smallest element:
1. Check the left subtree of the root (15). The size is 3.
2. Since \( k = 3 \), and the size of the left subtree plus one is also 3, the roots left child (10) is the answer.
#### Updates and Maintenance
- **Insertion**: When a node is added, the tree is rebalanced if necessary, and sizes are updated accordingly as the recursive call returns.
- **Deletion**: When a node is deleted, the size is also updated as you traverse back up, and rotations are applied if the tree becomes unbalanced.
### 5. Applications of AVL Trees and Their Augmentations
1. **Interval Trees**: AVL trees can be augmented to store intervals and efficiently answer queries like finding overlapping intervals.
2. **Order Statistics**: By keeping track of subtree sizes, AVL trees can efficiently find the rank or the k-th smallest/largest element.
3. **Range Queries**: Augmenting nodes with additional data (like sum or minimum/maximum) can help quickly answer range queries in \( O(\log n) \) time.
### 6. Summary
- **AVL Tree Basics**: Its a self-balancing binary search tree ensuring \( O(\log n) \) operations by maintaining balance factors and using rotations.
- **Augmentation**: Adding extra information (e.g., subtree size) enhances the functionality, allowing efficient solutions to complex problems like finding k-th smallest elements or performing range queries.
- **Efficiency**: The augmented AVL tree remains efficient in terms of both time and space, as each operation still runs in \( O(\log n) \) time, making it suitable for handling large data sets.
The use of AVL trees (with or without augmentation) is fundamental in many computational problems where dynamic updates and efficient queries are required, making them a versatile tool in computer science.
# Code Block 2
The provided code solves the *For the Greater Good* problem using a combination of sorting and a max-heap strategy. The code efficiently determines the maximum number of generators that Bob can hack while alternating between AC and DC generators. Let's go through the code step by step to understand how it works.
### Problem Recap
Bob needs to hack as many generators as possible, alternating between AC and DC generators. He starts with a certain amount of experience points (`XP`), and each generator has:
- **Type** (`0` for AC, `1` for DC)
- **XP Needed**: The minimum XP required to hack the generator
- **XP Generated**: The amount of XP Bob gains after hacking that generator.
Bob can hack a generator if his XP is sufficient for that generator. The challenge is to find the maximum number of generators Bob can hack by alternating generator types.
### Overview of the Code
1. **Data Structures**:
- **`Generator` Struct**: Represents each generator with its type, XP required, and XP generated.
- **`MaxHeap`**: A custom max-heap structure is used to always choose the generator that yields the maximum XP from the set of generators that Bob can currently hack.
2. **Algorithm Outline**:
- Separate the generators into two lists: one for AC generators and one for DC generators.
- Sort these lists based on the XP needed in ascending order.
- Use two max-heaps (one for AC and one for DC) to keep track of which generators can be hacked based on Bobs current XP.
- Simulate Bob hacking generators, alternating between AC and DC types, and try both possible starting types to find the maximum number of generators Bob can hack.
### Detailed Explanation of the Code
#### 1. Generator Struct and Sorting Function
- The `Generator` struct stores the generator type, XP required (`xpNeeded`), and XP generated (`xpGenerated`).
- The `compareByXpNeeded` function is used to sort generators based on the XP needed in ascending order. This helps Bob efficiently find which generators are available based on his current XP.
#### 2. Heap Implementation
- **Heap Initialization**: The `initHeap` function initializes a max-heap for storing pointers to generators.
- **Heapify Up and Down**: These functions (`heapifyUp`, `heapifyDown`) maintain the max-heap property:
- `heapifyUp`: Ensures that after inserting a new element, the heap remains valid by moving the element up as needed.
- `heapifyDown`: Adjusts the heap after removing the top element, ensuring the next maximum element is at the top.
- **Push and Pop**: `pushHeap` adds a generator to the heap, and `popHeap` removes and returns the generator with the highest XP generated value. This allows Bob to always select the generator that maximizes his XP gain at each step.
#### 3. Hacking Strategy (`hackGenerators` Function)
The `hackGenerators` function implements the core logic:
1. **Separating Generators by Type**:
- It creates two arrays (`ac` for AC generators and `dc` for DC generators) and populates them based on the type of each generator.
- Both arrays are sorted by `xpNeeded` using `qsort`.
2. **Simulating the Hacking Process**:
- The function tests two scenarios: starting with an AC generator (`startingType = 0`) and starting with a DC generator (`startingType = 1`).
- For each starting type:
- Initialize indices (`acIdx` and `dcIdx`) to iterate through the sorted lists.
- Maintain two max-heaps (`acHeap` and `dcHeap`) to track available generators that Bob can hack.
- While there are generators that Bob can hack:
- Add all AC generators Bob can currently hack (where `xpNeeded <= xp`) to `acHeap` if the current type is AC.
- If a generator is available in the heap, hack it (pop from the heap), increase XP, and increment the hack count.
- Switch to the other type (AC to DC or DC to AC) for the next iteration.
- The maximum number of generators hacked in each scenario is tracked to determine the best strategy.
3. **Return the Maximum Number of Hacks**:
- The function returns the maximum value between the two scenarios (starting with AC or starting with DC).
### 4. Main Function
- The `main` function reads the input, initializes the array of generators, and calls `hackGenerators` with Bob's initial XP and the list of generators.
- It then prints the result (maximum number of generators Bob can hack) and frees the allocated memory.
### Example Walkthrough
Let's walk through an example to see how the algorithm works:
#### Input Example
```
5 4
1 3 2
0 4 1
0 10 5
1 7 3
0 22 9
```
- **Initial XP**: 4
- **Generators**:
- (Type: DC, XP Needed: 3, XP Generated: 2)
- (Type: AC, XP Needed: 4, XP Generated: 1)
- (Type: AC, XP Needed: 10, XP Generated: 5)
- (Type: DC, XP Needed: 7, XP Generated: 3)
- (Type: AC, XP Needed: 22, XP Generated: 9)
#### Simulation Details
1. **Separating and Sorting Generators**:
- AC generators: [(4, 1), (10, 5), (22, 9)]
- DC generators: [(3, 2), (7, 3)]
2. **Starting with DC**:
- Bob hacks DC (3, 2), gaining 2 XP (now XP = 6).
- Next, Bob hacks AC (4, 1), gaining 1 XP (now XP = 7).
- Bob hacks DC (7, 3), gaining 3 XP (now XP = 10).
- Finally, Bob hacks AC (10, 5), gaining 5 XP (now XP = 15).
- Total hacks: 4.
3. **Starting with AC**:
- Bob hacks AC (4, 1), gaining 1 XP (now XP = 5).
- Next, Bob hacks DC (3, 2), gaining 2 XP (now XP = 7).
- Bob hacks AC (10, 5), gaining 5 XP (now XP = 12).
- Finally, Bob hacks DC (7, 3), gaining 3 XP (now XP = 15).
- Total hacks: 4.
The maximum number of hacks is 4.
### Complexity Analysis
- **Time Complexity**:
- Sorting the generators takes \( O(n \log n) \).
- Heap operations (push and pop) each take \( O(\log n) \). Since each generator is pushed and popped at most once, the overall time complexity remains \( O(n \log n) \).
- **Space Complexity**:
- The space used for the heaps is \( O(n) \), and the arrays (`ac` and `dc`) also take \( O(n) \). Thus, the space complexity is \( O(n) \).
### Summary
The code efficiently determines the maximum number of generators Bob can hack using a combination of sorting and max-heap operations to select the optimal generators at each step. By testing both starting scenarios (AC first and DC first), the code guarantees the optimal solution.
# Code Block 3
### Heaps Overview
Heaps are tree-based structures used for efficient priority queues. They are complete binary trees with two types:
- **Min-Heap**: Root is the smallest element.
- **Max-Heap**: Root is the largest element.
### Max-Heap Properties
- Each nodes value is greater than or equal to its childrens.
- The largest value is always at the root.
- Its a **complete** binary tree: fully filled except possibly the last level, filled from left to right.
### Max-Heap Operations
1. **Insertion**:
- Add the new element at the next available spot.
- **Heapify Up**: Swap with the parent until the heap property is restored.
2. **Deletion (Remove Max)**:
- Remove the root.
- Move the last element to the root.
- **Heapify Down**: Swap with the largest child until the heap property is restored.
3. **Peek/Top**: Access the maximum element (root) in \(O(1)\).
### Heapify Process
- **Heapify Up**: Used during insertion to move the element up until its in the right spot.
- **Heapify Down**: Used during deletion to move the new root down to maintain the max-heap property.
### Array Representation
Heaps are often stored in arrays:
- **Left Child**: \(2i + 1\)
- **Right Child**: \(2i + 2\)
- **Parent**: \((i - 1) / 2\)
### Applications
- **Priority Queues**: Efficient access to max/min elements.
- **Heap Sort**: Sorts in \(O(n \log n)\).
- **Graph Algorithms**: E.g., Dijkstras shortest path.
### Advantages and Disadvantages
**Pros**:
- Fast insertion/deletion (\(O(\log n)\)).
- Efficient array representation.
**Cons**:
- No efficient ordered traversal (unlike BST).
- Limited to max/min element access unless augmented.
Heaps provide efficient operations for scenarios where fast access to max/min elements is needed.
# Excalidraw Data
## Text Elements
0:00 Intro
0:40 For the greater good - Max heap
12:59 Max heaps definition
15:44 Volcano research - Intervals + AVL Tree
30:10 Volcano Research - Overview of Code
33:48 IMPORTANT: Volcano Research - AVL Tree Code ^isYmpx3J
## Element Links
FKUJp7bM: [[Practical 5#Code Block]]
9gZqEpBZ: [[Practical 5#Code Block 1]]
zteJnsSi: [[Practical 5#Code Block 2]]
mivngCCL: [[Practical 5#Code Block 3]]
%%
## Drawing
```compressed-json
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
```
%%

File diff suppressed because one or more lines are too long