diff --git a/leetcode/src/lib.rs b/leetcode/src/lib.rs index 694972c..8c8087e 100644 --- a/leetcode/src/lib.rs +++ b/leetcode/src/lib.rs @@ -1,4 +1,5 @@ //! Collections of problems and solutions in LeetCode +mod list; pub mod problems; pub mod solutions; diff --git a/leetcode/src/list.rs b/leetcode/src/list.rs new file mode 100644 index 0000000..e0bbb16 --- /dev/null +++ b/leetcode/src/list.rs @@ -0,0 +1,95 @@ +// Definition for singly-linked list. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct ListNode { + pub val: T, + pub next: Option>>, +} + +impl ListNode { + #[inline] + pub fn new(val: T) -> Self { + ListNode { next: None, val } + } + + pub fn create_list(v: [T; N]) -> Option> { + let mut head = None; + + for item in v.into_iter().rev() { + head = Some(Box::new(ListNode { + val: item, + next: head, + })); + } + + head + } +} + +pub fn assert_eq_list( + l1: &Option>>, + l2: &Option>>, +) -> bool { + let mut p1 = l1; + let mut p2 = l2; + while let (Some(node1), Some(node2)) = (p1, p2) { + if node1.val != node2.val { + return false; + } + p1 = &node1.next; + p2 = &node2.next; + } + + p1.is_none() && p2.is_none() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_list() { + let v = [1, 3, 2]; + let list = ListNode::create_list(v); + + let mut head = &list; + let mut v = Vec::from(v); + while let Some(node) = head { + let item = v.remove(0); + assert_eq!(item, node.val); + head = &node.next; + } + } + + #[test] + fn test_assert_eq_list() { + let l1 = Some(Box::new(ListNode { + val: 0, + next: Some(Box::new(ListNode { + val: 1, + next: Some(Box::new(ListNode { val: 2, next: None })), + })), + })); + let l2 = Some(Box::new(ListNode { + val: 0, + next: Some(Box::new(ListNode { + val: 1, + next: Some(Box::new(ListNode { val: 2, next: None })), + })), + })); + let l3 = Some(Box::new(ListNode { + val: 0, + next: Some(Box::new(ListNode { + val: 1, + next: Some(Box::new(ListNode { val: 3, next: None })), + })), + })); + let l4 = Some(Box::new(ListNode { + val: 0, + next: Some(Box::new(ListNode { val: 1, next: None })), + })); + + assert!(assert_eq_list(&l1, &l2)); + assert!(!assert_eq_list(&l1, &l3)); + assert!(!assert_eq_list(&l1, &l4)); + } +} diff --git a/leetcode/src/problems.rs b/leetcode/src/problems.rs index c27ebd3..f4d6577 100644 --- a/leetcode/src/problems.rs +++ b/leetcode/src/problems.rs @@ -1,3 +1,4 @@ +pub mod add_two_numbers; pub mod two_sum; #[allow(dead_code)] diff --git a/leetcode/src/problems/add_two_numbers.rs b/leetcode/src/problems/add_two_numbers.rs new file mode 100644 index 0000000..f6a97c3 --- /dev/null +++ b/leetcode/src/problems/add_two_numbers.rs @@ -0,0 +1,28 @@ +use super::{Difficulty, Problem, Topic}; + +pub struct AddTwoNumbers; + +impl Problem for AddTwoNumbers { + fn id(&self) -> usize { + 2 + } + fn difficulty(&self) -> Difficulty { + Difficulty::Medium + } + fn topic(&self) -> Topic { + Topic::Algorithms + } + fn title(&self) -> String { + "Add Two Numbers".into() + } + fn description(&self) -> String { + r#"You are given two non-empty linked lists representing two non-negative integers. +The digits are stored in reverse order, and each of their nodes contains a single digit. +Add the two numbers and return the sum as a linked list. +You may assume the two numbers do not contain any leading zero, except the number 0 itself."# + .into() + } + fn labels(&self) -> Vec { + ["link list".into()].into() + } +} diff --git a/leetcode/src/solutions.rs b/leetcode/src/solutions.rs index 19e4766..bed0d07 100644 --- a/leetcode/src/solutions.rs +++ b/leetcode/src/solutions.rs @@ -1,5 +1,6 @@ use anyhow::Result; +pub mod add_two_numbers; pub mod two_sum; pub trait Solution: Send + Sync { diff --git a/leetcode/src/solutions/add_two_numbers.rs b/leetcode/src/solutions/add_two_numbers.rs new file mode 100644 index 0000000..3a7ebb4 --- /dev/null +++ b/leetcode/src/solutions/add_two_numbers.rs @@ -0,0 +1,93 @@ +use super::Solution; +use crate::list::{assert_eq_list, ListNode}; + +pub struct AddTwoNumbers; + +impl Solution for AddTwoNumbers { + fn name(&self) -> String { + "Add Two Numbers Solution".into() + } + fn problem_id(&self) -> usize { + 2 + } + fn test(&self) -> anyhow::Result<()> { + let testcases = [ + ( + ListNode::create_list([2, 4, 3]), + ListNode::create_list([5, 6, 4]), + ListNode::create_list([7, 0, 8]), + ), + ( + ListNode::create_list([0]), + ListNode::create_list([0]), + ListNode::create_list([0]), + ), + ( + ListNode::create_list([9, 9, 9, 9, 9, 9, 9]), + ListNode::create_list([9, 9, 9, 9]), + ListNode::create_list([8, 9, 9, 9, 0, 0, 0, 1]), + ), + ]; + + for (l1, l2, expect) in testcases { + let output = Self::add_two_numbers(l1, l2); + + if !assert_eq_list(&expect, &output) { + // TODO: print link list + anyhow::bail!("test failed in AddTwoNumbers solution"); + } + } + Ok(()) + } + fn benchmark(&self) -> anyhow::Result { + anyhow::bail!("TODO"); + } +} + +impl AddTwoNumbers { + pub fn add_two_numbers( + mut l1: Option>>, + mut l2: Option>>, + ) -> Option>> { + let mut head = None; + let mut p = &mut head; + let mut residue = 0; + + while let (Some(node1), Some(node2)) = (l1.as_ref(), l2.as_ref()) { + let val = (node1.val + node2.val + residue) % 10; + residue = (node1.val + node2.val + residue) / 10; + + *p = Some(Box::new(ListNode::new(val))); + p = &mut p.as_mut().unwrap().next; + + l1 = l1.take().unwrap().next; + l2 = l2.take().unwrap().next; + } + + while let Some(node) = l1.take() { + let val = (node.val + residue) % 10; + residue = (node.val + residue) / 10; + + *p = Some(Box::new(ListNode::new(val))); + p = &mut p.as_mut().unwrap().next; + + l1 = node.next; + } + + while let Some(node) = l2.take() { + let val = (node.val + residue) % 10; + residue = (node.val + residue) / 10; + + *p = Some(Box::new(ListNode::new(val))); + p = &mut p.as_mut().unwrap().next; + + l2 = node.next; + } + + if residue > 0 { + *p = Some(Box::new(ListNode::new(residue))); + } + + head + } +} diff --git a/runtime/rust-toolchain b/runtime/rust-toolchain new file mode 100644 index 0000000..bf867e0 --- /dev/null +++ b/runtime/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/runtime/rustfmt.toml b/runtime/rustfmt.toml new file mode 100644 index 0000000..b4040e3 --- /dev/null +++ b/runtime/rustfmt.toml @@ -0,0 +1,9 @@ +error_on_line_overflow = false +format_strings = true +normalize_comments = true +imports_granularity = "Crate" +reorder_modules = true +# enum_discrim_align_threshold = 20 +use_try_shorthand = true +wrap_comments = true +edition = "2021" \ No newline at end of file diff --git a/runtime/src/container.rs b/runtime/src/container.rs index 7b1aca4..ef062a7 100644 --- a/runtime/src/container.rs +++ b/runtime/src/container.rs @@ -1,8 +1,9 @@ -use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; -use leetcode::problems::Problem; -use leetcode::solutions::Solution; +use leetcode::{problems::Problem, solutions::Solution}; #[derive(Default)] pub struct Container { diff --git a/runtime/src/registration.rs b/runtime/src/registration.rs index 1a51042..87cd570 100644 --- a/runtime/src/registration.rs +++ b/runtime/src/registration.rs @@ -1,11 +1,13 @@ use super::container::ContainerHandle; -use leetcode::problems; -use leetcode::solutions; +use leetcode::{problems, solutions}; pub fn register_all(handle: ContainerHandle) -> anyhow::Result<()> { handle.register_problem(|_| problems::two_sum::TwoSum)?; handle.register_solution(|_| solutions::two_sum::TwoSum)?; + handle.register_problem(|_| problems::add_two_numbers::AddTwoNumbers)?; + handle.register_solution(|_| solutions::add_two_numbers::AddTwoNumbers)?; + Ok(()) }