dsa_rust/tgg/tgg_05.rs
1// This module didn't make the cut: Suppres unused code warnings
2#![allow(dead_code)]
3
4// Ch 5: Recursion
5//////////////////
6
7// This factorial function is defined with u32 which a) cannot take
8// negative values by default and b) has a max return of
9// 4,294,967,295, or 12! because 13! is 6,227,020,800.
10// If you bump it up to u128 you can calculate up to 34!.
11/** Computs a n! up to 12 in O(n) time */
12pub fn factorial_0(mut n: u32) -> u32 {
13 if n <= 1 {
14 return n;
15 } else {
16 let nex = factorial_0(n - 1);
17 n *= nex;
18 };
19 n
20}
21// Translated from the book's Java example in O(n) time
22// Note that this version uses a signed integer primitive so the function
23// also includes a bounds check for values < 0.
24// Clippy wants a match statement tho
25//pub fn factorial_1(n: i32) -> i32 {
26// //use std::cmp::Ordering;
27// //match n.cmp(&0) {
28// // Ordering::Equal => return 1,
29// // Ordering::Less => {
30// // println!("Error: Cannot compute factorials for n < 0");
31// // return n;
32// // },
33// // _ => return n * factorial_1(n - 1),
34// //}
35// if n < 0 {
36// println!("Error: Cannot compute factorials for n < 0");
37// return n;
38// } else if n == 0 {
39// return 1;
40// } else {
41// return n * factorial_1(n - 1);
42// }
43//}
44// After analyzing the book's example (cheating)
45/** Recursive implementation of a factorial calculator up to 12! in O(n) time */
46pub fn factorial_2(n: u32) -> u32 {
47 if n <= 1 {
48 return n;
49 }
50 n * factorial_2(n - 1)
51}
52// Refactoring the recursive function for an iterative one
53/** Iterative implementation of a factorial calculator up to 12! in O(n) time */
54pub fn factorial_3(mut n: u32) -> u32 {
55 let mut fac = n;
56 while n > 1 {
57 fac *= n - 1;
58 n -= 1;
59 }
60 fac
61}
62// Reimplementation as an iterative function for a more logical evaluation (cheat)
63/** Iterative implementation of a factorial calculator up to 12! in O(n) time */
64pub fn factorial_4(n: u32) -> u32 {
65 let mut fac = 1;
66 for e in 2..=n {
67 fac *= e
68 }
69 fac
70}
71
72/** Recursive implementation of a binary search in O(log n) time.
73 * Returns the index of the target within a given array, if present.
74 * Otherwise the function returns -1. */
75pub fn bin_search_0(a: &Vec<i32>, t: i32, left: i32, right: i32) -> i32 {
76 use std::cmp::Ordering;
77
78 // Recursive base case
79 if left > right {
80 -1
81 } else {
82 let mid = (left + right) / 2;
83 //if t == a[mid as usize] {
84 // return mid as i32;
85 //} else if t < a[mid as usize] {
86 // return bin_search_0(&a, t, left, mid - 1);
87 //} else {
88 // return bin_search_0(&a, t, mid + 1, right);
89 //}
90 // clippy wants match block
91 match t.cmp(&a[mid as usize]) {
92 Ordering::Equal => mid,
93 Ordering::Less => bin_search_0(a, t, left, mid - 1),
94 Ordering::Greater => bin_search_0(a, t, mid + 1, right),
95 }
96 }
97}
98pub fn bin_search_0_wrapper(a: &Vec<i32>, target: i32) -> i32 {
99 let right = a.clone().len() as i32 - 1;
100 bin_search_0(a, target, 0, right)
101}
102// The recursive binary search algorithm represents tail recursion.
103// Even though Rust (likely) reimplements this automatically this is done
104// manually as a fun exercise.
105/** Iterative reimplementation of a recursive binary search algorithm in O(log n) time.
106 * This algorithm takes a referenced vec and returns the index of the target, if it exists.
107 * Otherwise it returns -1 to indicate that the target is not in the vec. */
108pub fn bin_search_1(data: &[i32], target: i32) -> i32 {
109 let mut low = 0;
110 let mut high = data.len() - 1;
111 while low <= high {
112 let mid: usize = (low + high) / 2;
113 //if target == data[mid] {
114 // return mid as i32;
115 //} else if target < data[mid] {
116 // high = mid - 1
117 //} else {
118 // low = mid + 1
119 //}
120 // clippy wants a match block tho
121 use std::cmp::Ordering;
122 match target.cmp(&data[mid]) {
123 Ordering::Equal => return mid as i32,
124 Ordering::Less => high = mid - 1,
125 Ordering::Greater => low = mid + 1,
126 }
127 }
128 -1
129}
130#[test]
131pub fn bin_search_test() {
132 let v = vec![12, 26, 31, 48, 52, 61, 75, 80, 93];
133 assert_eq!(2, bin_search_0_wrapper(&v, 31));
134 assert_eq!(6, bin_search_1(&v, 75));
135}
136
137// Initially it appears this algorithm runs in O(n^2) time, but it actually
138// runs in O(n) time because it touches (and performs O(1) operations) on
139// n nodes in the tree exactly once. This algorithm represents multiple recursion
140// because for each invocation there are x number of directory nodes to sum.
141/** Walks a directory tree printing out names and sizes in O(n) time */
142use std::path::Path;
143pub fn disk_usage(root: &Path) -> u64 {
144 let mut dir_size = 0;
145 if root.is_dir() {
146 for e in root.read_dir().expect("read_dir call failed") {
147 let entry = e.expect("failure to deconstruct value");
148 dir_size += disk_usage(&entry.path());
149 //if let Ok(entry) = e {
150 // dir_size += disk_usage(&entry.path());
151 //}
152 }
153 let this_dir = std::fs::metadata(root)
154 .expect("metadata call failed [0]")
155 .len();
156 //dir_size += this_dir;
157 println!("d {:>7}B {}", dir_size + this_dir, root.display());
158 } else if root.is_file() {
159 let size = std::fs::metadata(root)
160 .expect("metadata call failed [1]")
161 .len();
162 println!(" {:>7}B {}", size, root.display());
163 return size;
164 }
165 dir_size
166}
167
168// Sum of array of integers to n indexes in O(n) time using linear recursion
169// Iterative implementation (so easy, so intuitive)
170pub fn array_sum_0(v: &[i32]) -> i32 {
171 let mut sum = 0;
172 //for e in 0..v.len() {
173 // sum += v[e]
174 //}
175 for (i, _) in v.iter().enumerate() {
176 sum += v[i]
177 }
178 sum
179}
180// Reimplementation using an iterator instead of a range
181pub fn array_sum_1(v: Vec<i32>) -> i32 {
182 let mut sum = 0;
183 for e in v.iter() {
184 sum += v[*e as usize]
185 }
186 sum
187}
188// Recursive implementation (is dumb)
189pub fn array_sum_2(data: &Vec<i32>, n: usize) -> i32 {
190 if n == 0 {
191 data[0]
192 } else {
193 array_sum_2(data, n - 1) + data[n]
194 }
195}
196// Sum of an array of integers to n in O(n) time using O(log n) space with binary recursion
197pub fn array_sum_4(data: Vec<i32>) -> i32 {
198 let h = data.clone().len() - 1;
199 fn array_sum_3(data: Vec<i32>, low: usize, high: usize) -> i32 {
200 //if low > high {
201 // 0
202 //} else if low == high {
203 // return data[low];
204 //} else {
205 // let mid = (low + high) / 2;
206 // return array_sum_3(data.clone(), low, mid) + array_sum_3(data, mid + 1, high);
207 //}
208 // clippy wants a match block tho
209 use std::cmp::Ordering;
210 match low.cmp(&high) {
211 Ordering::Equal => data[low],
212 Ordering::Greater => 0,
213 Ordering::Less => {
214 let mid = (low + high) / 2;
215 array_sum_3(data.clone(), low, mid) + array_sum_3(data, mid + 1, high)
216 }
217 }
218 }
219 array_sum_3(data, 0, h)
220}
221// Represents a public interface for the unsightly recursive implementation in array_sum_3()
222//pub fn array_sum_4(data: Vec<i32>) -> i32 {
223// let h = data.clone().len() - 1;
224// return array_sum_3(data, 0, h)
225//}
226#[test]
227pub fn array_sum_test() {
228 // Iterative impelmentation test
229 let t = [6, 7, 8, 9];
230 assert_eq!(30, array_sum_0(&t));
231 // Recursive implementation test
232 // Dumb, error prone implementation
233 let t = vec![6, 7, 8, 9];
234 assert_eq!(30, array_sum_2(&t, 3));
235
236 // Binary recursive implementation test
237 let t = vec![6, 7, 8, 9];
238 assert_eq!(30, array_sum_4(t));
239}
240
241// Iteratively reverses the elements of an array
242/** Iteratively reverses the elements of an array in O(n) time */
243pub fn array_reversal_0(mut v: Vec<i32>) -> Vec<i32> {
244 let mut high = v.len() - 1; // Match the index values, not # of elements
245 let mut low = 0;
246 let mut temp;
247 println!("Iterative approach\nOriginal: {v:?}");
248 while high > low {
249 temp = v[low];
250 v[low] = v[high];
251 v[high] = temp;
252 high -= 1;
253 low += 1;
254 }
255 println!("Reversed: {v:?}");
256 v
257}
258// Linear recursion to reverse the elements of an array in place
259// (with liberal type conversion)
260pub fn array_reversal_1(v: &mut Vec<i32>, low: i32, high: i32) -> &mut Vec<i32> {
261 if low < high {
262 //let temp = v[low as usize];
263 //v[low as usize] = v[high as usize];
264 //v[high as usize] = temp;
265 v.swap(low as usize, high as usize);
266 array_reversal_1(v, low + 1, high - 1);
267 }
268 v
269}
270// Recursive reimplementation but everything is usize
271pub fn array_reversal_2(v: &mut Vec<usize>, low: usize, high: usize) -> &mut Vec<usize> {
272 if low < high {
273 //let temp = v[low];
274 //v[low] = v[high];
275 //v[high] = temp;
276 v.swap(low, high);
277 array_reversal_2(v, low + 1, high - 1);
278 }
279 v
280}
281#[test]
282pub fn array_reversal_test() {
283 // Tests the iterative approach
284 let v = vec![1, 2, 3, 4, 5, 6, 7, 8];
285 let rev = vec![8, 7, 6, 5, 4, 3, 2, 1];
286 assert_eq!(array_reversal_0(v), rev);
287
288 // Tests the dumb recursive approach
289 let mut v = vec![11, 22, 33, 44, 55, 66, 77, 88];
290 let rev = vec![88, 77, 66, 55, 44, 33, 22, 11];
291 let high = v.len() as i32 - 1;
292 array_reversal_1(&mut v, 0, high);
293 assert_eq!(v, rev)
294}
295
296// Computing powers
297// First attempt uses iteration
298pub fn powers_0(x: u32, n: u32) -> u32 {
299 let mut product = 1;
300 for _ in 1..=n {
301 product *= x;
302 }
303 product
304}
305// My attempt at linear recursion used to compute powers
306pub fn powers_1(x: u32, mut product: u32, n: u32) -> u32 {
307 if n > 1 {
308 product *= x;
309 return powers_1(x, product, n - 1);
310 }
311 product
312}
313// Transling the book's Java example that runs in O(n) time
314pub fn powers_2(x: u32, n: u32) -> u32 {
315 if n > 1 {
316 return x * powers_2(x, n - 1);
317 }
318 x
319}
320// Reimplementation using binary recursion that computes powers in O(log n) time
321// Relies on truncation in integer division; if n is even the result is x^n, and
322// if n is even the result is x^(n-1) * x
323pub fn powers_3(x: u32, n: u32) -> u32 {
324 if n == 0 {
325 1
326 } else {
327 let partial = powers_3(x, n / 2); // Relies on truncation
328 let mut result = partial * partial;
329 if n % 2 == 1 {
330 result *= x
331 }
332 result
333 }
334}
335#[test]
336pub fn powers_test() {
337 assert_eq!(128, powers_0(2, 7));
338 assert_eq!(0, powers_0(0, 7));
339 assert_eq!(1, powers_0(1, 7));
340 assert_eq!(128, powers_1(2, 2, 7));
341 assert_eq!(0, powers_1(0, 0, 7));
342 assert_eq!(1, powers_1(1, 1, 7));
343 assert_eq!(256, powers_3(2, 8));
344 assert_eq!(128, powers_3(2, 7));
345 assert_eq!(0, powers_3(0, 7));
346 assert_eq!(1, powers_3(1, 7));
347 assert_eq!(19683, powers_3(3, 9));
348}
349
350// Another stab at Fibonacci sequence
351pub fn fib_0(n: i32) -> Vec<i32> {
352 let mut seq = Vec::new();
353 let mut first = 0;
354 let mut next = 1;
355 let mut this: i32;
356 seq.push(first);
357 seq.push(next);
358 for _ in 2..n {
359 this = first + next;
360 seq.push(this);
361 first = next;
362 next = this;
363 }
364 seq
365}
366
367// EXTRA CREDIT
368///////////////
369
370// The internet's version of the recursive solution
371/** A recursive implementation of the Tower of Hanoi solution that
372 * runs in O(2^n) time. This algorithm works by breaking the
373 * problem set into source, auxiliary, and destination pegs. */
374pub fn tower_of_hanoi(n: u32, src: char, dest: char, aux: char) {
375 if n == 1 {
376 println!("Move disk 1 from peg {src} to peg {dest}");
377 return;
378 }
379 tower_of_hanoi(n - 1, src, aux, dest);
380 println!("Move disk {n} from peg {src} to peg {dest}"); // Trace
381 tower_of_hanoi(n - 1, aux, dest, src);
382}