diff --git a/2023_11_16/longest-increasing-subsequence/Cargo.toml b/2023_11_16/longest-increasing-subsequence/Cargo.toml new file mode 100644 index 0000000..e9e7de9 --- /dev/null +++ b/2023_11_16/longest-increasing-subsequence/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "longest-increasing-subsequence" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/2023_11_16/longest-increasing-subsequence/README.md b/2023_11_16/longest-increasing-subsequence/README.md new file mode 100644 index 0000000..6fefdb8 --- /dev/null +++ b/2023_11_16/longest-increasing-subsequence/README.md @@ -0,0 +1,47 @@ +# Longest Increasing Subsequence + +Let's have a look at how this algorithm for finding the longest increasing subsequence works: + +```rust +impl Solution { + pub fn length_of_lis(nums: Vec) -> i32 { + let mut ans: Vec = Vec::new(); + ans.push(nums[0]); + + for &num in nums[1..].iter() { + if num > *ans.last().unwrap() { + ans.push(num); + } else { + let mut low = 0; + let mut high = ans.len() - 1; + while low < high { + let mid = low + (high - low) / 2; + if ans[mid] < num { + low = mid + 1; + } else { + high = mid; + } + } + ans[low] = num; + } + } + ans.len() as i32 + } +} +``` + +* It initializes an empty vector `ans` and pushes the first element of the input vector into it. + +* It then iterates over the rest of the input vector. For each number: + - If the number is greater than the last number in `ans`, it pushes the number into `ans`. + - If the number is not greater, it performs a binary search in `ans` to find the first number that is not less than the current number and replaces it with the current number. This is done using a while loop that adjusts the `low` and `high` indices until `low` is no longer less than `high`. The loop invariant is that `ans[low]` is the first number in `ans` that is not less than the current number. The loop terminates when `low` and `high` are equal, and `low` is the index of the first number in `ans` that is not less than the current number. The current number is then inserted into `ans` at index `low`, replacing the existing number which is larger. +* Finally, it returns the length of `ans` as the length of the longest increasing subsequence. + +This algorithm works because `ans` always contains the smallest tail elements for all increasing subsequences of the same length. When a new number comes in, if it is larger than all tail elements, it extends the longest increasing subsequence. If it is not, it can potentially become a tail element of an increasing subsequence of a certain length, replacing the existing larger one. + +### Complexity Analysis + +* Time complexity : $O(n \log n)$. Binary search takes $\log n$ time and it is called $$n$$ times. +* Space complexity : $O(n)$. The size of `ans` can grow up to $n$. + +![](https://i.imgur.com/koJfK3t.png) diff --git a/2023_11_16/longest-increasing-subsequence/src/main.rs b/2023_11_16/longest-increasing-subsequence/src/main.rs new file mode 100644 index 0000000..488b932 --- /dev/null +++ b/2023_11_16/longest-increasing-subsequence/src/main.rs @@ -0,0 +1,31 @@ +impl Solution { + pub fn length_of_lis(nums: Vec) -> i32 { + let mut ans: Vec = Vec::new(); + ans.push(nums[0]); + + for &num in nums[1..].iter() { + if num > *ans.last().unwrap() { + ans.push(num); + } else { + let mut low = 0; + let mut high = ans.len() - 1; + while low < high { + let mid = low + (high - low) / 2; + if ans[mid] < num { + low = mid + 1; + } else { + high = mid; + } + } + ans[low] = num; + } + } + ans.len() as i32 + } +} + +struct Solution; + +fn main() { + assert_eq!(Solution::length_of_lis(vec![10, 9, 2, 5, 3, 7, 101, 18]), 4); +}