-
-
Notifications
You must be signed in to change notification settings - Fork 211
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
355 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
--- | ||
id: binary-search-algo | ||
sidebar_position: 2 | ||
title: Binary Search | ||
sidebar_label: Binary Search | ||
--- | ||
|
||
### Definition: | ||
|
||
Binary search is an efficient algorithm for finding a specific element in a sorted array or list. It works by repeatedly dividing the search interval in half. If the target value is less than the middle element, the search continues in the lower half; if it's greater, the search continues in the upper half. This process continues until the target is found or the interval is empty. | ||
|
||
### Characteristics: | ||
|
||
- **Divides and Conquers:** | ||
- The algorithm follows the divide-and-conquer paradigm. It breaks the problem into smaller subproblems (halves of the array) and solves them independently. | ||
|
||
- **Sorted Data:** | ||
- The input array must be sorted beforehand. If the array is unsorted, binary search will not work correctly. | ||
|
||
- **Can be Implemented Recursively or Iteratively:** | ||
- Binary search can be implemented using either a recursive function or an iterative approach. | ||
|
||
- **No Duplicates Handling:** | ||
- The basic binary search implementation does not handle duplicates well; it will only find one instance of the target. | ||
|
||
### Time Complexity: | ||
- **Best Case: O(1)** | ||
The best-case scenario occurs when the target element is found at the middle of the array on the first check. | ||
|
||
- **Average Case: O(logn)** | ||
On average, binary search will cut the search space in half with each iteration. This logarithmic growth means that as the size of the dataset n increases, the number of operations grows very slowly. | ||
|
||
- **Worst Case: O(logn)** | ||
The worst-case scenario occurs when the target element is not present in the array. The algorithm will still narrow down the search space until it exhausts all possibilities, leading to a logarithmic number of comparisons. | ||
|
||
### Space Complexity: | ||
- **Iterative Approach: O(1)** | ||
The iterative version of binary search uses a constant amount of space, as it only requires a few variables (like left, right, and mid) to keep track of indices. It does not require additional storage proportional to the size of the input. | ||
|
||
- **Recursive Approach: O(logn)** | ||
The recursive version of binary search uses stack space for recursive function calls. The maximum depth of the recursion is proportional to the logarithm of the number of elements in the array, leading to a space complexity of O(logn). | ||
|
||
### C++ Implementation: | ||
|
||
**Iterative Approach** | ||
```cpp | ||
#include <iostream> | ||
using namespace std; | ||
|
||
int binarySearchIterative(int arr[], int size, int target) { | ||
int left = 0; | ||
int right = size - 1; | ||
|
||
while (left <= right) { | ||
int mid = left + (right - left) / 2; // Prevents overflow | ||
|
||
if (arr[mid] == target) { | ||
return mid; // Target found | ||
} else if (arr[mid] < target) { | ||
left = mid + 1; // Search in the right half | ||
} else { | ||
right = mid - 1; // Search in the left half | ||
} | ||
} | ||
return -1; // Target not found | ||
} | ||
|
||
int main() { | ||
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // Sorted array | ||
int size = sizeof(arr) / sizeof(arr[0]); | ||
int target = 7; | ||
|
||
int result = binarySearchIterative(arr, size, target); | ||
|
||
if (result != -1) { | ||
cout << "Element found at index: " << result << endl; | ||
} else { | ||
cout << "Element not found." << endl; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
``` | ||
**Recursive Approach** | ||
```cpp | ||
#include <iostream> | ||
using namespace std; | ||
int binarySearchRecursive(int arr[], int left, int right, int target) { | ||
if (left > right) { | ||
return -1; // Base case: target not found | ||
} | ||
int mid = left + (right - left) / 2; // Prevents overflow | ||
if (arr[mid] == target) { | ||
return mid; // Target found | ||
} else if (arr[mid] < target) { | ||
return binarySearchRecursive(arr, mid + 1, right, target); // Search in the right half | ||
} else { | ||
return binarySearchRecursive(arr, left, mid - 1, target); // Search in the left half | ||
} | ||
} | ||
int main() { | ||
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // Sorted array | ||
int size = sizeof(arr) / sizeof(arr[0]); | ||
int target = 7; | ||
int result = binarySearchRecursive(arr, 0, size - 1, target); | ||
if (result != -1) { | ||
cout << "Element found at index: " << result << endl; | ||
} else { | ||
cout << "Element not found." << endl; | ||
} | ||
return 0; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
--- | ||
id: linear-search-algo | ||
sidebar_position: 1 | ||
title: Linear Search | ||
sidebar_label: Linear Search | ||
--- | ||
|
||
### Definition: | ||
|
||
A linear search algorithm is a simple method for finding a specific element in a list or array. It works by sequentially checking each element until the desired element is found or the end of the list is reached. | ||
|
||
### Characteristics: | ||
|
||
- **Simplicity:** | ||
- The algorithm is easy to understand and implement. It requires minimal code and logic, making it accessible for beginners. | ||
|
||
- **No Sorting Required:** | ||
- Linear search can be performed on unsorted arrays or lists. This makes it versatile since it doesn't need any preprocessing of the data. | ||
|
||
- **Multiple Occurrences:** | ||
- If multiple occurrences of the target value exist in the list, the linear search will return the index of the first occurrence. It can be modified to find all occurrences if needed. | ||
|
||
- **Best for Small Datasets:** | ||
- Linear search is efficient for small to medium-sized datasets. As the dataset grows, its inefficiency becomes more pronounced compared to more advanced search algorithms. | ||
|
||
### Linear Search Algorithm: | ||
|
||
**Steps:** | ||
1. Start at the beginning of the list. | ||
2. Check each element in the list one by one: | ||
- If the current element matches the target value, return the index of that element. | ||
- If it does not match, move to the next element. | ||
3. If the end of the list is reached and no match is found, return a value indicating that the target is not in the list (e.g., -1). | ||
|
||
### Time Complexity: | ||
|
||
- **Worst-case: O(n)** | ||
- This occurs when the target element is not present or is at the last position. | ||
|
||
- **Best-case: O(1)** | ||
- This occurs when the target element is the first element in the list. | ||
|
||
The space complexity of the linear search algorithm is O(1), which means it uses a constant amount of additional space regardless of the input size. | ||
|
||
### C++ Implementation: | ||
|
||
```cpp | ||
#include <iostream> | ||
using namespace std; | ||
|
||
int linearSearch(int arr[], int size, int target) { | ||
for (int index = 0; index < size; index++) { | ||
if (arr[index] == target) { | ||
return index; // Return the index if the target is found | ||
} | ||
} | ||
return -1; // Return -1 if the target is not found | ||
} | ||
|
||
int main() { | ||
int arr[] = {5, 3, 8, 4, 2}; | ||
int size = sizeof(arr) / sizeof(arr[0]); | ||
int target = 4; | ||
|
||
int result = linearSearch(arr, size, target); | ||
|
||
if (result != -1) { | ||
cout << "Element found at index: " << result << endl; | ||
} else { | ||
cout << "Element not found." << endl; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"label": "Algoritms", | ||
"position": 4, | ||
"link": { | ||
"type": "generated-index", | ||
"description": "5 minutes to learn the most important algorithms." | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
144 changes: 144 additions & 0 deletions
144
docs/data-structures/shortest_path_algo/shortestpath.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
--- | ||
id: shortest-path-algorithm | ||
title: Shortest Path Algorithm (Dijkstra's Algorithm) | ||
sidebar_label: Dijkstra's Algorithm | ||
tags: | ||
- Graph | ||
- Greedy | ||
- Algorithm | ||
- Shortest Path | ||
description: Given a graph represented by nodes and weighted edges, find the shortest path from a given source node to all other nodes using Dijkstra's Algorithm. | ||
--- | ||
|
||
## Problem Statement | ||
|
||
Given a graph with `n` nodes and weighted edges, the task is to find the shortest path from a given source node to all other nodes using Dijkstra's algorithm. | ||
|
||
### Examples | ||
|
||
*Example 1:* | ||
|
||
```plaintext | ||
Input: | ||
Number of vertices: 5 | ||
Edges: {(0, 1, 2), (0, 4, 8), (1, 2, 3), (1, 3, 5), (2, 3, 1), (4, 3, 4)} | ||
Source: 0 | ||
Output: [0, 2, 5, 6, 8] | ||
Explanation: | ||
The shortest path from node 0 to node 3 is 6. | ||
``` | ||
|
||
*Example 2:* | ||
```plaintext | ||
Input: | ||
Number of vertices: 3 | ||
Edges: {(0, 1, 1), (1, 2, 2), (0, 2, 4)} | ||
Source: 0 | ||
Output: [0, 1, 3] | ||
Explanation: | ||
The shortest path from node 0 to node 2 is 3. | ||
``` | ||
|
||
### Constraints | ||
```plaintext | ||
1 <= number of vertices <= 1000 | ||
1 <= weight of edges <= 100 | ||
``` | ||
### Solution | ||
Dijkstra’s algorithm is a well-known greedy algorithm used to find the shortest path between nodes in a graph, which can either be directed or undirected. The algorithm works by maintaining a set of nodes whose shortest path distance from the source is known. It picks the closest unvisited node, calculates its distance through its neighbors, and updates the shortest paths accordingly. | ||
|
||
## Approach: Dijkstra's Algorithm | ||
### Algorithm | ||
1) Create a priority queue (min-heap) to store nodes and their minimum distance from the source node. | ||
2) | ||
Initialize the distance of all nodes to infinity, except the source node which is set to 0. | ||
3) While the priority queue is not empty: | ||
- Extract the node with the minimum distance from the queue. | ||
- For each neighboring node, calculate the distance via the current node and update the neighbor’s distance | ||
4) if it's smaller than the previously known distance. | ||
Repeat until all nodes have been processed or the queue is empty. | ||
### Implementation | ||
|
||
```cpp | ||
#include <iostream> | ||
#include <vector> | ||
#include <queue> | ||
#include <climits> | ||
|
||
using namespace std; | ||
|
||
typedef pair<int, int> pii; // {distance, node} | ||
|
||
vector<int> dijkstra(int V, vector<vector<pii>>& adj, int source) { | ||
// Create a distance vector, initialized to infinity | ||
vector<int> dist(V, INT_MAX); | ||
|
||
// Min-heap priority queue to store {distance, node} | ||
priority_queue<pii, vector<pii>, greater<pii>> pq; | ||
|
||
// Set the distance of the source to 0 and add it to the queue | ||
dist[source] = 0; | ||
pq.push({0, source}); | ||
|
||
while (!pq.empty()) { | ||
int currentDist = pq.top().first; | ||
int currentNode = pq.top().second; | ||
pq.pop(); | ||
|
||
// If this distance is already larger than the known shortest, skip | ||
if (currentDist > dist[currentNode]) { | ||
continue; | ||
} | ||
|
||
// Process each neighbor | ||
for (auto& neighbor : adj[currentNode]) { | ||
int nextNode = neighbor.first; | ||
int weight = neighbor.second; | ||
|
||
// Calculate new potential distance | ||
int newDist = currentDist + weight; | ||
|
||
// If the new distance is shorter, update and push to queue | ||
if (newDist < dist[nextNode]) { | ||
dist[nextNode] = newDist; | ||
pq.push({newDist, nextNode}); | ||
} | ||
} | ||
} | ||
|
||
return dist; | ||
} | ||
|
||
int main() { | ||
int V = 5; | ||
vector<vector<pii>> adj(V); | ||
|
||
// Adding edges (node1, node2, weight) | ||
adj[0].push_back({1, 2}); | ||
adj[0].push_back({4, 8}); | ||
adj[1].push_back({2, 3}); | ||
adj[1].push_back({3, 5}); | ||
adj[2].push_back({3, 1}); | ||
adj[4].push_back({3, 4}); | ||
|
||
int source = 0; | ||
vector<int> shortestPaths = dijkstra(V, adj, source); | ||
|
||
cout << "Shortest distances from node " << source << ": "; | ||
for (int dist : shortestPaths) { | ||
cout << dist << " "; | ||
} | ||
|
||
return 0; | ||
} | ||
``` | ||
## Complexity Analysis | ||
- **Time complexity:** `$O((V + E) \log V)$`, where `V` is the number of vertices and `E` is the number of edges. | ||
- Inserting and extracting from the priority queue takes `$O(\log V)$`, and each - edge is processed at most once. | ||
- **Space complexity**: `$O(V + E)$` for the adjacency list and the distance array. | ||
## Conclusion | ||
Dijkstra’s algorithm efficiently finds the shortest path from a source node to all other nodes in a graph. The algorithm works best with non-negative weights, and its performance can be optimized using min-heaps or priority queues. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters