diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e712304 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +wip diff --git a/2/atsd/README.md b/2/atsd/README.md new file mode 100644 index 0000000..2dd3e14 --- /dev/null +++ b/2/atsd/README.md @@ -0,0 +1 @@ +# Algorithms and Data Structures (Алгоритми та Структури Даних) diff --git a/2/atsd/array_list/.gitignore b/2/atsd/array_list/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/2/atsd/array_list/.gitignore @@ -0,0 +1 @@ +/target diff --git a/2/atsd/array_list/Cargo.lock b/2/atsd/array_list/Cargo.lock new file mode 100644 index 0000000..9cc30c2 --- /dev/null +++ b/2/atsd/array_list/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "array_list" +version = "0.1.0" diff --git a/2/atsd/array_list/Cargo.toml b/2/atsd/array_list/Cargo.toml new file mode 100644 index 0000000..3314e09 --- /dev/null +++ b/2/atsd/array_list/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "array_list" +version = "0.1.0" +edition = "2021" diff --git a/2/atsd/array_list/README.md b/2/atsd/array_list/README.md new file mode 100644 index 0000000..3f60c93 --- /dev/null +++ b/2/atsd/array_list/README.md @@ -0,0 +1,125 @@ +# Array List + +Implements a custom dynamic array (similar to a standard Vector) that manually manages memory allocation and resizing. It includes built-in Binary Heap logic to perform in-place Heapsort and priority-queue operations (like deleting the top element). It also features a utility to visualize the array's internal structure as a printed ASCII tree. + +Execution result: +``` +Data unsorted: [0, 1, 4, 9, 16, 25, 36, 0, 8, 18, 30, 44, 60, 78, 0, 15, 32, 51, 72, 95, 120, 0, 22, 46, 72, 100, 130, 162, 0, 29, 60, 93] + ┌ 93 + ┌ 15 + ┌ 0 + │ └ 32 + ┌ 9 + │ │ ┌ 51 + │ └ 8 + │ └ 72 +┌ 1 +│ │ ┌ 95 +│ │ ┌ 18 +│ │ │ └ 120 +│ └ 16 +│ │ ┌ 0 +│ └ 30 +│ └ 22 +0 +│ ┌ 46 +│ ┌ 44 +│ │ └ 72 +│ ┌ 25 +│ │ │ ┌ 100 +│ │ └ 60 +│ │ └ 130 +└ 4 + │ ┌ 162 + │ ┌ 78 + │ │ └ 0 + └ 36 + │ ┌ 29 + └ 0 + └ 60 + +Data ascending: [0, 0, 0, 0, 0, 1, 4, 8, 9, 15, 16, 18, 22, 25, 29, 30, 32, 36, 44, 46, 51, 60, 60, 72, 72, 78, 93, 95, 100, 120, 130, 162] + ┌ 162 + ┌ 30 + ┌ 8 + │ └ 32 + ┌ 0 + │ │ ┌ 36 + │ └ 9 + │ └ 44 +┌ 0 +│ │ ┌ 46 +│ │ ┌ 15 +│ │ │ └ 51 +│ └ 0 +│ │ ┌ 60 +│ └ 16 +│ └ 60 +0 +│ ┌ 72 +│ ┌ 18 +│ │ └ 72 +│ ┌ 1 +│ │ │ ┌ 78 +│ │ └ 22 +│ │ └ 93 +└ 0 + │ ┌ 95 + │ ┌ 25 + │ │ └ 100 + └ 4 + │ ┌ 120 + └ 29 + └ 130 + +Data descending: [162, 130, 120, 100, 95, 93, 78, 72, 72, 60, 60, 51, 46, 44, 36, 32, 30, 29, 25, 22, 18, 16, 15, 9, 8, 4, 1, 0, 0, 0, 0, 0] + ┌ 0 + ┌ 32 + ┌ 72 + │ └ 30 + ┌ 100 + │ │ ┌ 29 + │ └ 72 + │ └ 25 +┌ 130 +│ │ ┌ 22 +│ │ ┌ 60 +│ │ │ └ 18 +│ └ 95 +│ │ ┌ 16 +│ └ 60 +│ └ 15 +162 +│ ┌ 9 +│ ┌ 51 +│ │ └ 8 +│ ┌ 93 +│ │ │ ┌ 4 +│ │ └ 46 +│ │ └ 1 +└ 120 + │ ┌ 0 + │ ┌ 44 + │ │ └ 0 + └ 78 + │ ┌ 0 + └ 36 + └ 0 + + +Data [1, 12, 9, 5, 6, 10] + ┌ 5 +┌ 12 +│ └ 6 +1 +│ ┌ 10 +└ 9 +Deleting top: Some(12) + ┌ 5 +┌ 6 +│ └ 1 +10 +└ 9 + +Deleting top with one element: None +``` diff --git a/2/atsd/array_list/src/main.rs b/2/atsd/array_list/src/main.rs new file mode 100644 index 0000000..2a25bd2 --- /dev/null +++ b/2/atsd/array_list/src/main.rs @@ -0,0 +1,202 @@ +#[derive(Default)] +struct ArrayList { + length: usize, + allocate: usize, + capacity: usize, + data: Box<[i32]>, +} + +impl ArrayList { + const fn len(&self) -> usize { + self.length + } + + const fn capacity(&self) -> usize { + self.capacity + } + + const fn is_empty(&self) -> bool { + self.length == 0 + } + + const fn is_full(&self) -> bool { + self.length == self.capacity + } +} + +impl ArrayList { + fn alloc(size: usize) -> Box<[i32]> { + vec![0i32; size].into_boxed_slice() + } + + fn change_capacity(&mut self, size: usize) { + self.capacity = size; + let old_data = std::mem::replace(&mut self.data, Self::alloc(size)); + self.data[..self.length].copy_from_slice(&old_data[..self.length]); + } + + pub fn new(allocate: usize) -> Self { + assert!(allocate > 0); + + Self { + allocate, + ..Default::default() + } + } + + pub fn push(&mut self, num: i32) { + if self.is_full() { + self.change_capacity(self.capacity + self.allocate); + } + + self.data[self.length] = num; + self.length += 1; + } + + pub fn pop(&mut self) -> Option { + if self.is_empty() { + return None; + } + + self.length -= 1; + Some(self.data[self.length]) + } + + pub fn shrink(&mut self) { + self.change_capacity(self.length); + } + + pub fn slice(&self) -> &[i32] { + &self.data[..self.length] + } + + fn heapify(&mut self, idx: usize, len: usize, max_heap: bool) { + if idx >= len { + return; + } + + let idxs = [idx, 2 * idx + 1, 2 * idx + 2] + .into_iter() + .filter(|&i| i < len); + + let swap_idx = if max_heap { + idxs.max_by_key(|&i| self.data[i]) + } else { + idxs.min_by_key(|&i| self.data[i]) + } + .unwrap(); + + if swap_idx != idx { + self.data.swap(swap_idx, idx); + self.heapify(swap_idx, len, max_heap); + } + } + + fn make_heap(&mut self, max_heap: bool) { + // parent of last element (last_idx-1)/2 or (len/2)-1 + for idx in (0..self.length / 2).rev() { + self.heapify(idx, self.length, max_heap); + } + } + + pub fn sort(&mut self, ascending: bool) { + self.make_heap(ascending); + + for len in (1..self.length).rev() { + self.data.swap(0, len); + self.heapify(0, len, ascending); + } + } + + pub fn delete_top(&mut self) -> Option { + if self.is_empty() { + return None; + } + if self.length == 1 { + return self.pop(); + } + + self.make_heap(true); + self.length -= 1; + self.data.swap(0, self.length); + self.heapify(0, self.length, true); + + Some(self.data[self.length]) + } + + fn tree_internal(&self, idx: usize, lines: Vec<&str>, dir: i8) { + if 2 * idx + 1 < self.length { + let mut l = lines.clone(); + if dir == 1 { + l.pop(); + l.push(" "); + } + l.push("│"); + self.tree_internal(2 * idx + 1, l, 1); + } + + let mut l = lines.clone(); + l.pop(); + l.push(["└ ", "", "┌ "][(dir + 1) as usize]); + println!("{}{}", l.join(" "), self.data[idx]); + + if 2 * idx + 2 < self.length { + let mut l = lines; + if dir == -1 { + l.pop(); + l.push(" "); + } + l.push("│"); + self.tree_internal(2 * idx + 2, l, -1); + } + } + + pub fn tree(&self) { + self.tree_internal(0, vec![], 0); + } +} + +use std::fmt; +impl fmt::Display for ArrayList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", &self.data[..self.length]) + } +} + +fn main() { + let mut arr = ArrayList::new(10); + for i in 0..32 { + arr.push(i % 7 * i); + } + + println!("Data unsorted: {arr}"); + arr.tree(); + println!(); + + arr.sort(true); + println!("Data ascending: {arr}"); + arr.tree(); + println!(); + + arr.sort(false); + println!("Data descending: {arr}"); + arr.tree(); + println!(); + + let mut arr = ArrayList::new(10); + for i in [1, 12, 9, 5, 6, 10] { + arr.push(i); + } + + println!(); + println!("Data {arr}"); + arr.tree(); + println!("Deleting top: {:?}", arr.delete_top()); + arr.tree(); + + println!(); + println!( + "Deleting top with one element: {:?}", + ArrayList::new(5).delete_top() + ); +} diff --git a/2/atsd/avl/.gitignore b/2/atsd/avl/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/2/atsd/avl/.gitignore @@ -0,0 +1 @@ +/target diff --git a/2/atsd/avl/Cargo.lock b/2/atsd/avl/Cargo.lock new file mode 100644 index 0000000..37db529 --- /dev/null +++ b/2/atsd/avl/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "avl" +version = "0.1.0" diff --git a/2/atsd/avl/Cargo.toml b/2/atsd/avl/Cargo.toml new file mode 100644 index 0000000..99774c7 --- /dev/null +++ b/2/atsd/avl/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "avl" +version = "0.1.0" +edition = "2021" diff --git a/2/atsd/avl/README.md b/2/atsd/avl/README.md new file mode 100644 index 0000000..c6e22bf --- /dev/null +++ b/2/atsd/avl/README.md @@ -0,0 +1,55 @@ +# AVL Tree + +Implements an AVL Tree, which is a self-balancing Binary Search Tree (BST) that ensures logarithmic time complexity for operations. It automatically maintains balance by tracking node heights and performing rotations (left and right) whenever an insertion or deletion causes the tree to become skewed. + +The implementation includes standard operations like search, min/max finding, and removal, along with a recursive utility to visualize the tree structure in the console. + +For this code: +```rs +Node::from_iter(-20..=20).tree(); +``` + +The resulting tree looks like: +``` + ┌ 20 + ┌ 19 + │ └ 18 + ┌ 17 + │ └ 16 + ┌ 15 + │ │ ┌ 14 + │ └ 13 + │ └ 12 +┌ 11 +│ │ ┌ 10 +│ │ ┌ 9 +│ │ │ └ 8 +│ │ ┌ 7 +│ │ │ │ ┌ 6 +│ │ │ └ 5 +│ │ │ └ 4 +│ └ 3 +│ │ ┌ 2 +│ │ ┌ 1 +│ │ │ └ 0 +│ └ -1 +│ │ ┌ -2 +│ └ -3 +│ └ -4 +-5 +│ ┌ -6 +│ ┌ -7 +│ │ └ -8 +│ ┌ -9 +│ │ │ ┌ -10 +│ │ └ -11 +│ │ └ -12 +└ -13 + │ ┌ -14 + │ ┌ -15 + │ │ └ -16 + └ -17 + │ ┌ -18 + └ -19 + └ -20 +``` diff --git a/2/atsd/avl/src/main.rs b/2/atsd/avl/src/main.rs new file mode 100644 index 0000000..7362f3e --- /dev/null +++ b/2/atsd/avl/src/main.rs @@ -0,0 +1,263 @@ +// #![allow(unused)] +use std::cmp::Ordering; + +#[derive(Clone, Default)] +struct Node { + value: i32, + height: usize, + left: Option>, + right: Option>, +} + +// internal +impl Node { + fn height(node: &Option>) -> usize { + match node { + Some(node) => 1 + node.height, + None => 0, + } + } + + fn update_height(&mut self) { + self.height = std::cmp::max(Node::height(&self.left), Node::height(&self.right)); + } + + fn balance_factor(&self) -> isize { + Self::height(&self.left) as isize - Self::height(&self.right) as isize + } + + fn right_rotate(&mut self) { + let mut new_root = *self.left.take().unwrap(); + self.left = new_root.right.take(); + self.update_height(); + self.right = Some(Box::new(std::mem::replace(self, new_root))); + self.update_height(); + } + + fn left_rotate(&mut self) { + let mut new_root = *self.right.take().unwrap(); + self.right = new_root.left.take(); + self.update_height(); + self.left = Some(Box::new(std::mem::replace(self, new_root))); + self.update_height(); + } + + fn rebalance(&mut self) { + let balance_factor = self.balance_factor(); + + if balance_factor > 1 { + let node = self.left.as_mut().unwrap(); + if node.balance_factor() < 0 { + // skewed to the right + node.left_rotate(); + } + self.right_rotate(); + } + + if balance_factor < -1 { + let node = self.right.as_mut().unwrap(); + if node.balance_factor() > 0 { + // skewed to the left + node.right_rotate(); + } + self.left_rotate(); + } + } + + fn tree_internal(&self, lines: Vec<&str>, dir: i8) { + if let Some(ref node) = self.right { + let mut l = lines.clone(); + if dir == 1 { + l.pop(); + l.push(" "); + } + l.push("│"); + node.tree_internal(l, 1); + } + + let mut l = lines.clone(); + l.pop(); + l.push(["└ ", "", "┌ "][(dir + 1) as usize]); + let dbg_info = ""; // format!(" ({})", self.height); + println!("{}{}{}", l.join(" "), self.value, dbg_info); + + if let Some(ref node) = self.left { + let mut l = lines; + if dir == -1 { + l.pop(); + l.push(" "); + } + l.push("│"); + node.tree_internal(l, -1); + } + } +} + +// public +impl Node { + pub fn new(value: i32) -> Self { + Self { + value, + ..Default::default() + } + } + + pub fn from_vec(values: Vec) -> Self { + let mut node = Self::new(values[0]); + for value in values.into_iter().skip(1) { + node.add(value); + } + node + } + + pub fn add(&mut self, val: i32) { + match (val.cmp(&self.value), &mut self.left, &mut self.right) { + (Ordering::Less, opt, _) | (Ordering::Greater, _, opt) => match opt { + Some(node) => node.add(val), + None => *opt = Some(Box::new(Self::new(val))), + }, + _ => return, + }; + + self.update_height(); + self.rebalance(); + } + + pub fn del(mut self, val: i32) -> Option> { + match (val.cmp(&self.value), &mut self.left, &mut self.right) { + (Ordering::Less, opt, _) | (Ordering::Greater, _, opt) => { + if opt.is_some() { + *opt = opt.take().unwrap().del(val); + } + } + (Ordering::Equal, _, Some(node)) => { + self.value = node.min(); + self.right = self.right.take().unwrap().del(self.value); + } + + (Ordering::Equal, Some(_), None) => return self.left, + (Ordering::Equal, None, None) => return None, + }; + + self.update_height(); + self.rebalance(); + Some(Box::new(self)) + } + + pub fn has(&self, val: i32) -> bool { + match (val.cmp(&self.value), &self.left, &self.right) { + (Ordering::Less, Some(n), _) | (Ordering::Greater, _, Some(n)) => n.has(val), + (Ordering::Equal, ..) => true, + _ => false, + } + } + + pub fn min(&self) -> i32 { + match self.left { + Some(ref node) => node.min(), + None => self.value, + } + } + + pub fn max(&self) -> i32 { + match self.right { + Some(ref node) => node.max(), + None => self.value, + } + } + + pub fn tree(&self) { + self.tree_internal(vec![], 0); + } + + pub fn clone_node(&self) -> Self { + Self { + value: self.value, + height: self.height, + left: self.left.as_ref().map(|node| Box::new(node.clone_node())), + right: self.right.as_ref().map(|node| Box::new(node.clone_node())), + } + } +} + +use std::fmt; +impl fmt::Display for Node { + // inorder + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(ref node) = self.left { + write!(f, "{node}")?; + } + write!(f, "{} ", self.value)?; + if let Some(ref node) = self.right { + write!(f, "{node}")?; + } + Ok(()) + } +} + +impl FromIterator for Node { + fn from_iter>(iter: I) -> Self { + let mut iter = iter.into_iter(); + let mut node = Node::new(iter.next().unwrap()); + for value in iter { + node.add(value); + } + node + } +} + +fn main() { + let mut avl = Node::from_vec(vec![20, 4]); + avl.add(15); + avl.tree(); + println!(); + let mut avl = Node::from_vec(vec![20, 4, 26, 3, 9]); + avl.add(15); + avl.tree(); + println!(); + let mut avl = Node::from_vec(vec![20, 4, 26, 3, 9, 21, 30, 2, 7, 11]); + avl.add(15); + avl.tree(); + println!(); + + println!(); + let mut avl = Node::from_vec(vec![20, 4]); + avl.add(8); + avl.tree(); + println!(); + let mut avl = Node::from_vec(vec![20, 4, 26, 3, 9]); + avl.add(8); + avl.tree(); + println!(); + let mut avl = Node::from_vec(vec![20, 4, 26, 3, 9, 21, 30, 2, 7, 11]); + avl.add(8); + avl.tree(); + println!(); + + println!(); + let avl = Node::from_vec(vec![2, 1, 4, 3, 5]); + let avl = avl.del(1).unwrap(); + avl.tree(); + println!(); + let avl = Node::from_vec(vec![6, 2, 9, 1, 4, 8, 11, 3, 5, 7, 10, 12, 13]); + let avl = avl.del(1).unwrap(); + avl.tree(); + println!(); + + let avl = Node::from_vec(vec![5, 2, 8, 1, 3, 7, 10, 4, 6, 11, 12]); + let avl = avl.del(1).unwrap(); + avl.tree(); + println!("{}", avl.has(1)); + println!("{}", avl.has(7)); + println!("{}", avl.min()); + println!("{}", avl.max()); + + let mut avl2 = avl.clone(); + avl2.add(42); + avl2.tree(); + println!(); + println!("{avl}"); + println!("{avl2}"); + + // Node::from_iter(-1000..=1000).tree(); +} diff --git a/2/atsd/exam/.gitignore b/2/atsd/exam/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/2/atsd/exam/.gitignore @@ -0,0 +1 @@ +/target diff --git a/2/atsd/exam/Cargo.lock b/2/atsd/exam/Cargo.lock new file mode 100644 index 0000000..181eb28 --- /dev/null +++ b/2/atsd/exam/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "exam" +version = "0.1.0" diff --git a/2/atsd/exam/Cargo.toml b/2/atsd/exam/Cargo.toml new file mode 100644 index 0000000..7716893 --- /dev/null +++ b/2/atsd/exam/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "exam" +version = "0.1.0" +edition = "2021" diff --git a/2/atsd/exam/README.md b/2/atsd/exam/README.md new file mode 100644 index 0000000..effea43 --- /dev/null +++ b/2/atsd/exam/README.md @@ -0,0 +1,7 @@ +# Exam + +> Algorithm for replacing elements of a one-dimensional list. Implement in pseudocode or your favorite programming language. + +Cooked this bad boy during the exam. + + diff --git a/2/atsd/exam/src/main.rs b/2/atsd/exam/src/main.rs new file mode 100644 index 0000000..638626d --- /dev/null +++ b/2/atsd/exam/src/main.rs @@ -0,0 +1,28 @@ +fn replace(list: &mut [T], find: &T, with: &T, all: bool) { + for element in list.iter_mut() { + if element == find { + *element = with.clone(); + if !all { + return; + } + } + } +} + +fn main() { + let mut list: Vec<_> = (0..10).map(|x| x % 7).collect(); + println!("{list:?}"); + + let find = 2; + let with = 42; + + replace(&mut list, &find, &with, false); + println!("{list:?}"); + + replace(&mut list, &find, &with, true); + println!("{list:?}"); + + let mut list = [1, 2, 3, 4, 1, 3, 1, 0, 4]; + replace(&mut list, &1, &9, true); + println!("{list:?}"); +} diff --git a/2/atsd/graph/.gitignore b/2/atsd/graph/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/2/atsd/graph/.gitignore @@ -0,0 +1 @@ +/target diff --git a/2/atsd/graph/Cargo.lock b/2/atsd/graph/Cargo.lock new file mode 100644 index 0000000..3d6a2ee --- /dev/null +++ b/2/atsd/graph/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "graph" +version = "0.1.0" diff --git a/2/atsd/graph/Cargo.toml b/2/atsd/graph/Cargo.toml new file mode 100644 index 0000000..c702d24 --- /dev/null +++ b/2/atsd/graph/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "graph" +version = "0.1.0" +edition = "2021" diff --git a/2/atsd/graph/README.md b/2/atsd/graph/README.md new file mode 100644 index 0000000..eb5d33d --- /dev/null +++ b/2/atsd/graph/README.md @@ -0,0 +1,67 @@ +# Graph + +Implements a weighted, undirected graph with a minimal CLI to build it. Algorithms like Dijkstra's Shortest Path and Kruskal's Minimum Spanning Tree (MST) are available. + +Execution result: +``` +Exit - 'q' or 'x' +Print the graph - 'p' +Calculate dijkstra to each vertex - 'd vertex' +Remove an edge - 'vertex1 vertex2' +Add an edge - 'vertex1 vertex2 distance' +1 2 6 +Added 1 - 2, 6 +2 4 5 +Added 2 - 4, 5 +4 5 22 +Added 4 - 5, 22 +5 7 10 +Added 5 - 7, 10 +7 6 12 +Added 7 - 6, 12 +6 1 15 +Added 6 - 1, 15 +3 2 11 +Added 3 - 2, 11 +3 4 17 +Added 3 - 4, 17 +3 6 25 +Added 3 - 6, 25 +3 7 12 +Added 3 - 7, 12 +3 7 +Removed 3 - 7 +3 7 9 +Added 3 - 7, 9 +p +1 - 2, 6 +1 - 6, 15 +2 - 3, 11 +2 - 4, 5 +3 - 4, 17 +3 - 6, 25 +3 - 7, 9 +4 - 5, 22 +5 - 7, 10 +6 - 7, 12 +d 1 +{1: None, 2: Some((1, 6)), 3: Some((2, 17)), 4: Some((2, 11)), 5: Some((4, 33)), 6: Some((1, 15)), 7: Some((3, 26))} +q +1 - 2, 6 +1 - 6, 15 +2 - 3, 11 +2 - 4, 5 +3 - 4, 17 +3 - 6, 25 +3 - 7, 9 +4 - 5, 22 +5 - 7, 10 +6 - 7, 12 + +1 - 2, 6 +2 - 3, 11 +2 - 4, 5 +3 - 7, 9 +5 - 7, 10 +6 - 7, 12 +``` diff --git a/2/atsd/graph/src/main.rs b/2/atsd/graph/src/main.rs new file mode 100644 index 0000000..769a271 --- /dev/null +++ b/2/atsd/graph/src/main.rs @@ -0,0 +1,170 @@ +use std::{ + collections::{BTreeMap, BTreeSet}, + io, +}; + +#[derive(Default)] +struct Graph { + vertices: BTreeMap>, +} + +impl Graph { + fn rem_edge(&mut self, v1: usize, v2: usize) { + self.vertices.entry(v1).and_modify(|m| { + m.remove(&v2); + }); + self.vertices.entry(v2).and_modify(|m| { + m.remove(&v1); + }); + } + + fn add_edge(&mut self, v1: usize, v2: usize, distance: u32) { + self.vertices.entry(v1).or_default().insert(v2, distance); + self.vertices.entry(v2).or_default().insert(v1, distance); + } + + pub fn new() -> Self { + let mut graph = Self::default(); + println!( + "Exit - 'q' or 'x' +Print the graph - 'p' +Calculate dijkstra to each vertex - 'd vertex' +Remove an edge - 'vertex1 vertex2' +Add an edge - 'vertex1 vertex2 distance'" + ); + + loop { + let mut input = String::new(); + io::stdin().read_line(&mut input).unwrap(); + let args: Vec<&str> = input.split_whitespace().collect(); + + match args.len() { + 0 => continue, + 1 => match args[0] { + "q" | "x" => break, + "p" => graph.print(), + _ => println!("Can't parse {input}"), + }, + 2 => { + if args[0] == "d" { + println!("{:?}", graph.dijkstra(args[1].parse().unwrap())); + } else { + graph.rem_edge(args[0].parse().unwrap(), args[1].parse().unwrap()); + println!("Removed {} - {}", args[0], args[1]); + } + } + 3 => { + graph.add_edge( + args[0].parse().unwrap(), + args[1].parse().unwrap(), + args[2].parse().unwrap(), + ); + println!("Added {} - {}, {}", args[0], args[1], args[2]); + } + _ => println!("Can't parse {input}"), + } + } + + graph + } + + pub fn print(&self) { + let mut viewed = BTreeSet::new(); + + for (v1, vertices) in &self.vertices { + for (v2, distance) in vertices { + if viewed.contains(&(v1, v2)) || viewed.contains(&(v2, v1)) { + continue; + } + + println!("{v1} - {v2}, {distance}"); + viewed.insert((v1, v2)); + } + } + } + + // find a representative for a given element + fn find_repr(reprs: &BTreeMap, element: usize) -> usize { + if reprs[&element] == element { + return element; + } + + Self::find_repr(reprs, reprs[&element]) + } + + pub fn kruskal(&self) -> Self { + let mut kruskal_graph = Self::default(); + + let mut edges = BTreeSet::new(); + let mut reprs = BTreeMap::new(); + + for (v1, vertices) in &self.vertices { + for (v2, distance) in vertices { + if !edges.contains(&(*v2, *v1, *distance)) { + edges.insert((*v1, *v2, *distance)); + } + } + reprs.insert(*v1, *v1); + } + + let mut edges: Vec<_> = edges.into_iter().collect(); + edges.sort_by_key(|k| k.2); + + let mut count = 0; + for (v1, v2, dist) in edges { + if count + 1 >= self.vertices.len() { + break; + } + + let parent1 = Self::find_repr(&reprs, v1); + let parent2 = Self::find_repr(&reprs, v2); + + if parent1 != parent2 { + count += 1; + kruskal_graph.add_edge(v1, v2, dist); + reprs.insert(parent2, parent1); + } + } + + kruskal_graph + } + + pub fn dijkstra(&self, start: usize) -> BTreeMap> { + let mut visited = BTreeMap::new(); // map of vertices and predecessor with distance + let mut fifo = BTreeMap::new(); // first in first out buffer of vertex and distance + + visited.insert(start, None); // doesn't have a predecessor + + // adding vertices from start + for (vertex, distance) in &self.vertices[&start] { + visited.insert(*vertex, Some((start, *distance))); + fifo.insert(*vertex, *distance); + } + + while let Some((vertex, distance)) = fifo.pop_first() { + for (next, next_distance) in &self.vertices[&vertex] { + let new_distance = distance + next_distance; + match visited.get(next) { + // skip if new distance >= current distance for vertex + Some(Some((_, current_distance))) if new_distance >= *current_distance => {} + // vertex is a start + Some(None) => {} + // new distance is shorter or next was not in distances + _ => { + visited.insert(*next, Some((vertex, new_distance))); + fifo.insert(*next, new_distance); + } + } + } + } + + visited + } +} + +fn main() { + let graph = Graph::new(); + graph.print(); + println!(); + graph.kruskal().print(); +} diff --git a/2/atsd/sorted_linked_list/.gitignore b/2/atsd/sorted_linked_list/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/2/atsd/sorted_linked_list/.gitignore @@ -0,0 +1 @@ +/target diff --git a/2/atsd/sorted_linked_list/Cargo.lock b/2/atsd/sorted_linked_list/Cargo.lock new file mode 100644 index 0000000..3b99c1d --- /dev/null +++ b/2/atsd/sorted_linked_list/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "sorted_linked_list" +version = "0.1.0" diff --git a/2/atsd/sorted_linked_list/Cargo.toml b/2/atsd/sorted_linked_list/Cargo.toml new file mode 100644 index 0000000..03a094f --- /dev/null +++ b/2/atsd/sorted_linked_list/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "sorted_linked_list" +version = "0.1.0" +edition = "2021" diff --git a/2/atsd/sorted_linked_list/README.md b/2/atsd/sorted_linked_list/README.md new file mode 100644 index 0000000..9d72f3d --- /dev/null +++ b/2/atsd/sorted_linked_list/README.md @@ -0,0 +1,19 @@ +# Sorted Linked List + +> [!NOTE] +> This one implements tests, so `cargo test` + +This code implements a **generic**, sorted Singly Linked List that relies on Rust's `Rc` and `RefCell` smart pointers for shared ownership and interior mutability. Unlike a standard push-append list, the `add` method automatically inserts new elements into their correct sorted position based on their value, ensuring the list remains ordered at all times. It is fully unit-tested and supports operations like removing specific values, popping by index, and deep equality checks. + +Test results: +```sh +running 8 tests +test tests::add ... ok +test tests::custom_type ... ok +test tests::equal ... ok +test tests::get ... ok +test tests::pop ... ok +test tests::print ... ok +test tests::remove ... ok +test tests::remove_all ... ok +``` diff --git a/2/atsd/sorted_linked_list/src/lib.rs b/2/atsd/sorted_linked_list/src/lib.rs new file mode 100644 index 0000000..8d57a67 --- /dev/null +++ b/2/atsd/sorted_linked_list/src/lib.rs @@ -0,0 +1,288 @@ +use std::cell::RefCell; +use std::rc::Rc; + +pub use List::Nil; +pub use List::Node; + +#[derive(PartialEq, Debug)] +pub enum List { + Nil, + Node(T, Rc>>), +} + +impl List { + pub fn new(val: T, next: Self) -> Self { + Node(val, Rc::new(RefCell::new(next))) + } +} + +impl List { + pub fn unref(node: &RefCell) -> Self { + if let Node(val, next) = &*node.borrow() { + return Node(val.clone(), Rc::clone(next)); + } + Nil + } + + pub fn unpack(node: &RefCell) -> Option<(T, Rc>)> { + if let Node(val, next) = &*node.borrow() { + return Some((val.clone(), Rc::clone(next))); + } + None + } +} + +impl List { + pub fn print(&self) { + print!("["); + + if let Node(val, next) = self { + print!("{}", val); + let mut ptr = Rc::clone(next); + + while let Some((val, next)) = List::unpack(&ptr) { + print!(", {}", val); + ptr = next; + } + } + + println!("]"); + } +} + +impl List { + pub fn add(self, new_val: T) -> Self { + let head = Rc::new(RefCell::new(self)); + let mut ptr = Rc::clone(&head); + + while let Some((val, next)) = List::unpack(&ptr) { + if val < new_val { + ptr = next; + continue; + } + + *ptr.borrow_mut() = List::new(new_val, Node(val, next)); + return Self::unref(&head); + } + + if Self::unref(&ptr) == Nil { + *ptr.borrow_mut() = List::new(new_val, Nil); + } + + Self::unref(&head) + } + + pub fn remove(self, rem_val: T, all: bool) -> Self { + let head = Rc::new(RefCell::new(self)); + let mut ptr = Rc::clone(&head); + + while let Some((val, next)) = List::unpack(&ptr) { + if val > rem_val { + break; + } + if val != rem_val { + ptr = next; + continue; + } + + *ptr.borrow_mut() = List::unref(&next); + if !all { + break; + } + } + + Self::unref(&head) + } + + pub fn pop(&mut self, index: usize) -> Option { + if let Node(val, next) = self { + if index == 0 { + let res = val.clone(); + *self = Self::unref(next); + return Some(res); + } + + let mut idx = 1; + let mut ptr = Rc::clone(next); + + while let Some((val, next)) = List::unpack(&ptr) { + if idx < index { + idx += 1; + ptr = next; + continue; + } + + *ptr.borrow_mut() = List::unref(&next); + return Some(val); + } + } + + None + } + + pub fn get(&self, index: usize) -> Option { + if let Node(val, next) = self { + if index == 0 { + return Some(val.clone()); + } + + let mut idx = 0; + let mut ptr = Rc::clone(next); + + while let Some((val, next)) = List::unpack(&ptr) { + idx += 1; + ptr = next; + if idx == index { + return Some(val); + } + } + } + + None + } +} + +impl List +where + T: Clone + std::fmt::Display, +{ + pub fn equal(&self, other: &Self) -> bool { + let mut s1 = "[".to_string(); + if let Node(val, next) = self { + s1 += &val.to_string(); + let mut ptr = Rc::clone(next); + + while let Some((val, next)) = List::unpack(&ptr) { + s1 += &format!(", {val}"); + ptr = next; + } + } + s1 += &"]".to_string(); + + let mut s2 = "[".to_string(); + if let Node(val, next) = other { + s2 += &val.to_string(); + let mut ptr = Rc::clone(next); + + while let Some((val, next)) = List::unpack(&ptr) { + s2 += &format!(", {val}"); + ptr = next; + } + } + s2 += &"]".to_string(); + + println!("{s1}"); + println!("{s2}"); + + s1 == s2 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn equal() { + let list1 = Nil.add(1).add(3).add(2).add(5).add(42).add(7); + let list2 = Nil.add(1).add(2).add(3).add(7).add(42).add(5); + let list3 = Nil.add(3).add(0); + + assert!(list1.equal(&list2)); + println!(); + assert!(!list1.equal(&list3)); + println!(); + assert!(!list1.equal(&Nil)); + } + + #[test] + fn print() { + let list = List::new(3, Nil).add(1).add(5).add(2).add(4); + list.print(); + } + + #[test] + fn get() { + let list = List::new(1, List::new(2, List::new(4, Nil))); + assert_eq!(list.get(2), Some(4)); + assert_eq!(list.get(0), Some(1)); + assert_eq!(list.get(1), Some(2)); + assert_eq!(list.get(10), None); + } + + #[test] + fn add() { + let mut list = List::new(2, Nil); + let mut desired = List::new(1, List::new(2, List::new(4, Nil))); + list = list.add(3).add(1).add(2).add(4); + desired = desired.add(3).add(2); + + assert_eq!(list, desired); + } + + #[test] + fn pop() { + let mut list = Nil.add(2).add(2).add(4).add(3).add(1); + assert_eq!(list.pop(0), Some(1)); + assert_eq!(list, Nil.add(2).add(2).add(4).add(3)); + assert_eq!(list.pop(2), Some(3)); + assert_eq!(list, Nil.add(2).add(2).add(4)); + assert_eq!(list.pop(100), None); + } + + #[test] + fn remove() { + let mut list = Nil.add(2).add(2).add(4).add(3).add(1); + let desired = List::new(2, List::new(4, Nil)); + + list = list.remove(2, false).remove(3, false).remove(1, false); + + assert_eq!(list, desired); + } + + #[test] + fn remove_all() { + let mut list = Nil.add(2).add(4).add(3).add(1); + let desired = List::new(4, Nil); + + list = list.remove(2, true).remove(3, true).remove(1, true); + + assert_eq!(list, desired); + } + + #[test] + fn custom_type() { + #[derive(Debug, Clone, PartialEq)] + struct MyObj { + id: u8, + name: String, + } + + impl MyObj { + fn new(id: u8, name: &str) -> Self { + let name = name.into(); + MyObj { id, name } + } + } + + impl PartialOrd for MyObj { + fn partial_cmp(&self, other: &Self) -> Option { + Some(other.id.cmp(&self.id)) + } + } + + let mut list = List::new( + MyObj::new(1, "Alice"), + List::new(MyObj::new(3, "Bob"), List::new(MyObj::new(5, "Jane"), Nil)), + ) + .add(MyObj::new(2, "Tom")) + .add(MyObj::new(1, "Mike")) + .add(MyObj::new(4, "Kelvin")) + .remove(MyObj::new(5, "Jane"), false) + .remove(MyObj::new(1, "Mike"), true) + .remove(MyObj::new(10, "Mike"), true); + + assert_eq!(list.pop(10), None); + assert_eq!(list.pop(0), Some(MyObj::new(4, "Kelvin"))); + } +} diff --git a/2/atsd/sorted_linked_list/src/main.rs b/2/atsd/sorted_linked_list/src/main.rs new file mode 100644 index 0000000..09a2419 --- /dev/null +++ b/2/atsd/sorted_linked_list/src/main.rs @@ -0,0 +1,19 @@ +use sorted_linked_list::*; + +// Write a method ListEqual() to test if the calling List L1 and the argument +// linked lists L2 are equal (i.e., have the same values in the same order). +// Print both L1 and L2. + +fn main() { + let list1 = Nil.add(1).add(3).add(2).add(5).add(42).add(7); + let list2 = Nil.add(1).add(2).add(3).add(7).add(42).add(5); + let list3 = List::new(1, List::new(2, List::new(3, Nil))); + + assert!(list1.equal(&list2)); + println!(); + assert!(!list1.equal(&list3)); + println!(); + assert!(!list1.equal(&Nil)); + println!(); + assert!(Nil::.equal(&Nil)); +} diff --git a/2/atsd/unsorted_linked_list/.gitignore b/2/atsd/unsorted_linked_list/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/2/atsd/unsorted_linked_list/.gitignore @@ -0,0 +1 @@ +/target diff --git a/2/atsd/unsorted_linked_list/Cargo.lock b/2/atsd/unsorted_linked_list/Cargo.lock new file mode 100644 index 0000000..950b161 --- /dev/null +++ b/2/atsd/unsorted_linked_list/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "unsorted_linked_list" +version = "0.1.0" diff --git a/2/atsd/unsorted_linked_list/Cargo.toml b/2/atsd/unsorted_linked_list/Cargo.toml new file mode 100644 index 0000000..6965dc5 --- /dev/null +++ b/2/atsd/unsorted_linked_list/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "unsorted_linked_list" +version = "0.1.0" +edition = "2021" diff --git a/2/atsd/unsorted_linked_list/README.md b/2/atsd/unsorted_linked_list/README.md new file mode 100644 index 0000000..1801e52 --- /dev/null +++ b/2/atsd/unsorted_linked_list/README.md @@ -0,0 +1,11 @@ +# Unsorted Linked List + +implements a basic Singly Linked List that supports appending elements to the end and printing the contents. It features a Bubble Sort algorithm that sorts the list by swapping the integer values stored in the nodes, rather than rewiring the next pointers. + +Execution result: +``` +List: [42, 1, 37, 69, 9, 96, 12, 36] +Sorted: [1, 9, 12, 36, 37, 42, 69, 96] +Add 36: [1, 9, 12, 36, 37, 42, 69, 96, 36] +Sorted: [1, 9, 12, 36, 36, 37, 42, 69, 96] +``` diff --git a/2/atsd/unsorted_linked_list/src/main.rs b/2/atsd/unsorted_linked_list/src/main.rs new file mode 100644 index 0000000..03f6432 --- /dev/null +++ b/2/atsd/unsorted_linked_list/src/main.rs @@ -0,0 +1,88 @@ +struct List { + val: i32, + next: Option>, +} + +impl List { + pub const fn new(val: i32) -> Self { + Self { val, next: None } + } + + pub fn print(&self) { + let mut ptr = self; + + print!("[{}", ptr.val); + while let Some(ref next) = ptr.next { + ptr = next; + print!(", {}", ptr.val); + } + println!("]"); + } + + pub fn add(&mut self, val: i32) { + let mut ptr = self; + + while let Some(ref mut next) = ptr.next { + ptr = next; + } + ptr.next = Some(Box::new(Self::new(val))); + } + + pub fn bubblesort(&mut self) { + let mut len = 1; // there is already 1 element in the list + let mut sorted = false; + let mut ptr = &mut *self; + + // find the length + while let Some(next) = &mut ptr.next { + if ptr.val > next.val { + std::mem::swap(&mut ptr.val, &mut next.val); + } + + len += 1; + ptr = next; + } + + while !sorted { + sorted = true; + + ptr = &mut *self; + // len-2 because looped 1 time while getting len, and checking next element from current + for _ in 0..len - 2 { + let next = ptr.next.as_mut().unwrap(); + if ptr.val > next.val { + std::mem::swap(&mut ptr.val, &mut next.val); + sorted = false; + } + + ptr = next; + } + len -= 1; + } + } +} + +fn main() { + let mut list = List::new(42); + list.add(1); + list.add(37); + list.add(69); + list.add(9); + list.add(96); + list.add(12); + list.add(36); + print!("List: "); + list.print(); + + list.bubblesort(); + print!("Sorted: "); + list.print(); + + list.add(36); + print!("Add 36: "); + list.print(); + + list.bubblesort(); + print!("Sorted: "); + list.print(); +}