Skip to content

Commit b6f04da

Browse files
added comments and searchtree
1 parent 09dd460 commit b6f04da

File tree

12 files changed

+180
-2
lines changed

12 files changed

+180
-2
lines changed

src/main/scala/algorithms/dynamic/Decomposition.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
package algorithms.dynamic
22

33
// Bottom-up dynamic programming approach
4+
/// Compute the maximum revenue from rod decomposition using dynamic programming.
5+
///
6+
/// Given a rod of length `n` and an array `prices` where `prices(i)` represents the price of a rod of length `i+1`,
7+
/// the goal is to determine the optimal way to cut the rod to maximize revenue.
8+
///
9+
/// We use a **bottom-up dynamic programming** approach:
10+
/// - Define `dp(i)` as the maximum revenue obtainable for a rod of length `i`.
11+
/// - The recurrence relation is:
12+
/// dp(j) = max(prices(i) + dp(j - i - 1)) for all valid cuts i.
13+
/// - We iterate from length `1` to `n`, computing the best revenue for each length.
14+
///
15+
/// Time Complexity: **O(n²)** – We compute the maximum for each length `j` by iterating through all possible cuts `i`.
416
def bottomUpDecomposition(prices: Array[Int], n: Int): Int = {
517
val dp = Array.fill(n + 1)(0) // dp[i] stores the max revenue for length i
618

src/main/scala/algorithms/dynamic/MaxNonAdjacentWeight.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
package algorithms.dynamic
22

3+
/// Calculate the maximum weight sum of non-adjacent elements in an array.
4+
/// The function also returns the subsequence of selected elements.
5+
///
6+
/// Example:
7+
/// val arr = Array(3, 2, 7, 10)
8+
/// val (maxWeight, maxSequence) = maxNonAdjacentWeight(arr)
9+
/// println(s"Max weight: $maxWeight") // Returns (13)
10+
/// println(s"Sequence: $maxSequence") // Returns (3, 10)
11+
///
12+
/// Time Complexity: O(n) – The DP table is filled once iteratively.
313
def maxNonAdjacentWeight(a: Array[Int]): (Int, List[Int]) = {
414
val n = a.length
515
if (n == 0) return (0, List())

src/main/scala/algorithms/dynamic/OptimalMatrix.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ package algorithms.dynamic
22

33
case class Matrix(n: Int, m: Int)
44

5+
/// Find the optimal way to parenthesize matrix chain multiplication to minimize scalar multiplications.
6+
/// The function returns the minimal number of scalar multiplications and the DP table for matrix splits.
7+
///
8+
/// Example:
9+
/// val matrices = Array(Matrix(5, 10), Matrix(10, 3), Matrix(3, 12))
10+
/// val (num, res) = optimalMatrixParentheses(matrices)
11+
/// println(s"Minimal scalar multiplications: $num") // Returns 330
12+
///
13+
/// Time Complexity: O(n³) – Triple nested loop for DP table computation.
514
def optimalMatrixParentheses(lst: Array[Matrix]): (Int, Array[Array[Int]]) = {
615
val length = lst.length
716
val dp: Array[Array[Int]] = Array.fill(length, length)(0)

src/main/scala/algorithms/dynamic/OptimalStops.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ package algorithms.dynamic
22

33
case class Result(route: List[Int], minCost: Int)
44

5+
/// Find the optimal set of stops along a route to minimize the penalty for deviating from 200km/day driving distance.
6+
/// The penalty is based on the square of the difference between the ideal 200km and the actual distance driven each day.
7+
/// The function returns the optimal stops and the minimum penalty cost.
8+
///
9+
/// Example:
10+
/// val hotels = Array(0, 100, 250, 400, 600, 800)
11+
/// val result = findOptimalStops(hotels)
12+
/// println(s"Optimal stops: ${result.route}") // [0, 250, 400,600, 800]
13+
/// println(s"Minimum penalty: ${result.minCost}") // Minimum penalty : 500
14+
///
15+
/// Time Complexity: O(n²) – Nested loops for dynamic programming.
516
def findOptimalStops(a: Array[Int]): Result = {
617
val n = a.length
718
val dp = Array.fill(n)(Int.MaxValue) // Minimum penalty cost up to hotel i
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
package datastructures.basic
22

3-
// Define the Node class for the linked list
3+
/// Define a node for a singly linked list. Each node stores a value and a reference to the next node.
4+
///
5+
/// Time Complexity:
6+
/// - Accessing the value: O(1)
7+
/// - Insertion/Deletion: O(1) at the head (or known position)
8+
49
class LinkedNode[T](val value: T, var next: Option[LinkedNode[T]] = None)

src/main/scala/datastructures/basic/PriorityQueue.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ package datastructures.basic
22

33
import datastructures.heap.MaxHeap
44

5+
/// A priority queue implemented using a max-heap. Supports enqueue, dequeue, peek, size, and isEmpty operations.
6+
///
7+
/// Time Complexity:
8+
/// - Enqueue (insert): O(log n)
9+
/// - Dequeue (extractMax): O(log n)
10+
/// - Peek (peekMax): O(1)
11+
/// - isEmpty: O(1)
12+
/// - Size: O(1)
513
class PriorityQueue[T](implicit ord: Ordering[T]) {
614
private val maxHeap = new MaxHeap[T]()
715

src/main/scala/datastructures/hash/LinearProbingHashTable.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ package datastructures.hash
22

33
import scala.collection.mutable.ArrayBuffer
44

5+
/// A hash table implementation using linear probing for collision resolution.
6+
///
7+
/// Time Complexity:
8+
/// - Insert: O(1) average, O(n) worst-case (when resizing)
9+
/// - Get: O(1) average, O(n) worst-case (due to probing)
10+
/// - Remove: O(1) average, O(n) worst-case (due to probing)
11+
/// - Contains: O(1) average, O(n) worst-case (due to probing)
12+
/// - Resize: O(n) when triggered
513
class LinearProbingHashTable[K, V](initialSize: Int = 16) {
614
private var size = 0
715
private var table: ArrayBuffer[Option[(K, V)]] = ArrayBuffer.fill(initialSize)(None)

src/main/scala/datastructures/heap/MaxHeap.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ package datastructures.heap
22

33
import scala.collection.mutable.ArrayBuffer
44

5+
/// A max-heap implementation. Supports insert, extractMax, peekMax, and heap size operations.
6+
///
7+
/// Time Complexity:
8+
/// - Insert: O(log n)
9+
/// - ExtractMax: O(log n)
10+
/// - PeekMax: O(1)
11+
/// - Size: O(1)
12+
/// - IsEmpty: O(1)
513
class MaxHeap[T](implicit ord: Ordering[T]) {
614
private val heap: ArrayBuffer[T] = ArrayBuffer.empty[T]
715

src/main/scala/datastructures/heap/MinHeap.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ package datastructures.heap
22

33
import scala.collection.mutable.ArrayBuffer
44

5+
/// A min-heap implementation. Supports insert, extractMin, peekMin, key modifications, and heap-building operations.
6+
///
7+
/// Time Complexity:
8+
/// - Insert: O(log n)
9+
/// - ExtractMin: O(log n)
10+
/// - PeekMin: O(1)
11+
/// - BuildHeap: O(n)
12+
/// - IncreaseKey: O(log n)
13+
/// - DecreaseKey: O(log n)
14+
/// - Size: O(1)
15+
/// - IsEmpty: O(1)
516
class MinHeap[T](implicit ord: Ordering[T]) {
617
private val heap: ArrayBuffer[T] = ArrayBuffer.empty[T]
718

src/main/scala/datastructures/tree/AVLTree.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
package datastructures.tree
2+
/// AVL Tree implementation with balancing on insertions.
3+
///
4+
/// Basic Operations:
5+
/// - `search(value)`: O(log n) – Finds a node with the given value.
6+
/// - `insert(newKey)`: O(log n) – Inserts a new key and balances the tree.
7+
/// - Balancing after insertions ensures that the tree remains balanced with height at most O(log n).
8+
///
9+
/// Rotations:
10+
/// - Right and Left Rotations: O(1) – Performed during rebalancing.
11+
112
case class AVLTree[T](key: T, left: Option[AVLTree[T]] = None, right: Option[AVLTree[T]] = None, height: Int = 1) {
213

314
// Get the height of the tree or subtree

src/main/scala/datastructures/tree/BTree.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
package datastructures.tree
2+
3+
/// B-Tree implementation with automatic balancing during insertion.
4+
///
5+
/// Basic Operations:
6+
/// - `search(value)`: O(log n) – Searches for the key in the tree.
7+
/// - `insert(newKey)`: O(log n) – Inserts a new key and ensures the tree remains balanced.
8+
///
9+
/// Insertion:
10+
/// - The tree splits when a node exceeds the maximum number of keys (`maxKeys`), ensuring balanced growth. Each insertion triggers O(log n) rebalancing and potential splits.
11+
///
12+
/// Node Splitting:
13+
/// - When a node exceeds the max number of keys, it splits into two, promoting the middle key to the parent. Splitting happens in O(log n) time.
14+
115
case class BTree[T: Ordering](keys: List[T] = List(), children: List[BTree[T]] = List(), maxKeys: Int = 2) {
216

317
def isLeaf: Boolean = children.isEmpty
Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,74 @@
11
package datastructures.tree
22

3-
class SearchTree
3+
// Node class to represent each element of the tree
4+
case class Node[T](value: T, left: Option[Node[T]] = None, right: Option[Node[T]] = None)
5+
6+
// A simple implementation of a Binary Search Tree (BST)
7+
8+
// Time Complexity of Basic Operations:
9+
// - Insert: O(log n) on average, O(n) in the worst case (unbalanced tree).
10+
// - Search: O(log n) on average, O(n) in the worst case (unbalanced tree).
11+
// - Get Min/Max: O(log n) on average, O(n) in the worst case (unbalanced tree).
12+
13+
class SearchTree[T: Ordering](var root: Option[Node[T]] = None) {
14+
15+
// Insert a new element into the tree
16+
def insert(value: T): Unit = {
17+
root match {
18+
case Some(r) => insertRecursive(r, value)
19+
case None => root = Some(Node(value))
20+
}
21+
}
22+
23+
// Recursive helper function to insert a new value
24+
private def insertRecursive(node: Node[T], value: T): Node[T] = {
25+
if (implicitly[Ordering[T]].lt(value, node.value)) {
26+
node.copy(left = Some(node.left match {
27+
case Some(leftNode) => insertRecursive(leftNode, value)
28+
case None => Node(value)
29+
}))
30+
} else {
31+
node.copy(right = Some(node.right match {
32+
case Some(rightNode) => insertRecursive(rightNode, value)
33+
case None => Node(value)
34+
}))
35+
}
36+
}
37+
38+
// Search for a value in the tree
39+
def search(value: T): Option[T] = searchRecursive(root, value)
40+
41+
// Recursive helper function to search for a value
42+
private def searchRecursive(node: Option[Node[T]], value: T): Option[T] = {
43+
node match {
44+
case Some(n) if n.value == value => Some(n.value)
45+
case Some(n) if implicitly[Ordering[T]].lt(value, n.value) => searchRecursive(n.left, value)
46+
case Some(n) => searchRecursive(n.right, value)
47+
case None => None
48+
}
49+
}
50+
51+
// Get the minimum value in the tree
52+
def getMin: Option[T] = findMin(root)
53+
54+
// Helper function to find the minimum value
55+
private def findMin(node: Option[Node[T]]): Option[T] = {
56+
node match {
57+
case Some(n) if n.left.isEmpty => Some(n.value)
58+
case Some(n) => findMin(n.left)
59+
case None => None
60+
}
61+
}
62+
63+
// Get the maximum value in the tree
64+
def getMax: Option[T] = findMax(root)
65+
66+
// Helper function to find the maximum value
67+
private def findMax(node: Option[Node[T]]): Option[T] = {
68+
node match {
69+
case Some(n) if n.right.isEmpty => Some(n.value)
70+
case Some(n) => findMax(n.right)
71+
case None => None
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)