diff --git a/.gitignore b/book/.gitignore similarity index 100% rename from .gitignore rename to book/.gitignore diff --git a/assets/CNAME b/book/assets/CNAME similarity index 100% rename from assets/CNAME rename to book/assets/CNAME diff --git a/assets/baidu_verify_code-vTMwnHyCKW.html b/book/assets/baidu_verify_code-vTMwnHyCKW.html similarity index 100% rename from assets/baidu_verify_code-vTMwnHyCKW.html rename to book/assets/baidu_verify_code-vTMwnHyCKW.html diff --git a/assets/ferris.css b/book/assets/ferris.css similarity index 100% rename from assets/ferris.css rename to book/assets/ferris.css diff --git a/assets/ferris.js b/book/assets/ferris.js similarity index 100% rename from assets/ferris.js rename to book/assets/ferris.js diff --git a/assets/sitemap.xml b/book/assets/sitemap.xml similarity index 100% rename from assets/sitemap.xml rename to book/assets/sitemap.xml diff --git a/assets/theme/2018-edition.css b/book/assets/theme/2018-edition.css similarity index 100% rename from assets/theme/2018-edition.css rename to book/assets/theme/2018-edition.css diff --git a/book.toml b/book/book.toml similarity index 94% rename from book.toml rename to book/book.toml index d4264d1a..5b0691e9 100644 --- a/book.toml +++ b/book/book.toml @@ -2,7 +2,7 @@ authors = ["sunface"] language = "zh-CN" title = "Rust语言圣经(Rust教程 Rust Course)" -src = "course-book/contents" +src = "contents" [output.html] additional-css = ["assets/ferris.css", "assets/theme/2018-edition.css"] diff --git a/course-book/contents/SUMMARY.md b/book/contents/SUMMARY.md similarity index 99% rename from course-book/contents/SUMMARY.md rename to book/contents/SUMMARY.md index 47a6fca9..e9d893cf 100644 --- a/course-book/contents/SUMMARY.md +++ b/book/contents/SUMMARY.md @@ -117,7 +117,21 @@ - [自定义错误](errors/user-define.md) - [让错误输出更优雅](errors/pretty-format.md) - [会导致panic的代码](errors/panic-codes.md) - + + +- [Cargo详解 todo](cargo/intro.md) + - [常用命令](cargo/commands.md) + - [项目结构](cargo/layout.md) + - [Cargo.toml和Cargo.lock](cargo/cargo-toml-lock.md) + - [依赖管理](cargo/dependency.md) + - [构建缓存](cargo/cache.md) + - [版本管理](cargo/version.md) + - [工作空间](cargo/workspace.md) + - [条件编译、条件依赖](cargo/feature.md) + - [配置参数(todo)](cargo/manifest.md) + - [自定义构建脚本](cargo/build-js.md) + - [Cargo profile](cargo/profile.md) + - [测试 todo](test/intro.md) - [单元测试](test/unit.md) - [集成测试](test/intergration.md) @@ -214,19 +228,6 @@ - [命令行解析](libraries/command/intro.md) - [structopt(todo)](libraries/command/structopt.md) -- [Cargo详解 todo](cargo/intro.md) - - [常用命令](cargo/commands.md) - - [项目结构](cargo/layout.md) - - [Cargo.toml和Cargo.lock](cargo/cargo-toml-lock.md) - - [依赖管理](cargo/dependency.md) - - [构建缓存](cargo/cache.md) - - [版本管理](cargo/version.md) - - [工作空间](cargo/workspace.md) - - [条件编译、条件依赖](cargo/feature.md) - - [配置参数(todo)](cargo/manifest.md) - - [自定义构建脚本](cargo/build-js.md) - - [Cargo profile](cargo/profile.md) - ## 附录 - [附录](appendix/intro.md) - [A-关键字](appendix/keywords.md) diff --git a/course-book/contents/about-book.md b/book/contents/about-book.md similarity index 100% rename from course-book/contents/about-book.md rename to book/contents/about-book.md diff --git a/course-book/contents/advance/comment.md b/book/contents/advance/comment.md similarity index 100% rename from course-book/contents/advance/comment.md rename to book/contents/advance/comment.md diff --git a/course-book/contents/advance/crate-module/crate.md b/book/contents/advance/crate-module/crate.md similarity index 100% rename from course-book/contents/advance/crate-module/crate.md rename to book/contents/advance/crate-module/crate.md diff --git a/course-book/contents/advance/crate-module/intro.md b/book/contents/advance/crate-module/intro.md similarity index 100% rename from course-book/contents/advance/crate-module/intro.md rename to book/contents/advance/crate-module/intro.md diff --git a/course-book/contents/advance/crate-module/module.md b/book/contents/advance/crate-module/module.md similarity index 100% rename from course-book/contents/advance/crate-module/module.md rename to book/contents/advance/crate-module/module.md diff --git a/course-book/contents/advance/crate-module/use.md b/book/contents/advance/crate-module/use.md similarity index 100% rename from course-book/contents/advance/crate-module/use.md rename to book/contents/advance/crate-module/use.md diff --git a/course-book/contents/advance/custom-type.md b/book/contents/advance/custom-type.md similarity index 100% rename from course-book/contents/advance/custom-type.md rename to book/contents/advance/custom-type.md diff --git a/course-book/contents/advance/formatted-output.md b/book/contents/advance/formatted-output.md similarity index 100% rename from course-book/contents/advance/formatted-output.md rename to book/contents/advance/formatted-output.md diff --git a/course-book/contents/advance/functional-programing/closure.md b/book/contents/advance/functional-programing/closure.md similarity index 100% rename from course-book/contents/advance/functional-programing/closure.md rename to book/contents/advance/functional-programing/closure.md diff --git a/course-book/contents/advance/functional-programing/intro.md b/book/contents/advance/functional-programing/intro.md similarity index 100% rename from course-book/contents/advance/functional-programing/intro.md rename to book/contents/advance/functional-programing/intro.md diff --git a/course-book/contents/advance/functional-programing/iterator.md b/book/contents/advance/functional-programing/iterator.md similarity index 100% rename from course-book/contents/advance/functional-programing/iterator.md rename to book/contents/advance/functional-programing/iterator.md diff --git a/course-book/contents/advance/global-variable.md b/book/contents/advance/global-variable.md similarity index 100% rename from course-book/contents/advance/global-variable.md rename to book/contents/advance/global-variable.md diff --git a/course-book/contents/advance/hrtb.md b/book/contents/advance/hrtb.md similarity index 100% rename from course-book/contents/advance/hrtb.md rename to book/contents/advance/hrtb.md diff --git a/course-book/contents/advance/intro.md b/book/contents/advance/intro.md similarity index 100% rename from course-book/contents/advance/intro.md rename to book/contents/advance/intro.md diff --git a/course-book/contents/advance/lifetime/advance.md b/book/contents/advance/lifetime/advance.md similarity index 100% rename from course-book/contents/advance/lifetime/advance.md rename to book/contents/advance/lifetime/advance.md diff --git a/course-book/contents/advance/lifetime/basic.md b/book/contents/advance/lifetime/basic.md similarity index 100% rename from course-book/contents/advance/lifetime/basic.md rename to book/contents/advance/lifetime/basic.md diff --git a/course-book/contents/advance/lifetime/intro.md b/book/contents/advance/lifetime/intro.md similarity index 100% rename from course-book/contents/advance/lifetime/intro.md rename to book/contents/advance/lifetime/intro.md diff --git a/course-book/contents/advance/multi-threads/intro.md b/book/contents/advance/multi-threads/intro.md similarity index 100% rename from course-book/contents/advance/multi-threads/intro.md rename to book/contents/advance/multi-threads/intro.md diff --git a/course-book/contents/advance/multi-threads/message-passing.md b/book/contents/advance/multi-threads/message-passing.md similarity index 100% rename from course-book/contents/advance/multi-threads/message-passing.md rename to book/contents/advance/multi-threads/message-passing.md diff --git a/course-book/contents/advance/multi-threads/races.md b/book/contents/advance/multi-threads/races.md similarity index 100% rename from course-book/contents/advance/multi-threads/races.md rename to book/contents/advance/multi-threads/races.md diff --git a/course-book/contents/advance/multi-threads/ref-counter-lock.md b/book/contents/advance/multi-threads/ref-counter-lock.md similarity index 100% rename from course-book/contents/advance/multi-threads/ref-counter-lock.md rename to book/contents/advance/multi-threads/ref-counter-lock.md diff --git a/course-book/contents/advance/multi-threads/send-sync.md b/book/contents/advance/multi-threads/send-sync.md similarity index 100% rename from course-book/contents/advance/multi-threads/send-sync.md rename to book/contents/advance/multi-threads/send-sync.md diff --git a/course-book/contents/advance/multi-threads/thread.md b/book/contents/advance/multi-threads/thread.md similarity index 100% rename from course-book/contents/advance/multi-threads/thread.md rename to book/contents/advance/multi-threads/thread.md diff --git a/course-book/contents/advance/self-referential.md b/book/contents/advance/self-referential.md similarity index 68% rename from course-book/contents/advance/self-referential.md rename to book/contents/advance/self-referential.md index f08145f4..2ef01931 100644 --- a/course-book/contents/advance/self-referential.md +++ b/book/contents/advance/self-referential.md @@ -1,7 +1,67 @@ ## 结构体自引用 结构体自引用在Rust中是一个众所周知的难题,而且众说纷纭,也没有一篇文章能把相关的话题讲透,那本文就王婆卖瓜,来试试看能不能讲透这一块儿内容,让读者大大们舒心。 -> 这章内容足足花了半个月的时间来准备! - 来自作者的诉苦 +## 平平无奇的自引用 +可能也有不少人第一次听说自引用结构体,那咱们先来看看它们长啥样。 + +```rust +struct RefWithinMe<'a> { + value: String, + + // 该引用指向上面的value + pointer_to_value: &'a str, +} +``` +以上就是一个很简单的自引用结构体,看上去好像没什么,那来试着运行下: +```rust +fn main(){ + let s = "aaa".to_string(); + let v = SelfRef { + value: s, + pointer_to_value: &s + }; +} +``` + +运行后报错: +```console + let v = SelfRef { +12 | value: s, + | - value moved here +13 | pointer_to_value: &s + | ^^ value borrowed here after move +``` + +因为我们试图同时使用值和值的引用,最终所有权转移和借用一起发生了。所以,这个问题貌似并没有那么好解决,不信你可以回想下自己具有的知识,是否可以解决? + +#### 使用ouroboros + +## 玉树临风的自引用 +```rust +use std::str; + +struct MyStruct<'a>{ + Buf: Vec, + repr: Parsed<'a> +} + +struct Parsed<'a>{ + name:&'a str +} + +fn main(){ + + let v = vec!(0065,0066,0067,0068,0069); + let s = str::from_utf8(&v).unwrap(); + println!("{}",s); + let p = &v[1..=3]; + let s1 = str::from_utf8(p).unwrap(); + println!("{}",s1); + let par = Parsed{name:s1}; + + let new1 = MyStruct{Buf:v,repr:par}; +} +``` ## 使用Pin来解决自引用 Pin在后续章节会深入讲解,目前你只需要知道它可以固定住一个值,防止该值的所有权被转移。通过Pin也可以实现自引用的数据结构: diff --git a/course-book/contents/advance/smart-pointer/box.md b/book/contents/advance/smart-pointer/box.md similarity index 100% rename from course-book/contents/advance/smart-pointer/box.md rename to book/contents/advance/smart-pointer/box.md diff --git a/course-book/contents/advance/smart-pointer/cell-refcell.md b/book/contents/advance/smart-pointer/cell-refcell.md similarity index 100% rename from course-book/contents/advance/smart-pointer/cell-refcell.md rename to book/contents/advance/smart-pointer/cell-refcell.md diff --git a/course-book/contents/advance/smart-pointer/circle-reference.md b/book/contents/advance/smart-pointer/circle-reference.md similarity index 100% rename from course-book/contents/advance/smart-pointer/circle-reference.md rename to book/contents/advance/smart-pointer/circle-reference.md diff --git a/course-book/contents/advance/smart-pointer/deref.md b/book/contents/advance/smart-pointer/deref.md similarity index 100% rename from course-book/contents/advance/smart-pointer/deref.md rename to book/contents/advance/smart-pointer/deref.md diff --git a/course-book/contents/advance/smart-pointer/drop.md b/book/contents/advance/smart-pointer/drop.md similarity index 100% rename from course-book/contents/advance/smart-pointer/drop.md rename to book/contents/advance/smart-pointer/drop.md diff --git a/course-book/contents/advance/smart-pointer/intro.md b/book/contents/advance/smart-pointer/intro.md similarity index 100% rename from course-book/contents/advance/smart-pointer/intro.md rename to book/contents/advance/smart-pointer/intro.md diff --git a/course-book/contents/advance/smart-pointer/rc-arc.md b/book/contents/advance/smart-pointer/rc-arc.md similarity index 100% rename from course-book/contents/advance/smart-pointer/rc-arc.md rename to book/contents/advance/smart-pointer/rc-arc.md diff --git a/course-book/contents/appendix/derive.md b/book/contents/appendix/derive.md similarity index 100% rename from course-book/contents/appendix/derive.md rename to book/contents/appendix/derive.md diff --git a/course-book/contents/appendix/difficulties.md b/book/contents/appendix/difficulties.md similarity index 100% rename from course-book/contents/appendix/difficulties.md rename to book/contents/appendix/difficulties.md diff --git a/course-book/contents/appendix/expressions.md b/book/contents/appendix/expressions.md similarity index 100% rename from course-book/contents/appendix/expressions.md rename to book/contents/appendix/expressions.md diff --git a/course-book/contents/appendix/intro.md b/book/contents/appendix/intro.md similarity index 100% rename from course-book/contents/appendix/intro.md rename to book/contents/appendix/intro.md diff --git a/course-book/contents/appendix/keywords.md b/book/contents/appendix/keywords.md similarity index 100% rename from course-book/contents/appendix/keywords.md rename to book/contents/appendix/keywords.md diff --git a/course-book/contents/appendix/operators.md b/book/contents/appendix/operators.md similarity index 100% rename from course-book/contents/appendix/operators.md rename to book/contents/appendix/operators.md diff --git a/course-book/contents/appendix/prelude.md b/book/contents/appendix/prelude.md similarity index 100% rename from course-book/contents/appendix/prelude.md rename to book/contents/appendix/prelude.md diff --git a/course-book/contents/appendix/rust-version.md b/book/contents/appendix/rust-version.md similarity index 100% rename from course-book/contents/appendix/rust-version.md rename to book/contents/appendix/rust-version.md diff --git a/course-book/contents/basic/base-type/char-bool.md b/book/contents/basic/base-type/char-bool.md similarity index 100% rename from course-book/contents/basic/base-type/char-bool.md rename to book/contents/basic/base-type/char-bool.md diff --git a/course-book/contents/basic/base-type/function.md b/book/contents/basic/base-type/function.md similarity index 100% rename from course-book/contents/basic/base-type/function.md rename to book/contents/basic/base-type/function.md diff --git a/course-book/contents/basic/base-type/index.md b/book/contents/basic/base-type/index.md similarity index 100% rename from course-book/contents/basic/base-type/index.md rename to book/contents/basic/base-type/index.md diff --git a/course-book/contents/basic/base-type/numbers.md b/book/contents/basic/base-type/numbers.md similarity index 100% rename from course-book/contents/basic/base-type/numbers.md rename to book/contents/basic/base-type/numbers.md diff --git a/course-book/contents/basic/base-type/statement-expression.md b/book/contents/basic/base-type/statement-expression.md similarity index 100% rename from course-book/contents/basic/base-type/statement-expression.md rename to book/contents/basic/base-type/statement-expression.md diff --git a/course-book/contents/basic/collections/hashmap.md b/book/contents/basic/collections/hashmap.md similarity index 100% rename from course-book/contents/basic/collections/hashmap.md rename to book/contents/basic/collections/hashmap.md diff --git a/course-book/contents/basic/collections/intro.md b/book/contents/basic/collections/intro.md similarity index 100% rename from course-book/contents/basic/collections/intro.md rename to book/contents/basic/collections/intro.md diff --git a/course-book/contents/basic/collections/vector.md b/book/contents/basic/collections/vector.md similarity index 100% rename from course-book/contents/basic/collections/vector.md rename to book/contents/basic/collections/vector.md diff --git a/course-book/contents/basic/compound-type/array.md b/book/contents/basic/compound-type/array.md similarity index 100% rename from course-book/contents/basic/compound-type/array.md rename to book/contents/basic/compound-type/array.md diff --git a/course-book/contents/basic/compound-type/enum.md b/book/contents/basic/compound-type/enum.md similarity index 100% rename from course-book/contents/basic/compound-type/enum.md rename to book/contents/basic/compound-type/enum.md diff --git a/course-book/contents/basic/compound-type/intro.md b/book/contents/basic/compound-type/intro.md similarity index 100% rename from course-book/contents/basic/compound-type/intro.md rename to book/contents/basic/compound-type/intro.md diff --git a/course-book/contents/basic/compound-type/string-slice.md b/book/contents/basic/compound-type/string-slice.md similarity index 100% rename from course-book/contents/basic/compound-type/string-slice.md rename to book/contents/basic/compound-type/string-slice.md diff --git a/course-book/contents/basic/compound-type/struct.md b/book/contents/basic/compound-type/struct.md similarity index 100% rename from course-book/contents/basic/compound-type/struct.md rename to book/contents/basic/compound-type/struct.md diff --git a/course-book/contents/basic/compound-type/tuple.md b/book/contents/basic/compound-type/tuple.md similarity index 100% rename from course-book/contents/basic/compound-type/tuple.md rename to book/contents/basic/compound-type/tuple.md diff --git a/course-book/contents/basic/converse.md b/book/contents/basic/converse.md similarity index 100% rename from course-book/contents/basic/converse.md rename to book/contents/basic/converse.md diff --git a/course-book/contents/basic/flow-control.md b/book/contents/basic/flow-control.md similarity index 100% rename from course-book/contents/basic/flow-control.md rename to book/contents/basic/flow-control.md diff --git a/course-book/contents/basic/img/.DS_Store b/book/contents/basic/img/.DS_Store similarity index 100% rename from course-book/contents/basic/img/.DS_Store rename to book/contents/basic/img/.DS_Store diff --git a/course-book/contents/basic/img/ferris/does_not_compile.svg b/book/contents/basic/img/ferris/does_not_compile.svg similarity index 100% rename from course-book/contents/basic/img/ferris/does_not_compile.svg rename to book/contents/basic/img/ferris/does_not_compile.svg diff --git a/course-book/contents/basic/img/ferris/not_desired_behavior.svg b/book/contents/basic/img/ferris/not_desired_behavior.svg similarity index 100% rename from course-book/contents/basic/img/ferris/not_desired_behavior.svg rename to book/contents/basic/img/ferris/not_desired_behavior.svg diff --git a/course-book/contents/basic/img/ferris/panics.svg b/book/contents/basic/img/ferris/panics.svg similarity index 100% rename from course-book/contents/basic/img/ferris/panics.svg rename to book/contents/basic/img/ferris/panics.svg diff --git a/course-book/contents/basic/intro.md b/book/contents/basic/intro.md similarity index 100% rename from course-book/contents/basic/intro.md rename to book/contents/basic/intro.md diff --git a/course-book/contents/basic/match-pattern/all-patterns.md b/book/contents/basic/match-pattern/all-patterns.md similarity index 100% rename from course-book/contents/basic/match-pattern/all-patterns.md rename to book/contents/basic/match-pattern/all-patterns.md diff --git a/course-book/contents/basic/match-pattern/intro.md b/book/contents/basic/match-pattern/intro.md similarity index 100% rename from course-book/contents/basic/match-pattern/intro.md rename to book/contents/basic/match-pattern/intro.md diff --git a/course-book/contents/basic/match-pattern/match-if-let.md b/book/contents/basic/match-pattern/match-if-let.md similarity index 100% rename from course-book/contents/basic/match-pattern/match-if-let.md rename to book/contents/basic/match-pattern/match-if-let.md diff --git a/course-book/contents/basic/match-pattern/option.md b/book/contents/basic/match-pattern/option.md similarity index 100% rename from course-book/contents/basic/match-pattern/option.md rename to book/contents/basic/match-pattern/option.md diff --git a/course-book/contents/basic/match-pattern/pattern-match.md b/book/contents/basic/match-pattern/pattern-match.md similarity index 100% rename from course-book/contents/basic/match-pattern/pattern-match.md rename to book/contents/basic/match-pattern/pattern-match.md diff --git a/course-book/contents/basic/method.md b/book/contents/basic/method.md similarity index 100% rename from course-book/contents/basic/method.md rename to book/contents/basic/method.md diff --git a/course-book/contents/basic/ownership/borrowing.md b/book/contents/basic/ownership/borrowing.md similarity index 100% rename from course-book/contents/basic/ownership/borrowing.md rename to book/contents/basic/ownership/borrowing.md diff --git a/course-book/contents/basic/ownership/index.md b/book/contents/basic/ownership/index.md similarity index 100% rename from course-book/contents/basic/ownership/index.md rename to book/contents/basic/ownership/index.md diff --git a/course-book/contents/basic/ownership/ownership.md b/book/contents/basic/ownership/ownership.md similarity index 100% rename from course-book/contents/basic/ownership/ownership.md rename to book/contents/basic/ownership/ownership.md diff --git a/course-book/contents/basic/result-error/intro.md b/book/contents/basic/result-error/intro.md similarity index 100% rename from course-book/contents/basic/result-error/intro.md rename to book/contents/basic/result-error/intro.md diff --git a/course-book/contents/basic/result-error/panic.md b/book/contents/basic/result-error/panic.md similarity index 100% rename from course-book/contents/basic/result-error/panic.md rename to book/contents/basic/result-error/panic.md diff --git a/course-book/contents/basic/result-error/result.md b/book/contents/basic/result-error/result.md similarity index 100% rename from course-book/contents/basic/result-error/result.md rename to book/contents/basic/result-error/result.md diff --git a/course-book/contents/basic/trait/advance-trait.md b/book/contents/basic/trait/advance-trait.md similarity index 100% rename from course-book/contents/basic/trait/advance-trait.md rename to book/contents/basic/trait/advance-trait.md diff --git a/course-book/contents/basic/trait/generic.md b/book/contents/basic/trait/generic.md similarity index 100% rename from course-book/contents/basic/trait/generic.md rename to book/contents/basic/trait/generic.md diff --git a/course-book/contents/basic/trait/intro.md b/book/contents/basic/trait/intro.md similarity index 100% rename from course-book/contents/basic/trait/intro.md rename to book/contents/basic/trait/intro.md diff --git a/course-book/contents/basic/trait/trait-object.md b/book/contents/basic/trait/trait-object.md similarity index 100% rename from course-book/contents/basic/trait/trait-object.md rename to book/contents/basic/trait/trait-object.md diff --git a/course-book/contents/basic/trait/trait.md b/book/contents/basic/trait/trait.md similarity index 100% rename from course-book/contents/basic/trait/trait.md rename to book/contents/basic/trait/trait.md diff --git a/course-book/contents/basic/variable.md b/book/contents/basic/variable.md similarity index 100% rename from course-book/contents/basic/variable.md rename to book/contents/basic/variable.md diff --git a/course-book/contents/cargo/build-js.md b/book/contents/cargo/build-js.md similarity index 100% rename from course-book/contents/cargo/build-js.md rename to book/contents/cargo/build-js.md diff --git a/course-book/contents/cargo/cache.md b/book/contents/cargo/cache.md similarity index 100% rename from course-book/contents/cargo/cache.md rename to book/contents/cargo/cache.md diff --git a/course-book/contents/cargo/cargo-toml-lock.md b/book/contents/cargo/cargo-toml-lock.md similarity index 100% rename from course-book/contents/cargo/cargo-toml-lock.md rename to book/contents/cargo/cargo-toml-lock.md diff --git a/course-book/contents/cargo/commands.md b/book/contents/cargo/commands.md similarity index 100% rename from course-book/contents/cargo/commands.md rename to book/contents/cargo/commands.md diff --git a/course-book/contents/cargo/dependency.md b/book/contents/cargo/dependency.md similarity index 100% rename from course-book/contents/cargo/dependency.md rename to book/contents/cargo/dependency.md diff --git a/course-book/contents/cargo/feature.md b/book/contents/cargo/feature.md similarity index 100% rename from course-book/contents/cargo/feature.md rename to book/contents/cargo/feature.md diff --git a/course-book/contents/cargo/intro.md b/book/contents/cargo/intro.md similarity index 100% rename from course-book/contents/cargo/intro.md rename to book/contents/cargo/intro.md diff --git a/course-book/contents/cargo/layout.md b/book/contents/cargo/layout.md similarity index 100% rename from course-book/contents/cargo/layout.md rename to book/contents/cargo/layout.md diff --git a/course-book/contents/cargo/manifest.md b/book/contents/cargo/manifest.md similarity index 100% rename from course-book/contents/cargo/manifest.md rename to book/contents/cargo/manifest.md diff --git a/course-book/contents/cargo/profile.md b/book/contents/cargo/profile.md similarity index 100% rename from course-book/contents/cargo/profile.md rename to book/contents/cargo/profile.md diff --git a/course-book/contents/cargo/version.md b/book/contents/cargo/version.md similarity index 100% rename from course-book/contents/cargo/version.md rename to book/contents/cargo/version.md diff --git a/course-book/contents/cargo/workspace.md b/book/contents/cargo/workspace.md similarity index 100% rename from course-book/contents/cargo/workspace.md rename to book/contents/cargo/workspace.md diff --git a/course-book/contents/compiler/attributes.md b/book/contents/compiler/attributes.md similarity index 100% rename from course-book/contents/compiler/attributes.md rename to book/contents/compiler/attributes.md diff --git a/course-book/contents/compiler/intro.md b/book/contents/compiler/intro.md similarity index 100% rename from course-book/contents/compiler/intro.md rename to book/contents/compiler/intro.md diff --git a/course-book/contents/compiler/phantom-data.md b/book/contents/compiler/phantom-data.md similarity index 100% rename from course-book/contents/compiler/phantom-data.md rename to book/contents/compiler/phantom-data.md diff --git a/course-book/contents/compiler/speed-up.md b/book/contents/compiler/speed-up.md similarity index 100% rename from course-book/contents/compiler/speed-up.md rename to book/contents/compiler/speed-up.md diff --git a/course-book/contents/errors/intro.md b/book/contents/errors/intro.md similarity index 100% rename from course-book/contents/errors/intro.md rename to book/contents/errors/intro.md diff --git a/course-book/contents/errors/panic-codes.md b/book/contents/errors/panic-codes.md similarity index 100% rename from course-book/contents/errors/panic-codes.md rename to book/contents/errors/panic-codes.md diff --git a/course-book/contents/errors/pretty-format.md b/book/contents/errors/pretty-format.md similarity index 100% rename from course-book/contents/errors/pretty-format.md rename to book/contents/errors/pretty-format.md diff --git a/course-book/contents/errors/simplify.md b/book/contents/errors/simplify.md similarity index 100% rename from course-book/contents/errors/simplify.md rename to book/contents/errors/simplify.md diff --git a/course-book/contents/errors/user-define.md b/book/contents/errors/user-define.md similarity index 100% rename from course-book/contents/errors/user-define.md rename to book/contents/errors/user-define.md diff --git a/course-book/contents/fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.md b/book/contents/fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.md similarity index 100% rename from course-book/contents/fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.md rename to book/contents/fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.md diff --git a/course-book/contents/fight-with-compiler/borrowing/intro.md b/book/contents/fight-with-compiler/borrowing/intro.md similarity index 100% rename from course-book/contents/fight-with-compiler/borrowing/intro.md rename to book/contents/fight-with-compiler/borrowing/intro.md diff --git a/course-book/contents/fight-with-compiler/borrowing/ref-exist-in-out-fn.md b/book/contents/fight-with-compiler/borrowing/ref-exist-in-out-fn.md similarity index 100% rename from course-book/contents/fight-with-compiler/borrowing/ref-exist-in-out-fn.md rename to book/contents/fight-with-compiler/borrowing/ref-exist-in-out-fn.md diff --git a/course-book/contents/fight-with-compiler/intro.md b/book/contents/fight-with-compiler/intro.md similarity index 100% rename from course-book/contents/fight-with-compiler/intro.md rename to book/contents/fight-with-compiler/intro.md diff --git a/course-book/contents/fight-with-compiler/lifetime/closure-with-static.md b/book/contents/fight-with-compiler/lifetime/closure-with-static.md similarity index 100% rename from course-book/contents/fight-with-compiler/lifetime/closure-with-static.md rename to book/contents/fight-with-compiler/lifetime/closure-with-static.md diff --git a/course-book/contents/fight-with-compiler/lifetime/intro.md b/book/contents/fight-with-compiler/lifetime/intro.md similarity index 100% rename from course-book/contents/fight-with-compiler/lifetime/intro.md rename to book/contents/fight-with-compiler/lifetime/intro.md diff --git a/course-book/contents/fight-with-compiler/lifetime/loop.md b/book/contents/fight-with-compiler/lifetime/loop.md similarity index 100% rename from course-book/contents/fight-with-compiler/lifetime/loop.md rename to book/contents/fight-with-compiler/lifetime/loop.md diff --git a/course-book/contents/fight-with-compiler/lifetime/too-long1.md b/book/contents/fight-with-compiler/lifetime/too-long1.md similarity index 100% rename from course-book/contents/fight-with-compiler/lifetime/too-long1.md rename to book/contents/fight-with-compiler/lifetime/too-long1.md diff --git a/course-book/contents/fight-with-compiler/lifetime/too-long2.md b/book/contents/fight-with-compiler/lifetime/too-long2.md similarity index 100% rename from course-book/contents/fight-with-compiler/lifetime/too-long2.md rename to book/contents/fight-with-compiler/lifetime/too-long2.md diff --git a/course-book/contents/fight-with-compiler/phantom-data.md b/book/contents/fight-with-compiler/phantom-data.md similarity index 100% rename from course-book/contents/fight-with-compiler/phantom-data.md rename to book/contents/fight-with-compiler/phantom-data.md diff --git a/course-book/contents/fight-with-compiler/unconstrained.md b/book/contents/fight-with-compiler/unconstrained.md similarity index 100% rename from course-book/contents/fight-with-compiler/unconstrained.md rename to book/contents/fight-with-compiler/unconstrained.md diff --git a/course-book/contents/first-try/cargo.md b/book/contents/first-try/cargo.md similarity index 100% rename from course-book/contents/first-try/cargo.md rename to book/contents/first-try/cargo.md diff --git a/course-book/contents/first-try/editor.md b/book/contents/first-try/editor.md similarity index 100% rename from course-book/contents/first-try/editor.md rename to book/contents/first-try/editor.md diff --git a/course-book/contents/first-try/hello-world.md b/book/contents/first-try/hello-world.md similarity index 100% rename from course-book/contents/first-try/hello-world.md rename to book/contents/first-try/hello-world.md diff --git a/course-book/contents/first-try/installation.md b/book/contents/first-try/installation.md similarity index 100% rename from course-book/contents/first-try/installation.md rename to book/contents/first-try/installation.md diff --git a/course-book/contents/first-try/intro.md b/book/contents/first-try/intro.md similarity index 100% rename from course-book/contents/first-try/intro.md rename to book/contents/first-try/intro.md diff --git a/course-book/contents/img/.DS_Store b/book/contents/img/.DS_Store similarity index 100% rename from course-book/contents/img/.DS_Store rename to book/contents/img/.DS_Store diff --git a/course-book/contents/img/borrowing-01.svg b/book/contents/img/borrowing-01.svg similarity index 100% rename from course-book/contents/img/borrowing-01.svg rename to book/contents/img/borrowing-01.svg diff --git a/course-book/contents/img/comment-01.png b/book/contents/img/comment-01.png similarity index 100% rename from course-book/contents/img/comment-01.png rename to book/contents/img/comment-01.png diff --git a/course-book/contents/img/comment-02.png b/book/contents/img/comment-02.png similarity index 100% rename from course-book/contents/img/comment-02.png rename to book/contents/img/comment-02.png diff --git a/course-book/contents/img/comment-03.png b/book/contents/img/comment-03.png similarity index 100% rename from course-book/contents/img/comment-03.png rename to book/contents/img/comment-03.png diff --git a/course-book/contents/img/comment-04.png b/book/contents/img/comment-04.png similarity index 100% rename from course-book/contents/img/comment-04.png rename to book/contents/img/comment-04.png diff --git a/course-book/contents/img/comment-05.png b/book/contents/img/comment-05.png similarity index 100% rename from course-book/contents/img/comment-05.png rename to book/contents/img/comment-05.png diff --git a/course-book/contents/img/ferris/does_not_compile.svg b/book/contents/img/ferris/does_not_compile.svg similarity index 100% rename from course-book/contents/img/ferris/does_not_compile.svg rename to book/contents/img/ferris/does_not_compile.svg diff --git a/course-book/contents/img/ferris/not_desired_behavior.svg b/book/contents/img/ferris/not_desired_behavior.svg similarity index 100% rename from course-book/contents/img/ferris/not_desired_behavior.svg rename to book/contents/img/ferris/not_desired_behavior.svg diff --git a/course-book/contents/img/ferris/panics.svg b/book/contents/img/ferris/panics.svg similarity index 100% rename from course-book/contents/img/ferris/panics.svg rename to book/contents/img/ferris/panics.svg diff --git a/course-book/contents/img/function-01.png b/book/contents/img/function-01.png similarity index 100% rename from course-book/contents/img/function-01.png rename to book/contents/img/function-01.png diff --git a/course-book/contents/img/method-01.png b/book/contents/img/method-01.png similarity index 100% rename from course-book/contents/img/method-01.png rename to book/contents/img/method-01.png diff --git a/course-book/contents/img/mingw-w64-download.png b/book/contents/img/mingw-w64-download.png similarity index 100% rename from course-book/contents/img/mingw-w64-download.png rename to book/contents/img/mingw-w64-download.png diff --git a/course-book/contents/img/ownership01.svg b/book/contents/img/ownership01.svg similarity index 100% rename from course-book/contents/img/ownership01.svg rename to book/contents/img/ownership01.svg diff --git a/course-book/contents/img/self-ref-01.png b/book/contents/img/self-ref-01.png similarity index 100% rename from course-book/contents/img/self-ref-01.png rename to book/contents/img/self-ref-01.png diff --git a/course-book/contents/img/self-ref-02.png b/book/contents/img/self-ref-02.png similarity index 100% rename from course-book/contents/img/self-ref-02.png rename to book/contents/img/self-ref-02.png diff --git a/course-book/contents/img/string-01.svg b/book/contents/img/string-01.svg similarity index 100% rename from course-book/contents/img/string-01.svg rename to book/contents/img/string-01.svg diff --git a/course-book/contents/img/struct-01.png b/book/contents/img/struct-01.png similarity index 100% rename from course-book/contents/img/struct-01.png rename to book/contents/img/struct-01.png diff --git a/course-book/contents/into-rust.md b/book/contents/into-rust.md similarity index 100% rename from course-book/contents/into-rust.md rename to book/contents/into-rust.md diff --git a/course-book/contents/libraries/command/intro.md b/book/contents/libraries/command/intro.md similarity index 100% rename from course-book/contents/libraries/command/intro.md rename to book/contents/libraries/command/intro.md diff --git a/course-book/contents/libraries/command/structopt.md b/book/contents/libraries/command/structopt.md similarity index 100% rename from course-book/contents/libraries/command/structopt.md rename to book/contents/libraries/command/structopt.md diff --git a/course-book/contents/libraries/http/intro.md b/book/contents/libraries/http/intro.md similarity index 100% rename from course-book/contents/libraries/http/intro.md rename to book/contents/libraries/http/intro.md diff --git a/course-book/contents/libraries/http/reqwest.md b/book/contents/libraries/http/reqwest.md similarity index 100% rename from course-book/contents/libraries/http/reqwest.md rename to book/contents/libraries/http/reqwest.md diff --git a/course-book/contents/libraries/intro.md b/book/contents/libraries/intro.md similarity index 100% rename from course-book/contents/libraries/intro.md rename to book/contents/libraries/intro.md diff --git a/course-book/contents/libraries/json/intro.md b/book/contents/libraries/json/intro.md similarity index 100% rename from course-book/contents/libraries/json/intro.md rename to book/contents/libraries/json/intro.md diff --git a/course-book/contents/libraries/json/serde.md b/book/contents/libraries/json/serde.md similarity index 100% rename from course-book/contents/libraries/json/serde.md rename to book/contents/libraries/json/serde.md diff --git a/course-book/contents/macro/intro.md b/book/contents/macro/intro.md similarity index 100% rename from course-book/contents/macro/intro.md rename to book/contents/macro/intro.md diff --git a/course-book/contents/macro/procedure-macro.md b/book/contents/macro/procedure-macro.md similarity index 100% rename from course-book/contents/macro/procedure-macro.md rename to book/contents/macro/procedure-macro.md diff --git a/course-book/contents/memory/allocation.md b/book/contents/memory/allocation.md similarity index 100% rename from course-book/contents/memory/allocation.md rename to book/contents/memory/allocation.md diff --git a/course-book/contents/memory/intro.md b/book/contents/memory/intro.md similarity index 100% rename from course-book/contents/memory/intro.md rename to book/contents/memory/intro.md diff --git a/course-book/contents/memory/layout.md b/book/contents/memory/layout.md similarity index 100% rename from course-book/contents/memory/layout.md rename to book/contents/memory/layout.md diff --git a/course-book/contents/memory/pointer-ref.md b/book/contents/memory/pointer-ref.md similarity index 100% rename from course-book/contents/memory/pointer-ref.md rename to book/contents/memory/pointer-ref.md diff --git a/course-book/contents/memory/uninit.md b/book/contents/memory/uninit.md similarity index 100% rename from course-book/contents/memory/uninit.md rename to book/contents/memory/uninit.md diff --git a/course-book/contents/memory/virtual.md b/book/contents/memory/virtual.md similarity index 100% rename from course-book/contents/memory/virtual.md rename to book/contents/memory/virtual.md diff --git a/course-book/contents/monitor/apm.md b/book/contents/monitor/apm.md similarity index 100% rename from course-book/contents/monitor/apm.md rename to book/contents/monitor/apm.md diff --git a/course-book/contents/monitor/intro.md b/book/contents/monitor/intro.md similarity index 100% rename from course-book/contents/monitor/intro.md rename to book/contents/monitor/intro.md diff --git a/course-book/contents/monitor/log.md b/book/contents/monitor/log.md similarity index 100% rename from course-book/contents/monitor/log.md rename to book/contents/monitor/log.md diff --git a/course-book/contents/monitor/observability.md b/book/contents/monitor/observability.md similarity index 100% rename from course-book/contents/monitor/observability.md rename to book/contents/monitor/observability.md diff --git a/course-book/contents/networking/async/async-await.md b/book/contents/networking/async/async-await.md similarity index 100% rename from course-book/contents/networking/async/async-await.md rename to book/contents/networking/async/async-await.md diff --git a/course-book/contents/networking/async/future/future.md b/book/contents/networking/async/future/future.md similarity index 100% rename from course-book/contents/networking/async/future/future.md rename to book/contents/networking/async/future/future.md diff --git a/course-book/contents/networking/async/future/into.md b/book/contents/networking/async/future/into.md similarity index 100% rename from course-book/contents/networking/async/future/into.md rename to book/contents/networking/async/future/into.md diff --git a/course-book/contents/networking/async/future/multi-futures.md b/book/contents/networking/async/future/multi-futures.md similarity index 100% rename from course-book/contents/networking/async/future/multi-futures.md rename to book/contents/networking/async/future/multi-futures.md diff --git a/course-book/contents/networking/async/future/system-io.md b/book/contents/networking/async/future/system-io.md similarity index 100% rename from course-book/contents/networking/async/future/system-io.md rename to book/contents/networking/async/future/system-io.md diff --git a/course-book/contents/networking/async/future/task-excutor.md b/book/contents/networking/async/future/task-excutor.md similarity index 100% rename from course-book/contents/networking/async/future/task-excutor.md rename to book/contents/networking/async/future/task-excutor.md diff --git a/course-book/contents/networking/async/future/task-schedule.md b/book/contents/networking/async/future/task-schedule.md similarity index 100% rename from course-book/contents/networking/async/future/task-schedule.md rename to book/contents/networking/async/future/task-schedule.md diff --git a/course-book/contents/networking/async/future/workarounds.md b/book/contents/networking/async/future/workarounds.md similarity index 100% rename from course-book/contents/networking/async/future/workarounds.md rename to book/contents/networking/async/future/workarounds.md diff --git a/course-book/contents/networking/async/http.md b/book/contents/networking/async/http.md similarity index 100% rename from course-book/contents/networking/async/http.md rename to book/contents/networking/async/http.md diff --git a/course-book/contents/networking/async/intro.md b/book/contents/networking/async/intro.md similarity index 100% rename from course-book/contents/networking/async/intro.md rename to book/contents/networking/async/intro.md diff --git a/course-book/contents/networking/async/pin-unpin.md b/book/contents/networking/async/pin-unpin.md similarity index 100% rename from course-book/contents/networking/async/pin-unpin.md rename to book/contents/networking/async/pin-unpin.md diff --git a/course-book/contents/networking/async/tokio/basic.md b/book/contents/networking/async/tokio/basic.md similarity index 100% rename from course-book/contents/networking/async/tokio/basic.md rename to book/contents/networking/async/tokio/basic.md diff --git a/course-book/contents/networking/async/tokio/intro.md b/book/contents/networking/async/tokio/intro.md similarity index 100% rename from course-book/contents/networking/async/tokio/intro.md rename to book/contents/networking/async/tokio/intro.md diff --git a/course-book/contents/networking/async/tokio/stream.md b/book/contents/networking/async/tokio/stream.md similarity index 100% rename from course-book/contents/networking/async/tokio/stream.md rename to book/contents/networking/async/tokio/stream.md diff --git a/course-book/contents/networking/concurrency-parallelism.md b/book/contents/networking/concurrency-parallelism.md similarity index 100% rename from course-book/contents/networking/concurrency-parallelism.md rename to book/contents/networking/concurrency-parallelism.md diff --git a/course-book/contents/networking/intro.md b/book/contents/networking/intro.md similarity index 100% rename from course-book/contents/networking/intro.md rename to book/contents/networking/intro.md diff --git a/course-book/contents/networking/tcp.md b/book/contents/networking/tcp.md similarity index 100% rename from course-book/contents/networking/tcp.md rename to book/contents/networking/tcp.md diff --git a/course-book/contents/object-oriented/characteristics.md b/book/contents/object-oriented/characteristics.md similarity index 100% rename from course-book/contents/object-oriented/characteristics.md rename to book/contents/object-oriented/characteristics.md diff --git a/course-book/contents/object-oriented/design-pattern.md b/book/contents/object-oriented/design-pattern.md similarity index 100% rename from course-book/contents/object-oriented/design-pattern.md rename to book/contents/object-oriented/design-pattern.md diff --git a/course-book/contents/object-oriented/intro.md b/book/contents/object-oriented/intro.md similarity index 100% rename from course-book/contents/object-oriented/intro.md rename to book/contents/object-oriented/intro.md diff --git a/course-book/contents/object-oriented/trait-object.md b/book/contents/object-oriented/trait-object.md similarity index 100% rename from course-book/contents/object-oriented/trait-object.md rename to book/contents/object-oriented/trait-object.md diff --git a/course-book/contents/performance/benchmark.md b/book/contents/performance/benchmark.md similarity index 100% rename from course-book/contents/performance/benchmark.md rename to book/contents/performance/benchmark.md diff --git a/course-book/contents/performance/calculate.md b/book/contents/performance/calculate.md similarity index 100% rename from course-book/contents/performance/calculate.md rename to book/contents/performance/calculate.md diff --git a/course-book/contents/performance/clone-copy.md b/book/contents/performance/clone-copy.md similarity index 100% rename from course-book/contents/performance/clone-copy.md rename to book/contents/performance/clone-copy.md diff --git a/course-book/contents/performance/cpu-cache.md b/book/contents/performance/cpu-cache.md similarity index 100% rename from course-book/contents/performance/cpu-cache.md rename to book/contents/performance/cpu-cache.md diff --git a/course-book/contents/performance/deep-into-move.md b/book/contents/performance/deep-into-move.md similarity index 100% rename from course-book/contents/performance/deep-into-move.md rename to book/contents/performance/deep-into-move.md diff --git a/course-book/contents/performance/early-optimise.md b/book/contents/performance/early-optimise.md similarity index 100% rename from course-book/contents/performance/early-optimise.md rename to book/contents/performance/early-optimise.md diff --git a/course-book/contents/performance/heap-stack.md b/book/contents/performance/heap-stack.md similarity index 100% rename from course-book/contents/performance/heap-stack.md rename to book/contents/performance/heap-stack.md diff --git a/course-book/contents/performance/intro.md b/book/contents/performance/intro.md similarity index 100% rename from course-book/contents/performance/intro.md rename to book/contents/performance/intro.md diff --git a/course-book/contents/performance/runtime-check.md b/book/contents/performance/runtime-check.md similarity index 100% rename from course-book/contents/performance/runtime-check.md rename to book/contents/performance/runtime-check.md diff --git a/course-book/contents/performance/tools.md b/book/contents/performance/tools.md similarity index 100% rename from course-book/contents/performance/tools.md rename to book/contents/performance/tools.md diff --git a/course-book/contents/pitfalls/arithmetic-overflow.md b/book/contents/pitfalls/arithmetic-overflow.md similarity index 100% rename from course-book/contents/pitfalls/arithmetic-overflow.md rename to book/contents/pitfalls/arithmetic-overflow.md diff --git a/course-book/contents/pitfalls/closure-with-lifetime.md b/book/contents/pitfalls/closure-with-lifetime.md similarity index 100% rename from course-book/contents/pitfalls/closure-with-lifetime.md rename to book/contents/pitfalls/closure-with-lifetime.md diff --git a/course-book/contents/pitfalls/index.md b/book/contents/pitfalls/index.md similarity index 100% rename from course-book/contents/pitfalls/index.md rename to book/contents/pitfalls/index.md diff --git a/course-book/contents/pitfalls/iterator-everywhere.md b/book/contents/pitfalls/iterator-everywhere.md similarity index 100% rename from course-book/contents/pitfalls/iterator-everywhere.md rename to book/contents/pitfalls/iterator-everywhere.md diff --git a/course-book/contents/pitfalls/lazy-iterators.md b/book/contents/pitfalls/lazy-iterators.md similarity index 100% rename from course-book/contents/pitfalls/lazy-iterators.md rename to book/contents/pitfalls/lazy-iterators.md diff --git a/course-book/contents/pitfalls/multiple-mutable-references.md b/book/contents/pitfalls/multiple-mutable-references.md similarity index 100% rename from course-book/contents/pitfalls/multiple-mutable-references.md rename to book/contents/pitfalls/multiple-mutable-references.md diff --git a/course-book/contents/pitfalls/stack-overflow.md b/book/contents/pitfalls/stack-overflow.md similarity index 100% rename from course-book/contents/pitfalls/stack-overflow.md rename to book/contents/pitfalls/stack-overflow.md diff --git a/course-book/contents/pitfalls/the-disabled-mutability.md b/book/contents/pitfalls/the-disabled-mutability.md similarity index 100% rename from course-book/contents/pitfalls/the-disabled-mutability.md rename to book/contents/pitfalls/the-disabled-mutability.md diff --git a/course-book/contents/pitfalls/use-vec-in-for.md b/book/contents/pitfalls/use-vec-in-for.md similarity index 100% rename from course-book/contents/pitfalls/use-vec-in-for.md rename to book/contents/pitfalls/use-vec-in-for.md diff --git a/course-book/contents/pitfalls/weird-ranges.md b/book/contents/pitfalls/weird-ranges.md similarity index 100% rename from course-book/contents/pitfalls/weird-ranges.md rename to book/contents/pitfalls/weird-ranges.md diff --git a/course-book/contents/practice/best-pratice.md b/book/contents/practice/best-pratice.md similarity index 100% rename from course-book/contents/practice/best-pratice.md rename to book/contents/practice/best-pratice.md diff --git a/course-book/contents/practice/coding-tips.md b/book/contents/practice/coding-tips.md similarity index 100% rename from course-book/contents/practice/coding-tips.md rename to book/contents/practice/coding-tips.md diff --git a/course-book/contents/practice/intro.md b/book/contents/practice/intro.md similarity index 100% rename from course-book/contents/practice/intro.md rename to book/contents/practice/intro.md diff --git a/course-book/contents/practice/third-party-libs.md b/book/contents/practice/third-party-libs.md similarity index 100% rename from course-book/contents/practice/third-party-libs.md rename to book/contents/practice/third-party-libs.md diff --git a/course-book/contents/rustjs/deno.md b/book/contents/rustjs/deno.md similarity index 100% rename from course-book/contents/rustjs/deno.md rename to book/contents/rustjs/deno.md diff --git a/course-book/contents/rustjs/intro.md b/book/contents/rustjs/intro.md similarity index 100% rename from course-book/contents/rustjs/intro.md rename to book/contents/rustjs/intro.md diff --git a/course-book/contents/some-thoughts.md b/book/contents/some-thoughts.md similarity index 100% rename from course-book/contents/some-thoughts.md rename to book/contents/some-thoughts.md diff --git a/course-book/contents/std/hashmap.md b/book/contents/std/hashmap.md similarity index 100% rename from course-book/contents/std/hashmap.md rename to book/contents/std/hashmap.md diff --git a/course-book/contents/std/intro.md b/book/contents/std/intro.md similarity index 100% rename from course-book/contents/std/intro.md rename to book/contents/std/intro.md diff --git a/course-book/contents/std/iterator.md b/book/contents/std/iterator.md similarity index 100% rename from course-book/contents/std/iterator.md rename to book/contents/std/iterator.md diff --git a/course-book/contents/std/search.md b/book/contents/std/search.md similarity index 100% rename from course-book/contents/std/search.md rename to book/contents/std/search.md diff --git a/course-book/contents/std/vector.md b/book/contents/std/vector.md similarity index 100% rename from course-book/contents/std/vector.md rename to book/contents/std/vector.md diff --git a/course-book/contents/style-guide/clippy.md b/book/contents/style-guide/clippy.md similarity index 100% rename from course-book/contents/style-guide/clippy.md rename to book/contents/style-guide/clippy.md diff --git a/course-book/contents/style-guide/code.md b/book/contents/style-guide/code.md similarity index 100% rename from course-book/contents/style-guide/code.md rename to book/contents/style-guide/code.md diff --git a/course-book/contents/style-guide/intro.md b/book/contents/style-guide/intro.md similarity index 100% rename from course-book/contents/style-guide/intro.md rename to book/contents/style-guide/intro.md diff --git a/course-book/contents/style-guide/mark.md b/book/contents/style-guide/mark.md similarity index 100% rename from course-book/contents/style-guide/mark.md rename to book/contents/style-guide/mark.md diff --git a/course-book/contents/style-guide/naming.md b/book/contents/style-guide/naming.md similarity index 100% rename from course-book/contents/style-guide/naming.md rename to book/contents/style-guide/naming.md diff --git a/course-book/contents/templates/files/dir.md b/book/contents/templates/files/dir.md similarity index 100% rename from course-book/contents/templates/files/dir.md rename to book/contents/templates/files/dir.md diff --git a/course-book/contents/templates/files/intro.md b/book/contents/templates/files/intro.md similarity index 100% rename from course-book/contents/templates/files/intro.md rename to book/contents/templates/files/intro.md diff --git a/course-book/contents/templates/http/intro.md b/book/contents/templates/http/intro.md similarity index 100% rename from course-book/contents/templates/http/intro.md rename to book/contents/templates/http/intro.md diff --git a/course-book/contents/templates/intro.md b/book/contents/templates/intro.md similarity index 100% rename from course-book/contents/templates/intro.md rename to book/contents/templates/intro.md diff --git a/course-book/contents/test/benchmark.md b/book/contents/test/benchmark.md similarity index 100% rename from course-book/contents/test/benchmark.md rename to book/contents/test/benchmark.md diff --git a/course-book/contents/test/ci.md b/book/contents/test/ci.md similarity index 100% rename from course-book/contents/test/ci.md rename to book/contents/test/ci.md diff --git a/course-book/contents/test/intergration.md b/book/contents/test/intergration.md similarity index 100% rename from course-book/contents/test/intergration.md rename to book/contents/test/intergration.md diff --git a/course-book/contents/test/intro.md b/book/contents/test/intro.md similarity index 100% rename from course-book/contents/test/intro.md rename to book/contents/test/intro.md diff --git a/course-book/contents/test/unit.md b/book/contents/test/unit.md similarity index 100% rename from course-book/contents/test/unit.md rename to book/contents/test/unit.md diff --git a/course-book/contents/traits/as-ref-as-mut.md b/book/contents/traits/as-ref-as-mut.md similarity index 100% rename from course-book/contents/traits/as-ref-as-mut.md rename to book/contents/traits/as-ref-as-mut.md diff --git a/course-book/contents/traits/borrow-family.md b/book/contents/traits/borrow-family.md similarity index 100% rename from course-book/contents/traits/borrow-family.md rename to book/contents/traits/borrow-family.md diff --git a/course-book/contents/traits/cow.md b/book/contents/traits/cow.md similarity index 100% rename from course-book/contents/traits/cow.md rename to book/contents/traits/cow.md diff --git a/course-book/contents/traits/deref.md b/book/contents/traits/deref.md similarity index 100% rename from course-book/contents/traits/deref.md rename to book/contents/traits/deref.md diff --git a/course-book/contents/traits/eq.md b/book/contents/traits/eq.md similarity index 100% rename from course-book/contents/traits/eq.md rename to book/contents/traits/eq.md diff --git a/course-book/contents/traits/from-into.md b/book/contents/traits/from-into.md similarity index 100% rename from course-book/contents/traits/from-into.md rename to book/contents/traits/from-into.md diff --git a/course-book/contents/traits/intro.md b/book/contents/traits/intro.md similarity index 100% rename from course-book/contents/traits/intro.md rename to book/contents/traits/intro.md diff --git a/course-book/contents/unsafe/ffi.md b/book/contents/unsafe/ffi.md similarity index 100% rename from course-book/contents/unsafe/ffi.md rename to book/contents/unsafe/ffi.md diff --git a/course-book/contents/unsafe/intro.md b/book/contents/unsafe/intro.md similarity index 100% rename from course-book/contents/unsafe/intro.md rename to book/contents/unsafe/intro.md diff --git a/course-book/contents/unsafe/modify-global-var.md b/book/contents/unsafe/modify-global-var.md similarity index 100% rename from course-book/contents/unsafe/modify-global-var.md rename to book/contents/unsafe/modify-global-var.md diff --git a/course-book/contents/unsafe/raw-pointer.md b/book/contents/unsafe/raw-pointer.md similarity index 100% rename from course-book/contents/unsafe/raw-pointer.md rename to book/contents/unsafe/raw-pointer.md diff --git a/course-book/contents/unsafe/ub.md b/book/contents/unsafe/ub.md similarity index 100% rename from course-book/contents/unsafe/ub.md rename to book/contents/unsafe/ub.md diff --git a/course-book/contents/web/intro.md b/book/contents/web/intro.md similarity index 100% rename from course-book/contents/web/intro.md rename to book/contents/web/intro.md diff --git a/course-book/contents/web/serialization.md b/book/contents/web/serialization.md similarity index 100% rename from course-book/contents/web/serialization.md rename to book/contents/web/serialization.md diff --git a/deploy b/book/deploy similarity index 100% rename from deploy rename to book/deploy diff --git a/course-book/excercises/Readme.md b/book/excercises/Readme.md similarity index 100% rename from course-book/excercises/Readme.md rename to book/excercises/Readme.md diff --git a/course-book/excercises/closure.md b/book/excercises/closure.md similarity index 100% rename from course-book/excercises/closure.md rename to book/excercises/closure.md diff --git a/course-book/excercises/lifetime.md b/book/excercises/lifetime.md similarity index 100% rename from course-book/excercises/lifetime.md rename to book/excercises/lifetime.md diff --git a/course-book/solutions/Readme.md b/book/solutions/Readme.md similarity index 100% rename from course-book/solutions/Readme.md rename to book/solutions/Readme.md diff --git a/course-book/solutions/closure.md b/book/solutions/closure.md similarity index 100% rename from course-book/solutions/closure.md rename to book/solutions/closure.md diff --git a/course-book/writing-material/books.md b/book/writing-material/books.md similarity index 100% rename from course-book/writing-material/books.md rename to book/writing-material/books.md diff --git a/course-book/writing-material/demos_for_learning.md b/book/writing-material/demos_for_learning.md similarity index 100% rename from course-book/writing-material/demos_for_learning.md rename to book/writing-material/demos_for_learning.md diff --git a/course-book/writing-material/posts/.DS_Store b/book/writing-material/posts/.DS_Store similarity index 100% rename from course-book/writing-material/posts/.DS_Store rename to book/writing-material/posts/.DS_Store diff --git a/course-book/writing-material/posts/Iterator.md b/book/writing-material/posts/Iterator.md similarity index 100% rename from course-book/writing-material/posts/Iterator.md rename to book/writing-material/posts/Iterator.md diff --git a/course-book/writing-material/posts/SIMD.md b/book/writing-material/posts/SIMD.md similarity index 100% rename from course-book/writing-material/posts/SIMD.md rename to book/writing-material/posts/SIMD.md diff --git a/course-book/writing-material/posts/async.md b/book/writing-material/posts/async.md similarity index 100% rename from course-book/writing-material/posts/async.md rename to book/writing-material/posts/async.md diff --git a/course-book/writing-material/posts/atomic.md b/book/writing-material/posts/atomic.md similarity index 100% rename from course-book/writing-material/posts/atomic.md rename to book/writing-material/posts/atomic.md diff --git a/course-book/writing-material/posts/attributes.md b/book/writing-material/posts/attributes.md similarity index 100% rename from course-book/writing-material/posts/attributes.md rename to book/writing-material/posts/attributes.md diff --git a/course-book/writing-material/posts/fight-with-compiler-check/borrow.md b/book/writing-material/posts/fight-with-compiler-check/borrow.md similarity index 100% rename from course-book/writing-material/posts/fight-with-compiler-check/borrow.md rename to book/writing-material/posts/fight-with-compiler-check/borrow.md diff --git a/course-book/writing-material/posts/fight-with-compiler-check/generic.md b/book/writing-material/posts/fight-with-compiler-check/generic.md similarity index 100% rename from course-book/writing-material/posts/fight-with-compiler-check/generic.md rename to book/writing-material/posts/fight-with-compiler-check/generic.md diff --git a/course-book/writing-material/posts/file.md b/book/writing-material/posts/file.md similarity index 100% rename from course-book/writing-material/posts/file.md rename to book/writing-material/posts/file.md diff --git a/course-book/writing-material/posts/function_signature.md b/book/writing-material/posts/function_signature.md similarity index 100% rename from course-book/writing-material/posts/function_signature.md rename to book/writing-material/posts/function_signature.md diff --git a/course-book/writing-material/posts/hashmap.md b/book/writing-material/posts/hashmap.md similarity index 100% rename from course-book/writing-material/posts/hashmap.md rename to book/writing-material/posts/hashmap.md diff --git a/course-book/writing-material/posts/identifier.md b/book/writing-material/posts/identifier.md similarity index 100% rename from course-book/writing-material/posts/identifier.md rename to book/writing-material/posts/identifier.md diff --git a/course-book/writing-material/posts/images.md b/book/writing-material/posts/images.md similarity index 100% rename from course-book/writing-material/posts/images.md rename to book/writing-material/posts/images.md diff --git a/course-book/writing-material/posts/io.md b/book/writing-material/posts/io.md similarity index 100% rename from course-book/writing-material/posts/io.md rename to book/writing-material/posts/io.md diff --git a/course-book/writing-material/posts/lifetime.md b/book/writing-material/posts/lifetime.md similarity index 100% rename from course-book/writing-material/posts/lifetime.md rename to book/writing-material/posts/lifetime.md diff --git a/course-book/writing-material/posts/lifetime_elision_rules.md b/book/writing-material/posts/lifetime_elision_rules.md similarity index 100% rename from course-book/writing-material/posts/lifetime_elision_rules.md rename to book/writing-material/posts/lifetime_elision_rules.md diff --git a/course-book/writing-material/posts/non-lexical-lifetime.md b/book/writing-material/posts/non-lexical-lifetime.md similarity index 100% rename from course-book/writing-material/posts/non-lexical-lifetime.md rename to book/writing-material/posts/non-lexical-lifetime.md diff --git a/course-book/writing-material/posts/operators.md b/book/writing-material/posts/operators.md similarity index 100% rename from course-book/writing-material/posts/operators.md rename to book/writing-material/posts/operators.md diff --git a/course-book/writing-material/posts/package.md b/book/writing-material/posts/package.md similarity index 100% rename from course-book/writing-material/posts/package.md rename to book/writing-material/posts/package.md diff --git a/course-book/writing-material/posts/paralleism.md b/book/writing-material/posts/paralleism.md similarity index 100% rename from course-book/writing-material/posts/paralleism.md rename to book/writing-material/posts/paralleism.md diff --git a/course-book/writing-material/posts/performance.md b/book/writing-material/posts/performance.md similarity index 100% rename from course-book/writing-material/posts/performance.md rename to book/writing-material/posts/performance.md diff --git a/course-book/writing-material/posts/reference.md b/book/writing-material/posts/reference.md similarity index 100% rename from course-book/writing-material/posts/reference.md rename to book/writing-material/posts/reference.md diff --git a/course-book/writing-material/posts/rust-analyser.md b/book/writing-material/posts/rust-analyser.md similarity index 100% rename from course-book/writing-material/posts/rust-analyser.md rename to book/writing-material/posts/rust-analyser.md diff --git a/course-book/writing-material/posts/self-referential.md b/book/writing-material/posts/self-referential.md similarity index 100% rename from course-book/writing-material/posts/self-referential.md rename to book/writing-material/posts/self-referential.md diff --git a/course-book/writing-material/posts/string.md b/book/writing-material/posts/string.md similarity index 100% rename from course-book/writing-material/posts/string.md rename to book/writing-material/posts/string.md diff --git a/course-book/writing-material/posts/system_command.md b/book/writing-material/posts/system_command.md similarity index 100% rename from course-book/writing-material/posts/system_command.md rename to book/writing-material/posts/system_command.md diff --git a/course-book/writing-material/posts/tests/doc_test.md b/book/writing-material/posts/tests/doc_test.md similarity index 100% rename from course-book/writing-material/posts/tests/doc_test.md rename to book/writing-material/posts/tests/doc_test.md diff --git a/course-book/writing-material/posts/tests/integration_test.md b/book/writing-material/posts/tests/integration_test.md similarity index 100% rename from course-book/writing-material/posts/tests/integration_test.md rename to book/writing-material/posts/tests/integration_test.md diff --git a/course-book/writing-material/posts/tests/misc.md b/book/writing-material/posts/tests/misc.md similarity index 100% rename from course-book/writing-material/posts/tests/misc.md rename to book/writing-material/posts/tests/misc.md diff --git a/course-book/writing-material/posts/tests/unit_test.md b/book/writing-material/posts/tests/unit_test.md similarity index 100% rename from course-book/writing-material/posts/tests/unit_test.md rename to book/writing-material/posts/tests/unit_test.md diff --git a/course-book/writing-material/posts/threads.md b/book/writing-material/posts/threads.md similarity index 100% rename from course-book/writing-material/posts/threads.md rename to book/writing-material/posts/threads.md diff --git a/course-book/writing-material/posts/to_resolved.md b/book/writing-material/posts/to_resolved.md similarity index 100% rename from course-book/writing-material/posts/to_resolved.md rename to book/writing-material/posts/to_resolved.md diff --git a/course-book/writing-material/posts/trivia.md b/book/writing-material/posts/trivia.md similarity index 100% rename from course-book/writing-material/posts/trivia.md rename to book/writing-material/posts/trivia.md diff --git a/course-book/writing-material/posts/wasm.md b/book/writing-material/posts/wasm.md similarity index 100% rename from course-book/writing-material/posts/wasm.md rename to book/writing-material/posts/wasm.md diff --git a/course-book/writing-material/style_guide/coding.md b/book/writing-material/style_guide/coding.md similarity index 100% rename from course-book/writing-material/style_guide/coding.md rename to book/writing-material/style_guide/coding.md diff --git a/course-book/writing-material/style_guide/naming.md b/book/writing-material/style_guide/naming.md similarity index 100% rename from course-book/writing-material/style_guide/naming.md rename to book/writing-material/style_guide/naming.md diff --git a/course-book/writing-material/.DS_Store b/codes/.DS_Store similarity index 74% rename from course-book/writing-material/.DS_Store rename to codes/.DS_Store index c8d3f4ef..8b6e8564 100644 Binary files a/course-book/writing-material/.DS_Store and b/codes/.DS_Store differ diff --git a/codes/.gitignore b/codes/.gitignore new file mode 100644 index 00000000..5591cf06 --- /dev/null +++ b/codes/.gitignore @@ -0,0 +1,2 @@ +book +*/.DS_Store \ No newline at end of file diff --git a/codes/CNAME b/codes/CNAME new file mode 100644 index 00000000..fc542fe9 --- /dev/null +++ b/codes/CNAME @@ -0,0 +1 @@ +codes.rs \ No newline at end of file diff --git a/codes/README.md b/codes/README.md new file mode 100644 index 00000000..41efccaf --- /dev/null +++ b/codes/README.md @@ -0,0 +1,21 @@ +## Rust代码鉴赏(Rust Codes) + +- 官方网址:[https://codes.rs](https://codes.rs) +- 修订时间: **尚未发行** +- Rust版本:Rust edition 2021 +- QQ交流群: 1009730433 + +学习Rust光看一讲语法的书是不够的,因为Rust语言着实复杂和高级,所以本书是<>的补充阅读书本,目标是: +1. 为用户呈现高质量的代码片段,这种代码你往往会在某个时刻需要用到,却不知道如何更好的实现,甚至无法让它通过编译检查 +2. 精算的数据结构和算法设计 +3. 标准库代码分析 + +所以,这本书可以成为你的口袋书和工具书,随时可以打开手机鉴赏几段令人赏心悦目的代码。 + +**如果你想系统学习Rust,请先阅读我们的开山巨作**:[Rust语言圣经](https://course.rs),绝对是一极其优秀的Rust全面教学书籍。 + +## 开源版权说明 + +Rust代码鉴赏是完全开源的电子书籍,但是也应该受到版权的保护。 + +因此我们选择了[No License](https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwigkv-KtMT0AhXFdXAKHdI4BCcQFnoECAQQAw&url=https%3A%2F%2Fchoosealicense.com%2Fno-permission%2F&usg=AOvVaw3M2Q4IbdhnpJ2K71TF7SPB)作为我们的版权,这意味着读者可以随意的fork、阅读,但是不能私下修改后再分发,如果想要修改,请提RP或者加入[Rust编程学院](https://college.rs),成为正式成员。 diff --git a/codes/assets/baidu_verify_code-6xpiXNfMYY.html b/codes/assets/baidu_verify_code-6xpiXNfMYY.html new file mode 100644 index 00000000..fd6ee3f9 --- /dev/null +++ b/codes/assets/baidu_verify_code-6xpiXNfMYY.html @@ -0,0 +1 @@ +717a8be683db73a812bab355e948d1f7 \ No newline at end of file diff --git a/codes/book.toml b/codes/book.toml new file mode 100644 index 00000000..4ac4e0bd --- /dev/null +++ b/codes/book.toml @@ -0,0 +1,14 @@ +[book] +authors = ["sunface"] +language = "zh-CN" +multilingual = false +src = "src" +title = "Rust代码鉴赏(Rust Codes)" + +[output.html] +git-repository-url = "https://github.com/rustcollege/codes" +edit-url-template = "https://github.com/rustcollege/codes/edit/main/{path}" + +[output.html.fold] +enable = true +level = 1 diff --git a/codes/release b/codes/release new file mode 100755 index 00000000..804ed3ab --- /dev/null +++ b/codes/release @@ -0,0 +1,20 @@ +## this script is releasing the book to github pages + +## build book +mdbook build +## copy CNAME info to book dir +cp CNAME ./book/ +cp ./assets/*.html ./book/ + +## init git repo +cd book +git init +git config user.name "sunface" +git config user.email "cto@188.com" +git add . +git commit -m 'release book' +git branch -M gh-pages +git remote add origin https://github.com/rustcollege/rust-codes + +## push to github pages +git push -u -f origin gh-pages \ No newline at end of file diff --git a/codes/src/SUMMARY.md b/codes/src/SUMMARY.md new file mode 100644 index 00000000..00bd860e --- /dev/null +++ b/codes/src/SUMMARY.md @@ -0,0 +1,147 @@ +# Summary + +[Rust代码鉴赏](./about-book.md) + +- [算法之美](algorithms/index.md) + - [排序](algorithms/sorting/index.md) + - [冒泡排序](algorithms/sorting/bubble-sort.md) + - [桶排序](algorithms/sorting/bucket-sort.md) + - [鸡尾酒排序](algorithms/sorting/cocktail-shaker-sort.md) + - [梳排序](algorithms/sorting/comb-sort.md) + - [计数排序](algorithms/sorting/counting-sort.md) + - [地精排序](algorithms/sorting/gnome-sort.md) + - [堆排序](algorithms/sorting/heap-sort.md) + - [插入排序](algorithms/sorting/insertion-sort.md) + - [归并排序](algorithms/sorting/merge-sort.md) + - [奇偶排序](algorithms/sorting/odd-even.md) + - [快速排序](algorithms/sorting/quick-sort.md) + - [基数排序](algorithms/sorting/radix-sort.md) + - [选择排序](algorithms/sorting/selection-sort.md) + - [希尔排序](algorithms/sorting/shell-sort.md) + - [臭皮匠排序](algorithms/sorting/stooge-sort.md) + + - [字符串](algorithms/string/index.md) + - [逆序倒转](algorithms/string/reverse.md) + - [数据转换算法](algorithms/string/burrows-wheeler-transform.md) + - [KMP算法](algorithms/string/knuth-morris-pratt.md) + - [马拉车算法](algorithms/string/manacher.md) + - [Rabin Karp算法](algorithms/string/rabin-karp.md) + + - [查找算法](algorithms/searching/index.md) + - [二分查找](algorithms/searching/binary-search.md) + - [递归二分查找](algorithms/searching/binary-search-recursive.md) + - [查找第K小的元素](algorithms/searching/kth-smallest.md) + - [线性搜索](algorithms/searching/linear-search.md) + + - [图论](algorithms/graph/index.md) + - [最短路径-Bellman Ford](algorithms/graph/bellman-ford.md) + - [最短路径-Dijkstra](algorithms/graph/dijkstra.md) + - [深度优先搜索](algorithms/graph/depth-first-search.md) + - [广度优先搜索](algorithms/graph/breadth-first-search.md) + - [深度优先Tic Tac Toe](algorithms/graph/depth-first-tic-tac-toe.md) + - [最小生成树](algorithms/graph/minimum-spanning-tree.md) + - [Prim算法(最小生成树)](algorithms/graph/prim.md) + + - [动态规划](algorithms/dynamic-programming/index.md) + - [斐波那契(fibonacci)](algorithms/dynamic-programming/fibonacci.md) + - [找钱(Coin change)](algorithms/dynamic-programming/coin-change.md) + - [最小编辑距离(Edit distance)](algorithms/dynamic-programming/edit-distance.md) + - [扔鸡蛋(Egg dropping)](algorithms/dynamic-programming/egg-dropping.md) + - [判断子序列](algorithms/dynamic-programming/is-subsequence.md) + - [背包问题](algorithms/dynamic-programming/knapsack.md) + - [最长公共子序列](algorithms/dynamic-programming/longese-common-sequence.md) + - [最长连续递增序列](algorithms/dynamic-programming/longest_continuous_increasing_subsequence.md) + - [最长上升子序列](algorithms/dynamic-programming/longest-increasing-subsequence.md) + - [最大正方形](algorithms/dynamic-programming/maximal-square.md) + - [最大子数组](algorithms/dynamic-programming/maximal-subarray.md) + - [棒的切割](algorithms/dynamic-programming/rod-cutting.md) + + - [数学]() + - [扩展欧几里得算法](algorithms/math/extended-euclidean.md) + - [最大公约数](algorithms/math/greatest-common-divisor.md) + - [帕斯卡三角](algorithms/math/pascal-triange.md) + - [寻找完美数](algorithms/math/perfect-numbers.md) + - [质数检测](algorithms/math/prime-check.md) + - [质数筛法](algorithms/math/prime-numbers.md) + - [试除法](algorithms/math/trial-division.md) + + - [几何]() + - [最近点算法](algorithms/geometry/closet-points.md) + + - [常用算法]() + - [凸包算法](algorithms/general/convex-hull.md) + - [汉诺塔算法](algorithms/general/hanoi.md) + - [K-Means算法](algorithms/general/kmeans.md) + - [N皇后算法](algorithms/general/nqueens.md) + - [两数之和](algorithms/general/two-sum.md) + + - [加密算法](algorithms/cipher/index.md) + - [凯撒算法(caesar)](algorithms/cipher/caesar.md) + - [摩斯码](algorithms/cipher/morse-code.md) + - [Polibius密码](algorithms/cipher/polibius.md) + - [rot13加密算法](algorithms/cipher/rot13.md) + - [rot13第二种实现](algorithms/cipher/another-rot13.md) + - [sha256加密](algorithms/cipher/sha256.md) + - [vigenere加密](algorithms/cipher/vigenere.md) + - [xor](algorithms/cipher/xor.md) + +- [数据结构](data-structures/index.md) + - [B树](data-structures/b-tree.md) + - [二叉树](data-structures/binary-search-tree.md) + - [avl树](data-structures/avl-tree.md) + - [链表](data-structures/linked-list.md) + - [堆(Heap)](data-structures/heap.md) + - [栈](data-structures/stack.md) + - [队列](data-structures/queue.md) + - [trie(字典树)](data-structures/trie.md) + - [图(graph)](data-structures/graph.md) + +- [对抗编译检查](fight-compiler/index.md) + - [迭代器Iterator](fight-compiler/iterator.md) + +- [深入特征](deep-trait/index.md) + - [Deref](deep-trait/deref.md) + +- [Rust陷阱系列](pitfalls/index.md) + - [for循环中使用外部数组](pitfalls/use-vec-in-for.md) + - [线程类型导致的栈溢出](pitfalls/stack-overflow.md) + - [算术溢出导致的panic](pitfalls/arithmetic-overflow.md) + - [闭包中奇怪的生命周期](pitfalls/closure-with-lifetime.md) + - [可变变量不可变?](pitfalls/the-disabled-mutability.md) + - [可变借用失败引发的深入思考](pitfalls/multiple-mutable-references.md) + +- [迭代器Iterator](iterator/index.md) + - [同时从首尾遍历](iterator/from-start-and-end.md) + +- [Unsafe Rust](unsafe/index.md) + - [通过元素获取数组索引](unsafe/get-arryr-index-by-elem.md) + - [实现多维数组](unsafe/multi-dimension-array.md) + - [实现自引用](unsafe/self-ref.md) + +- [运算符重载](operator-override/index.md) + - [数组索引](operator-override/index.md) + +- [模式匹配](pattern-match/index.md) + - [使用match和if let返回值](pattern-match/return-with-match.md) + +- [奇怪代码背后的原理](principles-behind-weird-code/index.md) + - [右值取地址](principles-behind-weird-code/ref-to-rvalue.md) + - [autoref/autoderef](principles-behind-weird-code/autoref.md) + +- [有趣但是用处不大的代码](fun-but-useless/index.md) + - [?嵌套](fun-but-useless/question-mark-nested.md) + +- [堆和栈](stack-heap/index.md) + - [避免递归函数栈溢出](stack-heap/recursive-stack-overlfow.md) + +- [将复杂的实现简化](make-things-easier/index.md) + - [代码1](make-things-easier/1.md) + +- [复杂难懂的代码](obscure/index.md) + - [代码1](obscure/1.md) + +## 场景模版 +- [场景模版 todo](templates/intro.md) + - [文件操作](templates/files/intro.md) + - [目录(todo)](templates/files/dir.md) + - [Http请求(todo)](templates/http/intro.md) diff --git a/codes/src/about-book.md b/codes/src/about-book.md new file mode 100644 index 00000000..46f52391 --- /dev/null +++ b/codes/src/about-book.md @@ -0,0 +1,12 @@ +## Rust代码鉴赏(Rust Codes) + +- 书本官网:[https://codes.rs](https://codes.rs) + +学习Rust光看一讲语法的书是不够的,因为Rust语言着实复杂和高级,所以本书是<>的补充阅读书本,目标是: +1. 为用户呈现高质量的代码片段,这种代码你往往会在某个时刻需要用到,却不知道如何更好的实现,甚至无法让它通过编译检查 +2. 精算的数据结构和算法设计 +3. 标准库代码分析 + +所以,这本书可以成为你的口袋书和工具书,随时可以打开手机鉴赏几段令人赏心悦目的代码。 + +**如果你想系统学习Rust,请先阅读我们的开山巨作**:[Rust语言圣经](https://course.rs),绝对是一极其优秀的Rust全面教学书籍。 \ No newline at end of file diff --git a/codes/src/algorithms/cipher/another-rot13.md b/codes/src/algorithms/cipher/another-rot13.md new file mode 100644 index 00000000..614432e9 --- /dev/null +++ b/codes/src/algorithms/cipher/another-rot13.md @@ -0,0 +1,39 @@ +# rot13第二种实现 + +```rust +pub fn another_rot13(text: &str) -> String { + let input = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + let output = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"; + text.chars() + .map(|c| match input.find(c) { + Some(i) => output.chars().nth(i).unwrap(), + None => c, + }) + .collect() +} + +#[cfg(test)] +mod tests { + // Note this useful idiom: importing names from outer (for mod tests) scope. + use super::*; + + #[test] + fn test_simple() { + assert_eq!(another_rot13("ABCzyx"), "NOPmlk"); + } + + #[test] + fn test_every_alphabet_with_space() { + assert_eq!( + another_rot13("The quick brown fox jumps over the lazy dog"), + "Gur dhvpx oebja sbk whzcf bire gur ynml qbt" + ); + } + + #[test] + fn test_non_alphabet() { + assert_eq!(another_rot13("🎃 Jack-o'-lantern"), "🎃 Wnpx-b'-ynagrea"); + } +} +``` + diff --git a/codes/src/algorithms/cipher/caesar.md b/codes/src/algorithms/cipher/caesar.md new file mode 100644 index 00000000..da44fdfa --- /dev/null +++ b/codes/src/algorithms/cipher/caesar.md @@ -0,0 +1,38 @@ +# 凯撒算法(caesar) + +```rust +pub fn another_rot13(text: &str) -> String { + let input = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + let output = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"; + text.chars() + .map(|c| match input.find(c) { + Some(i) => output.chars().nth(i).unwrap(), + None => c, + }) + .collect() +} + +#[cfg(test)] +mod tests { + // Note this useful idiom: importing names from outer (for mod tests) scope. + use super::*; + + #[test] + fn test_simple() { + assert_eq!(another_rot13("ABCzyx"), "NOPmlk"); + } + + #[test] + fn test_every_alphabet_with_space() { + assert_eq!( + another_rot13("The quick brown fox jumps over the lazy dog"), + "Gur dhvpx oebja sbk whzcf bire gur ynml qbt" + ); + } + + #[test] + fn test_non_alphabet() { + assert_eq!(another_rot13("🎃 Jack-o'-lantern"), "🎃 Wnpx-b'-ynagrea"); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/cipher/index.md b/codes/src/algorithms/cipher/index.md new file mode 100644 index 00000000..fbc04a00 --- /dev/null +++ b/codes/src/algorithms/cipher/index.md @@ -0,0 +1,9 @@ +# 加密算法 + +数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码为“密文”,使其只能在输入相应的密钥之后才能显示出原容,通过这样的途径来达到保护数据不被非法人窃取、阅读的目的。 该过程的逆过程为解密,即将该编码信息转化为其原来数据的过程。 + +随着信息化和数字化社会的发展,人们对信息安全和保密的重要性认识不断提高,于是在1997年,美国国家标准局公布实施了“美国数据加密标准(DES)”,民间力量开始全面介入密码学的研究和应用中,采用的加密算法有`DES、RSA、SHA`等。随着对加密强度需求的不断提高,近期又出现了`AES、ECC`等。 +使用密码学可以达到以下目的: +- 保密性:防止用户的标识或数据被读取。 +- 数据完整性:防止数据被更改。 +- 身份验证:确保数据发自特定的一方。 \ No newline at end of file diff --git a/codes/src/algorithms/cipher/morse-code.md b/codes/src/algorithms/cipher/morse-code.md new file mode 100644 index 00000000..7d337852 --- /dev/null +++ b/codes/src/algorithms/cipher/morse-code.md @@ -0,0 +1,202 @@ +# 摩斯码 + +```rust +use std::collections::HashMap; +use std::io; + +const UNKNOWN_CHARACTER: &str = "........"; +const _UNKNOWN_MORSE_CHARACTER: &str = "_"; + +pub fn encode(message: &str) -> String { + let dictionary = _morse_dictionary(); + message + .chars() + .into_iter() + .map(|char| char.to_uppercase().to_string()) + .map(|letter| dictionary.get(letter.as_str())) + .map(|option| option.unwrap_or(&UNKNOWN_CHARACTER).to_string()) + .collect::>() + .join(" ") +} + +// Declaritive macro for creating readable map declarations, for more info see https://doc.rust-lang.org/book/ch19-06-macros.html +macro_rules! map { + ($($key:expr => $value:expr),* $(,)?) => { + std::iter::Iterator::collect(std::array::IntoIter::new([$(($key, $value),)*])) + }; +} + +fn _morse_dictionary() -> HashMap<&'static str, &'static str> { + map! { + "A" => ".-", "B" => "-...", "C" => "-.-.", + "D" => "-..", "E" => ".", "F" => "..-.", + "G" => "--.", "H" => "....", "I" => "..", + "J" => ".---", "K" => "-.-", "L" => ".-..", + "M" => "--", "N" => "-.", "O" => "---", + "P" => ".--.", "Q" => "--.-", "R" => ".-.", + "S" => "...", "T" => "-", "U" => "..-", + "V" => "...-", "W" => ".--", "X" => "-..-", + "Y" => "-.--", "Z" => "--..", + + "1" => ".----", "2" => "..---", "3" => "...--", + "4" => "....-", "5" => ".....", "6" => "-....", + "7" => "--...", "8" => "---..", "9" => "----.", + "0" => "-----", + + "&" => ".-...", "@" => ".--.-.", ":" => "---...", + "," => "--..--", "." => ".-.-.-", "'" => ".----.", + "\"" => ".-..-.", "?" => "..--..", "/" => "-..-.", + "=" => "-...-", "+" => ".-.-.", "-" => "-....-", + "(" => "-.--.", ")" => "-.--.-", " " => "/", + "!" => "-.-.--", + } +} + +fn _morse_to_alphanumeric_dictionary() -> HashMap<&'static str, &'static str> { + map! { + ".-" => "A", "-..." => "B", "-.-." => "C", + "-.." => "D", "." => "E", "..-." => "F", + "--." => "G", "...." => "H", ".." => "I", + ".---" => "J", "-.-" => "K", ".-.." => "L", + "--" => "M", "-." => "N", "---" => "O", + ".--." => "P", "--.-" => "Q", ".-." => "R", + "..." => "S", "-" => "T", "..-" => "U", + "...-" => "V", ".--" => "W", "-..-" => "X", + "-.--" => "Y", "--.." => "Z", + + ".----" => "1", "..---" => "2", "...--" => "3", + "....-" => "4", "....." => "5", "-...." => "6", + "--..." => "7", "---.." => "8", "----." => "9", + "-----" => "0", + + ".-..." => "&", ".--.-." => "@", "---..." => ":", + "--..--" => ",", ".-.-.-" => ".", ".----." => "'", + ".-..-." => "\"", "..--.." => "?", "-..-." => "/", + "-...-" => "=", ".-.-." => "+", "-....-" => "-", + "-.--." => "(", "-.--.-" => ")", "/" => " ", + "-.-.--" => "!", " " => " ", "" => "" + } +} + +fn _check_part(string: &str) -> bool { + for c in string.chars() { + match c { + '.' | '-' | ' ' => (), + _ => return false, + } + } + true +} + +fn _check_all_parts(string: &str) -> bool { + string.split('/').all(_check_part) +} + +fn _decode_token(string: &str) -> String { + _morse_to_alphanumeric_dictionary() + .get(string) + .unwrap_or(&_UNKNOWN_MORSE_CHARACTER) + .to_string() +} + +fn _decode_part(string: &str) -> String { + string + .split(' ') + .map(_decode_token) + .collect::>() + .join("") +} + +/// Convert morse code to ascii. +/// +/// Given a morse code, return the corresponding message. +/// If the code is invalid, the undecipherable part of the code is replaced by `_`. +pub fn decode(string: &str) -> Result { + if !_check_all_parts(string) { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Invalid morse code", + )); + } + + let mut partitions: Vec = vec![]; + + for part in string.split('/') { + partitions.push(_decode_part(part)); + } + + Ok(partitions.join(" ")) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn encrypt_only_letters() { + let message = "Hello Morse"; + let cipher = encode(message); + assert_eq!( + cipher, + ".... . .-.. .-.. --- / -- --- .-. ... .".to_string() + ) + } + + #[test] + fn encrypt_letters_and_special_characters() { + let message = "What's a great day!"; + let cipher = encode(message); + assert_eq!( + cipher, + ".-- .... .- - .----. ... / .- / --. .-. . .- - / -.. .- -.-- -.-.--".to_string() + ) + } + + #[test] + fn encrypt_message_with_unsupported_character() { + let message = "Error?? {}"; + let cipher = encode(message); + assert_eq!( + cipher, + ". .-. .-. --- .-. ..--.. ..--.. / ........ ........".to_string() + ) + } + + #[test] + fn decrypt_valid_morsecode_with_spaces() { + let expected = "Hello Morse! How's it goin, \"eh\"?" + .to_string() + .to_uppercase(); + let encypted = encode(&expected); + let result = decode(&encypted).unwrap(); + + assert_eq!(expected, result); + } + + #[test] + fn decrypt_valid_character_set_invalid_morsecode() { + let expected = format!( + "{}{}{}{} {}", + _UNKNOWN_MORSE_CHARACTER, + _UNKNOWN_MORSE_CHARACTER, + _UNKNOWN_MORSE_CHARACTER, + _UNKNOWN_MORSE_CHARACTER, + _UNKNOWN_MORSE_CHARACTER, + ); + + let encypted = ".-.-.--.-.-. --------. ..---.-.-. .-.-.--.-.-. / .-.-.--.-.-.".to_string(); + let result = decode(&encypted).unwrap(); + + assert_eq!(expected, result); + } + + #[test] + fn decrypt_invalid_morsecode_with_spaces() { + let encypted = "1... . .-.. .-.. --- / -- --- .-. ... ."; + let result = decode(encypted).map_err(|e| e.kind()); + let expected = Err(io::ErrorKind::InvalidData); + + assert_eq!(expected, result); + } +} +``` diff --git a/codes/src/algorithms/cipher/polibius.md b/codes/src/algorithms/cipher/polibius.md new file mode 100644 index 00000000..69f6c867 --- /dev/null +++ b/codes/src/algorithms/cipher/polibius.md @@ -0,0 +1,153 @@ +# Polibius密码 + +```rust +/// Encode an ASCII string into its location in a Polybius square. +/// Only alphabetical characters are encoded. +pub fn encode_ascii(string: &str) -> String { + string + .chars() + .map(|c| match c { + 'a' | 'A' => "11", + 'b' | 'B' => "12", + 'c' | 'C' => "13", + 'd' | 'D' => "14", + 'e' | 'E' => "15", + 'f' | 'F' => "21", + 'g' | 'G' => "22", + 'h' | 'H' => "23", + 'i' | 'I' | 'j' | 'J' => "24", + 'k' | 'K' => "25", + 'l' | 'L' => "31", + 'm' | 'M' => "32", + 'n' | 'N' => "33", + 'o' | 'O' => "34", + 'p' | 'P' => "35", + 'q' | 'Q' => "41", + 'r' | 'R' => "42", + 's' | 'S' => "43", + 't' | 'T' => "44", + 'u' | 'U' => "45", + 'v' | 'V' => "51", + 'w' | 'W' => "52", + 'x' | 'X' => "53", + 'y' | 'Y' => "54", + 'z' | 'Z' => "55", + _ => "", + }) + .collect() +} + +/// Decode a string of ints into their corresponding +/// letters in a Polybius square. +/// +/// Any invalid characters, or whitespace will be ignored. +pub fn decode_ascii(string: &str) -> String { + string + .chars() + .filter(|c| !c.is_whitespace()) + .collect::() + .as_bytes() + .chunks(2) + .map(|s| match std::str::from_utf8(s) { + Ok(v) => v.parse::().unwrap_or(0), + Err(_) => 0, + }) + .map(|i| match i { + 11 => 'A', + 12 => 'B', + 13 => 'C', + 14 => 'D', + 15 => 'E', + 21 => 'F', + 22 => 'G', + 23 => 'H', + 24 => 'I', + 25 => 'K', + 31 => 'L', + 32 => 'M', + 33 => 'N', + 34 => 'O', + 35 => 'P', + 41 => 'Q', + 42 => 'R', + 43 => 'S', + 44 => 'T', + 45 => 'U', + 51 => 'V', + 52 => 'W', + 53 => 'X', + 54 => 'Y', + 55 => 'Z', + _ => ' ', + }) + .collect::() + .replace(" ", "") +} + +#[cfg(test)] +mod tests { + use super::{decode_ascii, encode_ascii}; + + #[test] + fn encode_empty() { + assert_eq!(encode_ascii(""), ""); + } + + #[test] + fn encode_valid_string() { + assert_eq!(encode_ascii("This is a test"), "4423244324431144154344"); + } + + #[test] + fn encode_emoji() { + assert_eq!(encode_ascii("🙂"), ""); + } + + #[test] + fn decode_empty() { + assert_eq!(decode_ascii(""), ""); + } + + #[test] + fn decode_valid_string() { + assert_eq!( + decode_ascii("44 23 24 43 24 43 11 44 15 43 44 "), + "THISISATEST" + ); + } + + #[test] + fn decode_emoji() { + assert_eq!(decode_ascii("🙂"), ""); + } + + #[test] + fn decode_string_with_whitespace() { + assert_eq!( + decode_ascii("44\n23\t\r24\r\n43 2443\n 11 \t 44\r \r15 \n43 44"), + "THISISATEST" + ); + } + + #[test] + fn decode_unknown_string() { + assert_eq!(decode_ascii("94 63 64 83 64 48 77 00 05 47 48 "), ""); + } + + #[test] + fn decode_odd_length() { + assert_eq!(decode_ascii("11 22 33 4"), "AGN"); + } + + #[test] + fn encode_and_decode() { + let string = "Do you ever wonder why we're here?"; + let encode = encode_ascii(string); + assert_eq!( + "1434543445155115425234331415425223545215421523154215", + encode, + ); + assert_eq!("DOYOUEVERWONDERWHYWEREHERE", decode_ascii(&encode)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/cipher/rot13.md b/codes/src/algorithms/cipher/rot13.md new file mode 100644 index 00000000..a924dc8a --- /dev/null +++ b/codes/src/algorithms/cipher/rot13.md @@ -0,0 +1,40 @@ +# rot13加密算法 + +```rust +pub fn rot13(text: &str) -> String { + let to_enc = text.to_uppercase(); + to_enc + .chars() + .map(|c| match c { + 'A'..='M' => ((c as u8) + 13) as char, + 'N'..='Z' => ((c as u8) - 13) as char, + _ => c, + }) + .collect() +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_single_letter() { + assert_eq!("N", rot13("A")); + } + + #[test] + fn test_bunch_of_letters() { + assert_eq!("NOP", rot13("ABC")); + } + + #[test] + fn test_non_ascii() { + assert_eq!("😀NO", rot13("😀AB")); + } + + #[test] + fn test_twice() { + assert_eq!("ABCD", rot13(&rot13("ABCD"))); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/cipher/sha256.md b/codes/src/algorithms/cipher/sha256.md new file mode 100644 index 00000000..d0729ef1 --- /dev/null +++ b/codes/src/algorithms/cipher/sha256.md @@ -0,0 +1,187 @@ +# sha256加密 + +```rust +//! SHA-2 (256 Bit) + +struct BufState { + data: Vec, + len: usize, + total_len: usize, + single: bool, + total: bool, +} + +pub fn sha256(data: &[u8]) -> [u8; 32] { + let mut hash: [u8; 32] = [0; 32]; + + let mut h: [u32; 8] = [ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, + 0x5be0cd19, + ]; + + let k: [u32; 64] = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, + 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, + 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, + 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, + 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, + 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, + 0xc67178f2, + ]; + + let mut chunk: [u8; 64] = [0; 64]; + + let mut state: BufState = BufState { + data: (*data).to_owned(), + len: data.len(), + total_len: data.len(), + single: false, + total: false, + }; + + while calc_chunk(&mut chunk, &mut state) { + let mut ah: [u32; 8] = h; + let mut w: [u32; 16] = [0; 16]; + for i in 0..4 { + for j in 0..16 { + if i == 0 { + w[j] = ((chunk[j * 4] as u32) << 24) + | ((chunk[j * 4 + 1] as u32) << 16) + | ((chunk[j * 4 + 2] as u32) << 8) + | (chunk[j * 4 + 3] as u32); + } else { + let s0 = (w[(j + 1) & 0xf].rotate_right(7) ^ w[(j + 1) & 0xf].rotate_right(18)) + ^ (w[(j + 1) & 0xf] >> 3); + let s1 = w[(j + 14) & 0xf].rotate_right(17) + ^ w[(j + 14) & 0xf].rotate_right(19) + ^ (w[(j + 14) & 0xf] >> 10); + w[j] = w[j] + .wrapping_add(s0) + .wrapping_add(w[(j + 9) & 0xf]) + .wrapping_add(s1); + } + + let s1: u32 = + ah[4].rotate_right(6) ^ ah[4].rotate_right(11) ^ ah[4].rotate_right(25); + let ch: u32 = (ah[4] & ah[5]) ^ (!ah[4] & ah[6]); + let temp1: u32 = ah[7] + .wrapping_add(s1) + .wrapping_add(ch) + .wrapping_add(k[i << 4 | j]) + .wrapping_add(w[j]); + let s0: u32 = + ah[0].rotate_right(2) ^ ah[0].rotate_right(13) ^ ah[0].rotate_right(22); + let maj: u32 = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]); + let temp2: u32 = s0.wrapping_add(maj); + + ah[7] = ah[6]; + ah[6] = ah[5]; + ah[5] = ah[4]; + ah[4] = ah[3].wrapping_add(temp1); + ah[3] = ah[2]; + ah[2] = ah[1]; + ah[1] = ah[0]; + ah[0] = temp1.wrapping_add(temp2); + } + } + + for i in 0..8 { + h[i] = h[i].wrapping_add(ah[i]); + } + chunk = [0; 64]; + } + + for i in 0..8 { + hash[i * 4] = (h[i] >> 24) as u8; + hash[i * 4 + 1] = (h[i] >> 16) as u8; + hash[i * 4 + 2] = (h[i] >> 8) as u8; + hash[i * 4 + 3] = h[i] as u8; + } + + hash +} + +fn calc_chunk(chunk: &mut [u8; 64], state: &mut BufState) -> bool { + if state.total { + return false; + } + + if state.len >= 64 { + for x in chunk { + *x = state.data[0]; + state.data.remove(0); + } + state.len -= 64; + return true; + } + + let remaining: usize = state.data.len(); + let space: usize = 64 - remaining; + for x in chunk.iter_mut().take(state.data.len()) { + *x = state.data[0]; + state.data.remove(0); + } + + if !state.single { + chunk[remaining] = 0x80; + state.single = true; + } + + if space >= 8 { + let mut len = state.total_len; + chunk[63] = (len << 3) as u8; + len >>= 5; + for i in 1..8 { + chunk[(63 - i)] = len as u8; + len >>= 8; + } + state.total = true; + } + + true +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty() { + assert_eq!( + sha256(&Vec::new()), + [ + 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, + 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, + 0x78, 0x52, 0xb8, 0x55 + ] + ); + } + + #[test] + fn ascii() { + assert_eq!( + sha256(&b"The quick brown fox jumps over the lazy dog".to_vec()), + [ + 0xD7, 0xA8, 0xFB, 0xB3, 0x07, 0xD7, 0x80, 0x94, 0x69, 0xCA, 0x9A, 0xBC, 0xB0, 0x08, + 0x2E, 0x4F, 0x8D, 0x56, 0x51, 0xE4, 0x6D, 0x3C, 0xDB, 0x76, 0x2D, 0x02, 0xD0, 0xBF, + 0x37, 0xC9, 0xE5, 0x92 + ] + ) + } + + #[test] + fn ascii_avalanche() { + assert_eq!( + sha256(&b"The quick brown fox jumps over the lazy dog.".to_vec()), + [ + 0xEF, 0x53, 0x7F, 0x25, 0xC8, 0x95, 0xBF, 0xA7, 0x82, 0x52, 0x65, 0x29, 0xA9, 0xB6, + 0x3D, 0x97, 0xAA, 0x63, 0x15, 0x64, 0xD5, 0xD7, 0x89, 0xC2, 0xB7, 0x65, 0x44, 0x8C, + 0x86, 0x35, 0xFB, 0x6C + ] + ) + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/cipher/vigenere.md b/codes/src/algorithms/cipher/vigenere.md new file mode 100644 index 00000000..54701cf1 --- /dev/null +++ b/codes/src/algorithms/cipher/vigenere.md @@ -0,0 +1,90 @@ +# vigenere加密 + +```rust +//! Vigenère Cipher +//! +//! # Algorithm +//! +//! Rotate each ascii character by the offset of the corresponding key character. +//! When we reach the last key character, we start over from the first one. +//! This implementation does not rotate unicode characters. + +/// Vigenère cipher to rotate plain_text text by key and return an owned String. +pub fn vigenere(plain_text: &str, key: &str) -> String { + // Remove all unicode and non-ascii characters from key + let key: String = key.chars().filter(|&c| c.is_ascii_alphabetic()).collect(); + key.to_ascii_lowercase(); + + let key_len = key.len(); + if key_len == 0 { + return String::from(plain_text); + } + + let mut index = 0; + + plain_text + .chars() + .map(|c| { + if c.is_ascii_alphabetic() { + let first = if c.is_ascii_lowercase() { b'a' } else { b'A' }; + let shift = key.as_bytes()[index % key_len] - b'a'; + index += 1; + // modulo the distance to keep character range + (first + (c as u8 + shift - first) % 26) as char + } else { + c + } + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty() { + assert_eq!(vigenere("", "test"), ""); + } + + #[test] + fn vigenere_base() { + assert_eq!( + vigenere("LoremIpsumDolorSitAmet", "base"), + "MojinIhwvmVsmojWjtSqft" + ); + } + + #[test] + fn vigenere_with_spaces() { + assert_eq!( + vigenere( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "spaces" + ), + "Ddrgq ahhuo hgddr uml sbev, ggfheexwljr chahxsemfy tlkx." + ); + } + + #[test] + fn vigenere_unicode_and_numbers() { + assert_eq!( + vigenere("1 Lorem ⏳ ipsum dolor sit amet Ѡ", "unicode"), + "1 Fbzga ⏳ ltmhu fcosl fqv opin Ѡ" + ); + } + + #[test] + fn vigenere_unicode_key() { + assert_eq!( + vigenere("Lorem ipsum dolor sit amet", "😉 key!"), + "Vspoq gzwsw hmvsp cmr kqcd" + ); + } + + #[test] + fn vigenere_empty_key() { + assert_eq!(vigenere("Lorem ipsum", ""), "Lorem ipsum"); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/cipher/xor.md b/codes/src/algorithms/cipher/xor.md new file mode 100644 index 00000000..2fb7c30c --- /dev/null +++ b/codes/src/algorithms/cipher/xor.md @@ -0,0 +1,26 @@ +# xor + +```rust +pub fn xor(text: &str, key: u8) -> String { + text.chars().map(|c| ((c as u8) ^ key) as char).collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + let test_string = "test string"; + let ciphered_text = xor(test_string, 32); + assert_eq!(test_string, xor(&ciphered_text, 32)); + } + + #[test] + fn test_every_alphabet_with_space() { + let test_string = "The quick brown fox jumps over the lazy dog"; + let ciphered_text = xor(test_string, 64); + assert_eq!(test_string, xor(&ciphered_text, 64)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/coin-change.md b/codes/src/algorithms/dynamic-programming/coin-change.md new file mode 100644 index 00000000..484ed067 --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/coin-change.md @@ -0,0 +1,71 @@ +# 找钱(Coin change) + +```rust +/// Coin change via Dynamic Programming + +/// coin_change(coins, amount) returns the fewest number of coins that need to make up that amount. +/// If that amount of money cannot be made up by any combination of the coins, return `None`. +/// +/// Arguments: +/// * `coins` - coins of different denominations +/// * `amount` - a total amount of money be made up. +/// Complexity +/// - time complexity: O(amount * coins.length), +/// - space complexity: O(amount), +pub fn coin_change(coins: &[usize], amount: usize) -> Option { + let mut dp = vec![std::usize::MAX; amount + 1]; + dp[0] = 0; + + // Assume dp[i] is the fewest number of coins making up amount i, + // then for every coin in coins, dp[i] = min(dp[i - coin] + 1). + for i in 0..=amount { + for j in 0..coins.len() { + if i >= coins[j] && dp[i - coins[j]] != std::usize::MAX { + dp[i] = dp[i].min(dp[i - coins[j]] + 1); + } + } + } + + match dp[amount] { + std::usize::MAX => None, + _ => Some(dp[amount]), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + // 11 = 5 * 2 + 1 * 1 + let coins = vec![1, 2, 5]; + assert_eq!(Some(3), coin_change(&coins, 11)); + + // 119 = 11 * 10 + 7 * 1 + 2 * 1 + let coins = vec![2, 3, 5, 7, 11]; + assert_eq!(Some(12), coin_change(&coins, 119)); + } + + #[test] + fn coins_empty() { + let coins = vec![]; + assert_eq!(None, coin_change(&coins, 1)); + } + + #[test] + fn amount_zero() { + let coins = vec![1, 2, 3]; + assert_eq!(Some(0), coin_change(&coins, 0)); + } + + #[test] + fn fail_change() { + // 3 can't be change by 2. + let coins = vec![2]; + assert_eq!(None, coin_change(&coins, 3)); + let coins = vec![10, 20, 50, 100]; + assert_eq!(None, coin_change(&coins, 5)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/edit-distance.md b/codes/src/algorithms/dynamic-programming/edit-distance.md new file mode 100644 index 00000000..9004dc3c --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/edit-distance.md @@ -0,0 +1,71 @@ +# 最小编辑距离(Edit distance) + +```rust +/// Coin change via Dynamic Programming + +/// coin_change(coins, amount) returns the fewest number of coins that need to make up that amount. +/// If that amount of money cannot be made up by any combination of the coins, return `None`. +/// +/// Arguments: +/// * `coins` - coins of different denominations +/// * `amount` - a total amount of money be made up. +/// Complexity +/// - time complexity: O(amount * coins.length), +/// - space complexity: O(amount), +pub fn coin_change(coins: &[usize], amount: usize) -> Option { + let mut dp = vec![std::usize::MAX; amount + 1]; + dp[0] = 0; + + // Assume dp[i] is the fewest number of coins making up amount i, + // then for every coin in coins, dp[i] = min(dp[i - coin] + 1). + for i in 0..=amount { + for j in 0..coins.len() { + if i >= coins[j] && dp[i - coins[j]] != std::usize::MAX { + dp[i] = dp[i].min(dp[i - coins[j]] + 1); + } + } + } + + match dp[amount] { + std::usize::MAX => None, + _ => Some(dp[amount]), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + // 11 = 5 * 2 + 1 * 1 + let coins = vec![1, 2, 5]; + assert_eq!(Some(3), coin_change(&coins, 11)); + + // 119 = 11 * 10 + 7 * 1 + 2 * 1 + let coins = vec![2, 3, 5, 7, 11]; + assert_eq!(Some(12), coin_change(&coins, 119)); + } + + #[test] + fn coins_empty() { + let coins = vec![]; + assert_eq!(None, coin_change(&coins, 1)); + } + + #[test] + fn amount_zero() { + let coins = vec![1, 2, 3]; + assert_eq!(Some(0), coin_change(&coins, 0)); + } + + #[test] + fn fail_change() { + // 3 can't be change by 2. + let coins = vec![2]; + assert_eq!(None, coin_change(&coins, 3)); + let coins = vec![10, 20, 50, 100]; + assert_eq!(None, coin_change(&coins, 5)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/egg-dropping.md b/codes/src/algorithms/dynamic-programming/egg-dropping.md new file mode 100644 index 00000000..2af9ed8e --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/egg-dropping.md @@ -0,0 +1,95 @@ +# 扔鸡蛋(Egg dropping) + +```rust +/// # Egg Dropping Puzzle + +/// `egg_drop(eggs, floors)` returns the least number of egg droppings +/// required to determine the highest floor from which an egg will not +/// break upon dropping +/// +/// Assumptions: n > 0 +pub fn egg_drop(eggs: u32, floors: u32) -> u32 { + assert!(eggs > 0); + + // Explicity handle edge cases (optional) + if eggs == 1 || floors == 0 || floors == 1 { + return floors; + } + + let eggs_index = eggs as usize; + let floors_index = floors as usize; + + // Store solutions to subproblems in 2D Vec, + // where egg_drops[i][j] represents the solution to the egg dropping + // problem with i eggs and j floors + let mut egg_drops: Vec> = vec![vec![0; floors_index + 1]; eggs_index + 1]; + + // Assign solutions for egg_drop(n, 0) = 0, egg_drop(n, 1) = 1 + for egg_drop in egg_drops.iter_mut().skip(1) { + egg_drop[0] = 0; + egg_drop[1] = 1; + } + + // Assign solutions to egg_drop(1, k) = k + for j in 1..=floors_index { + egg_drops[1][j] = j as u32; + } + + // Complete solutions vector using optimal substructure property + for i in 2..=eggs_index { + for j in 2..=floors_index { + egg_drops[i][j] = std::u32::MAX; + + for k in 1..=j { + let res = 1 + std::cmp::max(egg_drops[i - 1][k - 1], egg_drops[i][j - k]); + + if res < egg_drops[i][j] { + egg_drops[i][j] = res; + } + } + } + } + + egg_drops[eggs_index][floors_index] +} + +#[cfg(test)] +mod tests { + use super::egg_drop; + + #[test] + fn zero_floors() { + assert_eq!(egg_drop(5, 0), 0); + } + + #[test] + fn one_egg() { + assert_eq!(egg_drop(1, 8), 8); + } + + #[test] + fn eggs2_floors2() { + assert_eq!(egg_drop(2, 2), 2); + } + + #[test] + fn eggs3_floors5() { + assert_eq!(egg_drop(3, 5), 3); + } + + #[test] + fn eggs2_floors10() { + assert_eq!(egg_drop(2, 10), 4); + } + + #[test] + fn eggs2_floors36() { + assert_eq!(egg_drop(2, 36), 8); + } + + #[test] + fn large_floors() { + assert_eq!(egg_drop(2, 100), 14); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/fibonacci.md b/codes/src/algorithms/dynamic-programming/fibonacci.md new file mode 100644 index 00000000..25ca1266 --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/fibonacci.md @@ -0,0 +1,209 @@ +# 斐波那契(fibonacci) + +```rust +/// Fibonacci via Dynamic Programming + +/// fibonacci(n) returns the nth fibonacci number +/// This function uses the definition of Fibonacci where: +/// F(0) = F(1) = 1 and F(n+1) = F(n) + F(n-1) for n>0 +/// +/// Warning: This will overflow the 128-bit unsigned integer at n=186 +pub fn fibonacci(n: u32) -> u128 { + // Use a and b to store the previous two values in the sequence + let mut a = 0; + let mut b = 1; + for _i in 0..n { + // As we iterate through, move b's value into a and the new computed + // value into b. + let c = a + b; + a = b; + b = c; + } + b +} + +/// fibonacci(n) returns the nth fibonacci number +/// This function uses the definition of Fibonacci where: +/// F(0) = F(1) = 1 and F(n+1) = F(n) + F(n-1) for n>0 +/// +/// Warning: This will overflow the 128-bit unsigned integer at n=186 +pub fn recursive_fibonacci(n: u32) -> u128 { + // Call the actual tail recursive implementation, with the extra + // arguments set up. + _recursive_fibonacci(n, 0, 1) +} + +fn _recursive_fibonacci(n: u32, previous: u128, current: u128) -> u128 { + if n == 0 { + current + } else { + _recursive_fibonacci(n - 1, current, current + previous) + } +} + +/// classical_fibonacci(n) returns the nth fibonacci number +/// This function uses the definition of Fibonacci where: +/// F(0) = 0, F(1) = 1 and F(n+1) = F(n) + F(n-1) for n>0 +/// +/// Warning: This will overflow the 128-bit unsigned integer at n=186 +pub fn classical_fibonacci(n: u32) -> u128 { + match n { + 0 => 0, + 1 => 1, + _ => { + let k = n / 2; + let f1 = classical_fibonacci(k); + let f2 = classical_fibonacci(k - 1); + + match n % 4 { + 0 | 2 => f1 * (f1 + 2 * f2), + 1 => (2 * f1 + f2) * (2 * f1 - f2) + 2, + _ => (2 * f1 + f2) * (2 * f1 - f2) - 2, + } + } + } +} + +/// logarithmic_fibonacci(n) returns the nth fibonacci number +/// This function uses the definition of Fibonacci where: +/// F(0) = 0, F(1) = 1 and F(n+1) = F(n) + F(n-1) for n>0 +/// +/// Warning: This will overflow the 128-bit unsigned integer at n=186 +pub fn logarithmic_fibonacci(n: u32) -> u128 { + // if it is the max value before overflow, use n-1 then get the second + // value in the tuple + if n == 186 { + let (_, second) = _logarithmic_fibonacci(185); + second + } else { + let (first, _) = _logarithmic_fibonacci(n); + first + } +} + +fn _logarithmic_fibonacci(n: u32) -> (u128, u128) { + match n { + 0 => (0, 1), + _ => { + let (current, next) = _logarithmic_fibonacci(n / 2); + let c = current * (next * 2 - current); + let d = current * current + next * next; + + match n % 2 { + 0 => (c, d), + _ => (d, c + d), + } + } + } +} + +#[cfg(test)] +mod tests { + use super::classical_fibonacci; + use super::fibonacci; + use super::logarithmic_fibonacci; + use super::recursive_fibonacci; + + #[test] + fn test_fibonacci() { + assert_eq!(fibonacci(0), 1); + assert_eq!(fibonacci(1), 1); + assert_eq!(fibonacci(2), 2); + assert_eq!(fibonacci(3), 3); + assert_eq!(fibonacci(4), 5); + assert_eq!(fibonacci(5), 8); + assert_eq!(fibonacci(10), 89); + assert_eq!(fibonacci(20), 10946); + assert_eq!(fibonacci(100), 573147844013817084101); + assert_eq!(fibonacci(184), 205697230343233228174223751303346572685); + } + + #[test] + fn test_recursive_fibonacci() { + assert_eq!(recursive_fibonacci(0), 1); + assert_eq!(recursive_fibonacci(1), 1); + assert_eq!(recursive_fibonacci(2), 2); + assert_eq!(recursive_fibonacci(3), 3); + assert_eq!(recursive_fibonacci(4), 5); + assert_eq!(recursive_fibonacci(5), 8); + assert_eq!(recursive_fibonacci(10), 89); + assert_eq!(recursive_fibonacci(20), 10946); + assert_eq!(recursive_fibonacci(100), 573147844013817084101); + assert_eq!( + recursive_fibonacci(184), + 205697230343233228174223751303346572685 + ); + } + + #[test] + fn test_classical_fibonacci() { + assert_eq!(classical_fibonacci(0), 0); + assert_eq!(classical_fibonacci(1), 1); + assert_eq!(classical_fibonacci(2), 1); + assert_eq!(classical_fibonacci(3), 2); + assert_eq!(classical_fibonacci(4), 3); + assert_eq!(classical_fibonacci(5), 5); + assert_eq!(classical_fibonacci(10), 55); + assert_eq!(classical_fibonacci(20), 6765); + assert_eq!(classical_fibonacci(21), 10946); + assert_eq!(classical_fibonacci(100), 354224848179261915075); + assert_eq!( + classical_fibonacci(184), + 127127879743834334146972278486287885163 + ); + } + + #[test] + fn test_logarithmic_fibonacci() { + assert_eq!(logarithmic_fibonacci(0), 0); + assert_eq!(logarithmic_fibonacci(1), 1); + assert_eq!(logarithmic_fibonacci(2), 1); + assert_eq!(logarithmic_fibonacci(3), 2); + assert_eq!(logarithmic_fibonacci(4), 3); + assert_eq!(logarithmic_fibonacci(5), 5); + assert_eq!(logarithmic_fibonacci(10), 55); + assert_eq!(logarithmic_fibonacci(20), 6765); + assert_eq!(logarithmic_fibonacci(21), 10946); + assert_eq!(logarithmic_fibonacci(100), 354224848179261915075); + assert_eq!( + logarithmic_fibonacci(184), + 127127879743834334146972278486287885163 + ); + } + + #[test] + /// Check that the itterative and recursive fibonacci + /// produce the same value. Both are combinatorial ( F(0) = F(1) = 1 ) + fn test_iterative_and_recursive_equivalence() { + assert_eq!(fibonacci(0), recursive_fibonacci(0)); + assert_eq!(fibonacci(1), recursive_fibonacci(1)); + assert_eq!(fibonacci(2), recursive_fibonacci(2)); + assert_eq!(fibonacci(3), recursive_fibonacci(3)); + assert_eq!(fibonacci(4), recursive_fibonacci(4)); + assert_eq!(fibonacci(5), recursive_fibonacci(5)); + assert_eq!(fibonacci(10), recursive_fibonacci(10)); + assert_eq!(fibonacci(20), recursive_fibonacci(20)); + assert_eq!(fibonacci(100), recursive_fibonacci(100)); + assert_eq!(fibonacci(184), recursive_fibonacci(184)); + } + + #[test] + /// Check that classical and combinatorial fibonacci produce the + /// same value when 'n' differs by 1. + /// classical fibonacci: ( F(0) = 0, F(1) = 1 ) + /// combinatorial fibonacci: ( F(0) = F(1) = 1 ) + fn test_classical_and_combinatorial_are_off_by_one() { + assert_eq!(classical_fibonacci(1), fibonacci(0)); + assert_eq!(classical_fibonacci(2), fibonacci(1)); + assert_eq!(classical_fibonacci(3), fibonacci(2)); + assert_eq!(classical_fibonacci(4), fibonacci(3)); + assert_eq!(classical_fibonacci(5), fibonacci(4)); + assert_eq!(classical_fibonacci(6), fibonacci(5)); + assert_eq!(classical_fibonacci(11), fibonacci(10)); + assert_eq!(classical_fibonacci(20), fibonacci(19)); + assert_eq!(classical_fibonacci(21), fibonacci(20)); + assert_eq!(classical_fibonacci(101), fibonacci(100)); + assert_eq!(classical_fibonacci(185), fibonacci(184)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/index.md b/codes/src/algorithms/dynamic-programming/index.md new file mode 100644 index 00000000..d47697e1 --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/index.md @@ -0,0 +1,4 @@ +# 动态规划 + +动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。 +动态规划算法的基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。 diff --git a/codes/src/algorithms/dynamic-programming/is-subsequence.md b/codes/src/algorithms/dynamic-programming/is-subsequence.md new file mode 100644 index 00000000..e8707803 --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/is-subsequence.md @@ -0,0 +1,209 @@ +# 判断子序列 + +```rust +/// Fibonacci via Dynamic Programming + +/// fibonacci(n) returns the nth fibonacci number +/// This function uses the definition of Fibonacci where: +/// F(0) = F(1) = 1 and F(n+1) = F(n) + F(n-1) for n>0 +/// +/// Warning: This will overflow the 128-bit unsigned integer at n=186 +pub fn fibonacci(n: u32) -> u128 { + // Use a and b to store the previous two values in the sequence + let mut a = 0; + let mut b = 1; + for _i in 0..n { + // As we iterate through, move b's value into a and the new computed + // value into b. + let c = a + b; + a = b; + b = c; + } + b +} + +/// fibonacci(n) returns the nth fibonacci number +/// This function uses the definition of Fibonacci where: +/// F(0) = F(1) = 1 and F(n+1) = F(n) + F(n-1) for n>0 +/// +/// Warning: This will overflow the 128-bit unsigned integer at n=186 +pub fn recursive_fibonacci(n: u32) -> u128 { + // Call the actual tail recursive implementation, with the extra + // arguments set up. + _recursive_fibonacci(n, 0, 1) +} + +fn _recursive_fibonacci(n: u32, previous: u128, current: u128) -> u128 { + if n == 0 { + current + } else { + _recursive_fibonacci(n - 1, current, current + previous) + } +} + +/// classical_fibonacci(n) returns the nth fibonacci number +/// This function uses the definition of Fibonacci where: +/// F(0) = 0, F(1) = 1 and F(n+1) = F(n) + F(n-1) for n>0 +/// +/// Warning: This will overflow the 128-bit unsigned integer at n=186 +pub fn classical_fibonacci(n: u32) -> u128 { + match n { + 0 => 0, + 1 => 1, + _ => { + let k = n / 2; + let f1 = classical_fibonacci(k); + let f2 = classical_fibonacci(k - 1); + + match n % 4 { + 0 | 2 => f1 * (f1 + 2 * f2), + 1 => (2 * f1 + f2) * (2 * f1 - f2) + 2, + _ => (2 * f1 + f2) * (2 * f1 - f2) - 2, + } + } + } +} + +/// logarithmic_fibonacci(n) returns the nth fibonacci number +/// This function uses the definition of Fibonacci where: +/// F(0) = 0, F(1) = 1 and F(n+1) = F(n) + F(n-1) for n>0 +/// +/// Warning: This will overflow the 128-bit unsigned integer at n=186 +pub fn logarithmic_fibonacci(n: u32) -> u128 { + // if it is the max value before overflow, use n-1 then get the second + // value in the tuple + if n == 186 { + let (_, second) = _logarithmic_fibonacci(185); + second + } else { + let (first, _) = _logarithmic_fibonacci(n); + first + } +} + +fn _logarithmic_fibonacci(n: u32) -> (u128, u128) { + match n { + 0 => (0, 1), + _ => { + let (current, next) = _logarithmic_fibonacci(n / 2); + let c = current * (next * 2 - current); + let d = current * current + next * next; + + match n % 2 { + 0 => (c, d), + _ => (d, c + d), + } + } + } +} + +#[cfg(test)] +mod tests { + use super::classical_fibonacci; + use super::fibonacci; + use super::logarithmic_fibonacci; + use super::recursive_fibonacci; + + #[test] + fn test_fibonacci() { + assert_eq!(fibonacci(0), 1); + assert_eq!(fibonacci(1), 1); + assert_eq!(fibonacci(2), 2); + assert_eq!(fibonacci(3), 3); + assert_eq!(fibonacci(4), 5); + assert_eq!(fibonacci(5), 8); + assert_eq!(fibonacci(10), 89); + assert_eq!(fibonacci(20), 10946); + assert_eq!(fibonacci(100), 573147844013817084101); + assert_eq!(fibonacci(184), 205697230343233228174223751303346572685); + } + + #[test] + fn test_recursive_fibonacci() { + assert_eq!(recursive_fibonacci(0), 1); + assert_eq!(recursive_fibonacci(1), 1); + assert_eq!(recursive_fibonacci(2), 2); + assert_eq!(recursive_fibonacci(3), 3); + assert_eq!(recursive_fibonacci(4), 5); + assert_eq!(recursive_fibonacci(5), 8); + assert_eq!(recursive_fibonacci(10), 89); + assert_eq!(recursive_fibonacci(20), 10946); + assert_eq!(recursive_fibonacci(100), 573147844013817084101); + assert_eq!( + recursive_fibonacci(184), + 205697230343233228174223751303346572685 + ); + } + + #[test] + fn test_classical_fibonacci() { + assert_eq!(classical_fibonacci(0), 0); + assert_eq!(classical_fibonacci(1), 1); + assert_eq!(classical_fibonacci(2), 1); + assert_eq!(classical_fibonacci(3), 2); + assert_eq!(classical_fibonacci(4), 3); + assert_eq!(classical_fibonacci(5), 5); + assert_eq!(classical_fibonacci(10), 55); + assert_eq!(classical_fibonacci(20), 6765); + assert_eq!(classical_fibonacci(21), 10946); + assert_eq!(classical_fibonacci(100), 354224848179261915075); + assert_eq!( + classical_fibonacci(184), + 127127879743834334146972278486287885163 + ); + } + + #[test] + fn test_logarithmic_fibonacci() { + assert_eq!(logarithmic_fibonacci(0), 0); + assert_eq!(logarithmic_fibonacci(1), 1); + assert_eq!(logarithmic_fibonacci(2), 1); + assert_eq!(logarithmic_fibonacci(3), 2); + assert_eq!(logarithmic_fibonacci(4), 3); + assert_eq!(logarithmic_fibonacci(5), 5); + assert_eq!(logarithmic_fibonacci(10), 55); + assert_eq!(logarithmic_fibonacci(20), 6765); + assert_eq!(logarithmic_fibonacci(21), 10946); + assert_eq!(logarithmic_fibonacci(100), 354224848179261915075); + assert_eq!( + logarithmic_fibonacci(184), + 127127879743834334146972278486287885163 + ); + } + + #[test] + /// Check that the itterative and recursive fibonacci + /// produce the same value. Both are combinatorial ( F(0) = F(1) = 1 ) + fn test_iterative_and_recursive_equivalence() { + assert_eq!(fibonacci(0), recursive_fibonacci(0)); + assert_eq!(fibonacci(1), recursive_fibonacci(1)); + assert_eq!(fibonacci(2), recursive_fibonacci(2)); + assert_eq!(fibonacci(3), recursive_fibonacci(3)); + assert_eq!(fibonacci(4), recursive_fibonacci(4)); + assert_eq!(fibonacci(5), recursive_fibonacci(5)); + assert_eq!(fibonacci(10), recursive_fibonacci(10)); + assert_eq!(fibonacci(20), recursive_fibonacci(20)); + assert_eq!(fibonacci(100), recursive_fibonacci(100)); + assert_eq!(fibonacci(184), recursive_fibonacci(184)); + } + + #[test] + /// Check that classical and combinatorial fibonacci produce the + /// same value when 'n' differs by 1. + /// classical fibonacci: ( F(0) = 0, F(1) = 1 ) + /// combinatorial fibonacci: ( F(0) = F(1) = 1 ) + fn test_classical_and_combinatorial_are_off_by_one() { + assert_eq!(classical_fibonacci(1), fibonacci(0)); + assert_eq!(classical_fibonacci(2), fibonacci(1)); + assert_eq!(classical_fibonacci(3), fibonacci(2)); + assert_eq!(classical_fibonacci(4), fibonacci(3)); + assert_eq!(classical_fibonacci(5), fibonacci(4)); + assert_eq!(classical_fibonacci(6), fibonacci(5)); + assert_eq!(classical_fibonacci(11), fibonacci(10)); + assert_eq!(classical_fibonacci(20), fibonacci(19)); + assert_eq!(classical_fibonacci(21), fibonacci(20)); + assert_eq!(classical_fibonacci(101), fibonacci(100)); + assert_eq!(classical_fibonacci(185), fibonacci(184)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/knapsack.md b/codes/src/algorithms/dynamic-programming/knapsack.md new file mode 100644 index 00000000..d356767e --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/knapsack.md @@ -0,0 +1,152 @@ +# 背包问题 + +```rust +//! Solves the knapsack problem +use std::cmp::max; + +/// knapsack_table(w, weights, values) returns the knapsack table (`n`, `m`) with maximum values, where `n` is number of items +/// +/// Arguments: +/// * `w` - knapsack capacity +/// * `weights` - set of weights for each item +/// * `values` - set of values for each item +fn knapsack_table(w: &usize, weights: &[usize], values: &[usize]) -> Vec> { + // Initialize `n` - number of items + let n: usize = weights.len(); + // Initialize `m` + // m[i, w] - the maximum value that can be attained with weight less that or equal to `w` using items up to `i` + let mut m: Vec> = vec![vec![0; w + 1]; n + 1]; + + for i in 0..=n { + for j in 0..=*w { + // m[i, j] compiled according to the following rule: + if i == 0 || j == 0 { + m[i][j] = 0; + } else if weights[i - 1] <= j { + // If `i` is in the knapsack + // Then m[i, j] is equal to the maximum value of the knapsack, + // where the weight `j` is reduced by the weight of the `i-th` item and the set of admissible items plus the value `k` + m[i][j] = max(values[i - 1] + m[i - 1][j - weights[i - 1]], m[i - 1][j]); + } else { + // If the item `i` did not get into the knapsack + // Then m[i, j] is equal to the maximum cost of a knapsack with the same capacity and a set of admissible items + m[i][j] = m[i - 1][j] + } + } + } + m +} + +/// knapsack_items(weights, m, i, j) returns the indices of the items of the optimal knapsack (from 1 to `n`) +/// +/// Arguments: +/// * `weights` - set of weights for each item +/// * `m` - knapsack table with maximum values +/// * `i` - include items 1 through `i` in knapsack (for the initial value, use `n`) +/// * `j` - maximum weight of the knapsack +fn knapsack_items(weights: &[usize], m: &[Vec], i: usize, j: usize) -> Vec { + if i == 0 { + return vec![]; + } + if m[i][j] > m[i - 1][j] { + let mut knap: Vec = knapsack_items(weights, m, i - 1, j - weights[i - 1]); + knap.push(i); + knap + } else { + knapsack_items(weights, m, i - 1, j) + } +} + +/// knapsack(w, weights, values) returns the tuple where first value is `optimal profit`, +/// second value is `knapsack optimal weight` and the last value is `indices of items`, that we got (from 1 to `n`) +/// +/// Arguments: +/// * `w` - knapsack capacity +/// * `weights` - set of weights for each item +/// * `values` - set of values for each item +/// +/// Complexity +/// - time complexity: O(nw), +/// - space complexity: O(nw), +/// +/// where `n` and `w` are `number of items` and `knapsack capacity` +pub fn knapsack(w: usize, weights: Vec, values: Vec) -> (usize, usize, Vec) { + // Checks if the number of items in the list of weights is the same as the number of items in the list of values + assert_eq!(weights.len(), values.len(), "Number of items in the list of weights doesn't match the number of items in the list of values!"); + // Initialize `n` - number of items + let n: usize = weights.len(); + // Find the knapsack table + let m: Vec> = knapsack_table(&w, &weights, &values); + // Find the indices of the items + let items: Vec = knapsack_items(&weights, &m, n, w); + // Find the total weight of optimal knapsack + let mut total_weight: usize = 0; + for i in items.iter() { + total_weight += weights[i - 1]; + } + // Return result + (m[n][w], total_weight, items) +} + +#[cfg(test)] +mod tests { + // Took test datasets from https://people.sc.fsu.edu/~jburkardt/datasets/bin_packing/bin_packing.html + use super::*; + + #[test] + fn test_p02() { + assert_eq!( + (51, 26, vec![2, 3, 4]), + knapsack(26, vec![12, 7, 11, 8, 9], vec![24, 13, 23, 15, 16]) + ); + } + + #[test] + fn test_p04() { + assert_eq!( + (150, 190, vec![1, 2, 5]), + knapsack( + 190, + vec![56, 59, 80, 64, 75, 17], + vec![50, 50, 64, 46, 50, 5] + ) + ); + } + + #[test] + fn test_p01() { + assert_eq!( + (309, 165, vec![1, 2, 3, 4, 6]), + knapsack( + 165, + vec![23, 31, 29, 44, 53, 38, 63, 85, 89, 82], + vec![92, 57, 49, 68, 60, 43, 67, 84, 87, 72] + ) + ); + } + + #[test] + fn test_p06() { + assert_eq!( + (1735, 169, vec![2, 4, 7]), + knapsack( + 170, + vec![41, 50, 49, 59, 55, 57, 60], + vec![442, 525, 511, 593, 546, 564, 617] + ) + ); + } + + #[test] + fn test_p07() { + assert_eq!( + (1458, 749, vec![1, 3, 5, 7, 8, 9, 14, 15]), + knapsack( + 750, + vec![70, 73, 77, 80, 82, 87, 90, 94, 98, 106, 110, 113, 115, 118, 120], + vec![135, 139, 149, 150, 156, 163, 173, 184, 192, 201, 210, 214, 221, 229, 240] + ) + ); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/longese-common-sequence.md b/codes/src/algorithms/dynamic-programming/longese-common-sequence.md new file mode 100644 index 00000000..a5779867 --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/longese-common-sequence.md @@ -0,0 +1,77 @@ +# 最长公共子序列 + +```rust +/// Longest common subsequence via Dynamic Programming + +/// longest_common_subsequence(a, b) returns the longest common subsequence +/// between the strings a and b. +pub fn longest_common_subsequence(a: &str, b: &str) -> String { + let a: Vec<_> = a.chars().collect(); + let b: Vec<_> = b.chars().collect(); + let (na, nb) = (a.len(), b.len()); + + // solutions[i][j] is the length of the longest common subsequence + // between a[0..i-1] and b[0..j-1] + let mut solutions = vec![vec![0; nb + 1]; na + 1]; + + for (i, ci) in a.iter().enumerate() { + for (j, cj) in b.iter().enumerate() { + // if ci == cj, there is a new common character; + // otherwise, take the best of the two solutions + // at (i-1,j) and (i,j-1) + solutions[i + 1][j + 1] = if ci == cj { + solutions[i][j] + 1 + } else { + solutions[i][j + 1].max(solutions[i + 1][j]) + } + } + } + + // reconstitute the solution string from the lengths + let mut result: Vec = Vec::new(); + let (mut i, mut j) = (na, nb); + while i > 0 && j > 0 { + if a[i - 1] == b[j - 1] { + result.push(a[i - 1]); + i -= 1; + j -= 1; + } else if solutions[i - 1][j] > solutions[i][j - 1] { + i -= 1; + } else { + j -= 1; + } + } + + result.reverse(); + result.iter().collect() +} + +#[cfg(test)] +mod tests { + use super::longest_common_subsequence; + + #[test] + fn test_longest_common_subsequence() { + // empty case + assert_eq!(&longest_common_subsequence("", ""), ""); + assert_eq!(&longest_common_subsequence("", "abcd"), ""); + assert_eq!(&longest_common_subsequence("abcd", ""), ""); + + // simple cases + assert_eq!(&longest_common_subsequence("abcd", "c"), "c"); + assert_eq!(&longest_common_subsequence("abcd", "d"), "d"); + assert_eq!(&longest_common_subsequence("abcd", "e"), ""); + assert_eq!(&longest_common_subsequence("abcdefghi", "acegi"), "acegi"); + + // less simple cases + assert_eq!(&longest_common_subsequence("abcdgh", "aedfhr"), "adh"); + assert_eq!(&longest_common_subsequence("aggtab", "gxtxayb"), "gtab"); + + // unicode + assert_eq!( + &longest_common_subsequence("你好,世界", "再见世界"), + "世界" + ); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/longest-increasing-subsequence.md b/codes/src/algorithms/dynamic-programming/longest-increasing-subsequence.md new file mode 100644 index 00000000..bc84c381 --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/longest-increasing-subsequence.md @@ -0,0 +1,432 @@ +# 最长上升子序列 + +```rust +/// Finds the longest increasing subsequence and returns it. +/// +/// If multiple subsequences with the longest possible subsequence length can be found, the +/// subsequence which appeared first will be returned (see `test_example_1`). +/// +/// Inspired by [this LeetCode problem](https://leetcode.com/problems/longest-increasing-subsequence/). +pub fn longest_increasing_subsequence(input_array: Vec) -> Vec { + let n = input_array.len(); + if n <= 1 { + return input_array; + } + + // Find longest increasing subsequence + let mut dp = vec![(1, None); n]; + let mut pair = 0; + + for i in 0..n { + for j in 0..i { + if input_array[j] < input_array[i] && dp[j].0 + 1 > dp[i].0 { + dp[i] = (dp[j].0 + 1, Some(j)); + + if dp[i].0 > dp[pair].0 { + pair = i; + } + } + } + } + + // Construct subsequence + let mut out: Vec = Vec::with_capacity(dp[pair].0); + + out.push(input_array[pair].clone()); + while let Some(next) = dp[pair].1 { + pair = next; + out.push(input_array[pair].clone()); + } + + out.into_iter().rev().collect() +} + +#[cfg(test)] +mod tests { + use super::longest_increasing_subsequence; + + #[test] + /// Need to specify generic type T in order to function + fn test_empty_vec() { + assert_eq!(longest_increasing_subsequence::(vec![]), vec![]); + } + + #[test] + fn test_example_1() { + assert_eq!( + longest_increasing_subsequence(vec![10, 9, 2, 5, 3, 7, 101, 18]), + vec![2, 5, 7, 101] + ); + } + + #[test] + fn test_example_2() { + assert_eq!( + longest_increasing_subsequence(vec![0, 1, 0, 3, 2, 3]), + vec![0, 1, 2, 3] + ); + } + + #[test] + fn test_example_3() { + assert_eq!( + longest_increasing_subsequence(vec![7, 7, 7, 7, 7, 7, 7]), + vec![7] + ); + } + + #[test] + #[ignore] + fn test_tle() { + assert_eq!( + longest_increasing_subsequence(vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, + 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, + 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, + 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, + 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, + 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, + 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, + 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, + 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, + 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, + 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, + 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, + 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, + 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, + 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, + 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, + 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, + 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, + 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, + 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, + 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, + 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, + 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, + 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, + 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, + 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, + 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, + 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, + 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, + 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, + 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, + 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, + 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, + 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, + 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, + 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, + 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, + 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, + 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, + 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, + 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, + 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, + 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, + 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, + 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, + 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, + 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, + 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, + 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, + 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, + 1156, 1157, 1158, 1159, 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, + 1170, 1171, 1172, 1173, 1174, 1175, 1176, 1177, 1178, 1179, 1180, 1181, 1182, 1183, + 1184, 1185, 1186, 1187, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197, + 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, + 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220, 1221, 1222, 1223, 1224, 1225, + 1226, 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236, 1237, 1238, 1239, + 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253, + 1254, 1255, 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, 1267, + 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, 1281, + 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295, + 1296, 1297, 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, + 1310, 1311, 1312, 1313, 1314, 1315, 1316, 1317, 1318, 1319, 1320, 1321, 1322, 1323, + 1324, 1325, 1326, 1327, 1328, 1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, + 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351, + 1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1365, + 1366, 1367, 1368, 1369, 1370, 1371, 1372, 1373, 1374, 1375, 1376, 1377, 1378, 1379, + 1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, 1391, 1392, 1393, + 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, + 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1415, 1416, 1417, 1418, 1419, 1420, 1421, + 1422, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1430, 1431, 1432, 1433, 1434, 1435, + 1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444, 1445, 1446, 1447, 1448, 1449, + 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457, 1458, 1459, 1460, 1461, 1462, 1463, + 1464, 1465, 1466, 1467, 1468, 1469, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, + 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1485, 1486, 1487, 1488, 1489, 1490, 1491, + 1492, 1493, 1494, 1495, 1496, 1497, 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, + 1506, 1507, 1508, 1509, 1510, 1511, 1512, 1513, 1514, 1515, 1516, 1517, 1518, 1519, + 1520, 1521, 1522, 1523, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1533, + 1534, 1535, 1536, 1537, 1538, 1539, 1540, 1541, 1542, 1543, 1544, 1545, 1546, 1547, + 1548, 1549, 1550, 1551, 1552, 1553, 1554, 1555, 1556, 1557, 1558, 1559, 1560, 1561, + 1562, 1563, 1564, 1565, 1566, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1575, + 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, + 1590, 1591, 1592, 1593, 1594, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1602, 1603, + 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615, 1616, 1617, + 1618, 1619, 1620, 1621, 1622, 1623, 1624, 1625, 1626, 1627, 1628, 1629, 1630, 1631, + 1632, 1633, 1634, 1635, 1636, 1637, 1638, 1639, 1640, 1641, 1642, 1643, 1644, 1645, + 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653, 1654, 1655, 1656, 1657, 1658, 1659, + 1660, 1661, 1662, 1663, 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, 1672, 1673, + 1674, 1675, 1676, 1677, 1678, 1679, 1680, 1681, 1682, 1683, 1684, 1685, 1686, 1687, + 1688, 1689, 1690, 1691, 1692, 1693, 1694, 1695, 1696, 1697, 1698, 1699, 1700, 1701, + 1702, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 1714, 1715, + 1716, 1717, 1718, 1719, 1720, 1721, 1722, 1723, 1724, 1725, 1726, 1727, 1728, 1729, + 1730, 1731, 1732, 1733, 1734, 1735, 1736, 1737, 1738, 1739, 1740, 1741, 1742, 1743, + 1744, 1745, 1746, 1747, 1748, 1749, 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, + 1758, 1759, 1760, 1761, 1762, 1763, 1764, 1765, 1766, 1767, 1768, 1769, 1770, 1771, + 1772, 1773, 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, 1782, 1783, 1784, 1785, + 1786, 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1794, 1795, 1796, 1797, 1798, 1799, + 1800, 1801, 1802, 1803, 1804, 1805, 1806, 1807, 1808, 1809, 1810, 1811, 1812, 1813, + 1814, 1815, 1816, 1817, 1818, 1819, 1820, 1821, 1822, 1823, 1824, 1825, 1826, 1827, + 1828, 1829, 1830, 1831, 1832, 1833, 1834, 1835, 1836, 1837, 1838, 1839, 1840, 1841, + 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849, 1850, 1851, 1852, 1853, 1854, 1855, + 1856, 1857, 1858, 1859, 1860, 1861, 1862, 1863, 1864, 1865, 1866, 1867, 1868, 1869, + 1870, 1871, 1872, 1873, 1874, 1875, 1876, 1877, 1878, 1879, 1880, 1881, 1882, 1883, + 1884, 1885, 1886, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894, 1895, 1896, 1897, + 1898, 1899, 1900, 1901, 1902, 1903, 1904, 1905, 1906, 1907, 1908, 1909, 1910, 1911, + 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920, 1921, 1922, 1923, 1924, 1925, + 1926, 1927, 1928, 1929, 1930, 1931, 1932, 1933, 1934, 1935, 1936, 1937, 1938, 1939, + 1940, 1941, 1942, 1943, 1944, 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, + 1954, 1955, 1956, 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, + 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, + 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, + 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, + 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, + 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, + 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, + 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079, + 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, + 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, + 2108, 2109, 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, + 2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, 2131, 2132, 2133, 2134, 2135, + 2136, 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, 2149, + 2150, 2151, 2152, 2153, 2154, 2155, 2156, 2157, 2158, 2159, 2160, 2161, 2162, 2163, + 2164, 2165, 2166, 2167, 2168, 2169, 2170, 2171, 2172, 2173, 2174, 2175, 2176, 2177, + 2178, 2179, 2180, 2181, 2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189, 2190, 2191, + 2192, 2193, 2194, 2195, 2196, 2197, 2198, 2199, 2200, 2201, 2202, 2203, 2204, 2205, + 2206, 2207, 2208, 2209, 2210, 2211, 2212, 2213, 2214, 2215, 2216, 2217, 2218, 2219, + 2220, 2221, 2222, 2223, 2224, 2225, 2226, 2227, 2228, 2229, 2230, 2231, 2232, 2233, + 2234, 2235, 2236, 2237, 2238, 2239, 2240, 2241, 2242, 2243, 2244, 2245, 2246, 2247, + 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, 2259, 2260, 2261, + 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, 2271, 2272, 2273, 2274, 2275, + 2276, 2277, 2278, 2279, 2280, 2281, 2282, 2283, 2284, 2285, 2286, 2287, 2288, 2289, + 2290, 2291, 2292, 2293, 2294, 2295, 2296, 2297, 2298, 2299, 2300, 2301, 2302, 2303, + 2304, 2305, 2306, 2307, 2308, 2309, 2310, 2311, 2312, 2313, 2314, 2315, 2316, 2317, + 2318, 2319, 2320, 2321, 2322, 2323, 2324, 2325, 2326, 2327, 2328, 2329, 2330, 2331, + 2332, 2333, 2334, 2335, 2336, 2337, 2338, 2339, 2340, 2341, 2342, 2343, 2344, 2345, + 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, 2359, + 2360, 2361, 2362, 2363, 2364, 2365, 2366, 2367, 2368, 2369, 2370, 2371, 2372, 2373, + 2374, 2375, 2376, 2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, + 2388, 2389, 2390, 2391, 2392, 2393, 2394, 2395, 2396, 2397, 2398, 2399, 2400, 2401, + 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 2410, 2411, 2412, 2413, 2414, 2415, + 2416, 2417, 2418, 2419, 2420, 2421, 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2429, + 2430, 2431, 2432, 2433, 2434, 2435, 2436, 2437, 2438, 2439, 2440, 2441, 2442, 2443, + 2444, 2445, 2446, 2447, 2448, 2449, 2450, 2451, 2452, 2453, 2454, 2455, 2456, 2457, + 2458, 2459, 2460, 2461, 2462, 2463, 2464, 2465, 2466, 2467, 2468, 2469, 2470, 2471, + 2472, 2473, 2474, 2475, 2476, 2477, 2478, 2479, 2480, 2481, 2482, 2483, 2484, 2485, + 2486, 2487, 2488, 2489, 2490, 2491, 2492, 2493, 2494, 2495, 2496, 2497, 2498, 2499, + 2500 + ]), + vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, + 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, + 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, + 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, + 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, + 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, + 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, + 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, + 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, + 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, + 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, + 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, + 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, + 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, + 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, + 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, + 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, + 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, + 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, + 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, + 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, + 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, + 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, + 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, + 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, + 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, + 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, + 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, + 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, + 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, + 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, + 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, + 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, + 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, + 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, + 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, + 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, + 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, + 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, + 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, + 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, + 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, + 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, + 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, + 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, + 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, + 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, + 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, + 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, + 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, + 1156, 1157, 1158, 1159, 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, + 1170, 1171, 1172, 1173, 1174, 1175, 1176, 1177, 1178, 1179, 1180, 1181, 1182, 1183, + 1184, 1185, 1186, 1187, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197, + 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, + 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220, 1221, 1222, 1223, 1224, 1225, + 1226, 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236, 1237, 1238, 1239, + 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253, + 1254, 1255, 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, 1267, + 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, 1281, + 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295, + 1296, 1297, 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, + 1310, 1311, 1312, 1313, 1314, 1315, 1316, 1317, 1318, 1319, 1320, 1321, 1322, 1323, + 1324, 1325, 1326, 1327, 1328, 1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, + 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351, + 1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1365, + 1366, 1367, 1368, 1369, 1370, 1371, 1372, 1373, 1374, 1375, 1376, 1377, 1378, 1379, + 1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, 1391, 1392, 1393, + 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, + 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1415, 1416, 1417, 1418, 1419, 1420, 1421, + 1422, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1430, 1431, 1432, 1433, 1434, 1435, + 1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444, 1445, 1446, 1447, 1448, 1449, + 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457, 1458, 1459, 1460, 1461, 1462, 1463, + 1464, 1465, 1466, 1467, 1468, 1469, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, + 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1485, 1486, 1487, 1488, 1489, 1490, 1491, + 1492, 1493, 1494, 1495, 1496, 1497, 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, + 1506, 1507, 1508, 1509, 1510, 1511, 1512, 1513, 1514, 1515, 1516, 1517, 1518, 1519, + 1520, 1521, 1522, 1523, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1533, + 1534, 1535, 1536, 1537, 1538, 1539, 1540, 1541, 1542, 1543, 1544, 1545, 1546, 1547, + 1548, 1549, 1550, 1551, 1552, 1553, 1554, 1555, 1556, 1557, 1558, 1559, 1560, 1561, + 1562, 1563, 1564, 1565, 1566, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1575, + 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, + 1590, 1591, 1592, 1593, 1594, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1602, 1603, + 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615, 1616, 1617, + 1618, 1619, 1620, 1621, 1622, 1623, 1624, 1625, 1626, 1627, 1628, 1629, 1630, 1631, + 1632, 1633, 1634, 1635, 1636, 1637, 1638, 1639, 1640, 1641, 1642, 1643, 1644, 1645, + 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653, 1654, 1655, 1656, 1657, 1658, 1659, + 1660, 1661, 1662, 1663, 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, 1672, 1673, + 1674, 1675, 1676, 1677, 1678, 1679, 1680, 1681, 1682, 1683, 1684, 1685, 1686, 1687, + 1688, 1689, 1690, 1691, 1692, 1693, 1694, 1695, 1696, 1697, 1698, 1699, 1700, 1701, + 1702, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 1714, 1715, + 1716, 1717, 1718, 1719, 1720, 1721, 1722, 1723, 1724, 1725, 1726, 1727, 1728, 1729, + 1730, 1731, 1732, 1733, 1734, 1735, 1736, 1737, 1738, 1739, 1740, 1741, 1742, 1743, + 1744, 1745, 1746, 1747, 1748, 1749, 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, + 1758, 1759, 1760, 1761, 1762, 1763, 1764, 1765, 1766, 1767, 1768, 1769, 1770, 1771, + 1772, 1773, 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, 1782, 1783, 1784, 1785, + 1786, 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1794, 1795, 1796, 1797, 1798, 1799, + 1800, 1801, 1802, 1803, 1804, 1805, 1806, 1807, 1808, 1809, 1810, 1811, 1812, 1813, + 1814, 1815, 1816, 1817, 1818, 1819, 1820, 1821, 1822, 1823, 1824, 1825, 1826, 1827, + 1828, 1829, 1830, 1831, 1832, 1833, 1834, 1835, 1836, 1837, 1838, 1839, 1840, 1841, + 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849, 1850, 1851, 1852, 1853, 1854, 1855, + 1856, 1857, 1858, 1859, 1860, 1861, 1862, 1863, 1864, 1865, 1866, 1867, 1868, 1869, + 1870, 1871, 1872, 1873, 1874, 1875, 1876, 1877, 1878, 1879, 1880, 1881, 1882, 1883, + 1884, 1885, 1886, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894, 1895, 1896, 1897, + 1898, 1899, 1900, 1901, 1902, 1903, 1904, 1905, 1906, 1907, 1908, 1909, 1910, 1911, + 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920, 1921, 1922, 1923, 1924, 1925, + 1926, 1927, 1928, 1929, 1930, 1931, 1932, 1933, 1934, 1935, 1936, 1937, 1938, 1939, + 1940, 1941, 1942, 1943, 1944, 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, + 1954, 1955, 1956, 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, + 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, + 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, + 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, + 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, + 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, + 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, + 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079, + 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, + 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, + 2108, 2109, 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, + 2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, 2131, 2132, 2133, 2134, 2135, + 2136, 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, 2149, + 2150, 2151, 2152, 2153, 2154, 2155, 2156, 2157, 2158, 2159, 2160, 2161, 2162, 2163, + 2164, 2165, 2166, 2167, 2168, 2169, 2170, 2171, 2172, 2173, 2174, 2175, 2176, 2177, + 2178, 2179, 2180, 2181, 2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189, 2190, 2191, + 2192, 2193, 2194, 2195, 2196, 2197, 2198, 2199, 2200, 2201, 2202, 2203, 2204, 2205, + 2206, 2207, 2208, 2209, 2210, 2211, 2212, 2213, 2214, 2215, 2216, 2217, 2218, 2219, + 2220, 2221, 2222, 2223, 2224, 2225, 2226, 2227, 2228, 2229, 2230, 2231, 2232, 2233, + 2234, 2235, 2236, 2237, 2238, 2239, 2240, 2241, 2242, 2243, 2244, 2245, 2246, 2247, + 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, 2259, 2260, 2261, + 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, 2271, 2272, 2273, 2274, 2275, + 2276, 2277, 2278, 2279, 2280, 2281, 2282, 2283, 2284, 2285, 2286, 2287, 2288, 2289, + 2290, 2291, 2292, 2293, 2294, 2295, 2296, 2297, 2298, 2299, 2300, 2301, 2302, 2303, + 2304, 2305, 2306, 2307, 2308, 2309, 2310, 2311, 2312, 2313, 2314, 2315, 2316, 2317, + 2318, 2319, 2320, 2321, 2322, 2323, 2324, 2325, 2326, 2327, 2328, 2329, 2330, 2331, + 2332, 2333, 2334, 2335, 2336, 2337, 2338, 2339, 2340, 2341, 2342, 2343, 2344, 2345, + 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, 2359, + 2360, 2361, 2362, 2363, 2364, 2365, 2366, 2367, 2368, 2369, 2370, 2371, 2372, 2373, + 2374, 2375, 2376, 2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, + 2388, 2389, 2390, 2391, 2392, 2393, 2394, 2395, 2396, 2397, 2398, 2399, 2400, 2401, + 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 2410, 2411, 2412, 2413, 2414, 2415, + 2416, 2417, 2418, 2419, 2420, 2421, 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2429, + 2430, 2431, 2432, 2433, 2434, 2435, 2436, 2437, 2438, 2439, 2440, 2441, 2442, 2443, + 2444, 2445, 2446, 2447, 2448, 2449, 2450, 2451, 2452, 2453, 2454, 2455, 2456, 2457, + 2458, 2459, 2460, 2461, 2462, 2463, 2464, 2465, 2466, 2467, 2468, 2469, 2470, 2471, + 2472, 2473, 2474, 2475, 2476, 2477, 2478, 2479, 2480, 2481, 2482, 2483, 2484, 2485, + 2486, 2487, 2488, 2489, 2490, 2491, 2492, 2493, 2494, 2495, 2496, 2497, 2498, 2499, + 2500 + ] + ); + } + + #[test] + fn test_negative_elements() { + assert_eq!(longest_increasing_subsequence(vec![-2, -1]), vec![-2, -1]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/longest_continuous_increasing_subsequence.md b/codes/src/algorithms/dynamic-programming/longest_continuous_increasing_subsequence.md new file mode 100644 index 00000000..736dc0e4 --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/longest_continuous_increasing_subsequence.md @@ -0,0 +1,78 @@ +# 最长连续递增序列 + +```rust +pub fn longest_continuous_increasing_subsequence(input_array: &[T]) -> &[T] { + let length: usize = input_array.len(); + + //Handle the base cases + if length <= 1 { + return input_array; + } + + //Create the array to store the longest subsequence at each location + let mut tracking_vec = vec![1; length]; + + //Iterate through the input and store longest subsequences at each location in the vector + for i in (0..length - 1).rev() { + if input_array[i] < input_array[i + 1] { + tracking_vec[i] = tracking_vec[i + 1] + 1; + } + } + + //Find the longest subsequence + let mut max_index: usize = 0; + let mut max_value: i32 = 0; + for (index, value) in tracking_vec.iter().enumerate() { + if value > &max_value { + max_value = *value; + max_index = index; + } + } + + &input_array[max_index..max_index + max_value as usize] +} + +#[cfg(test)] +mod tests { + use super::longest_continuous_increasing_subsequence; + + #[test] + fn test_longest_increasing_subsequence() { + //Base Cases + let base_case_array: [i32; 0] = []; + assert_eq!( + &longest_continuous_increasing_subsequence(&base_case_array), + &[] + ); + assert_eq!(&longest_continuous_increasing_subsequence(&[1]), &[1]); + + //Normal i32 Cases + assert_eq!( + &longest_continuous_increasing_subsequence(&[1, 2, 3, 4]), + &[1, 2, 3, 4] + ); + assert_eq!( + &longest_continuous_increasing_subsequence(&[1, 2, 2, 3, 4, 2]), + &[2, 3, 4] + ); + assert_eq!( + &longest_continuous_increasing_subsequence(&[5, 4, 3, 2, 1]), + &[5] + ); + assert_eq!( + &longest_continuous_increasing_subsequence(&[5, 4, 3, 4, 2, 1]), + &[3, 4] + ); + + //Non-Numeric case + assert_eq!( + &longest_continuous_increasing_subsequence(&['a', 'b', 'c']), + &['a', 'b', 'c'] + ); + assert_eq!( + &longest_continuous_increasing_subsequence(&['d', 'c', 'd']), + &['c', 'd'] + ); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/maximal-square.md b/codes/src/algorithms/dynamic-programming/maximal-square.md new file mode 100644 index 00000000..4d44058d --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/maximal-square.md @@ -0,0 +1,68 @@ +# 最大正方形 + +```rust +use std::cmp::max; +use std::cmp::min; + +/// Maximal Square +/// Given an m x n binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area. +/// https://leetcode.com/problems/maximal-square/ +/// +/// Arguments: +/// * `matrix` - an array of integer array +/// Complexity +/// - time complexity: O(n^2), +/// - space complexity: O(n), +pub fn maximal_square(matrix: &mut Vec>) -> i32 { + if matrix.is_empty() { + return 0; + } + + let rows = matrix.len(); + let cols = matrix[0].len(); + let mut result: i32 = 0; + + for row in 0..rows { + for col in 0..cols { + if matrix[row][col] == 1 { + if row == 0 || col == 0 { + result = max(result, 1); + } else { + let temp = min(matrix[row - 1][col - 1], matrix[row - 1][col]); + + let count: i32 = min(temp, matrix[row][col - 1]) + 1; + result = max(result, count); + + matrix[row][col] = count; + } + } + } + } + + result * result +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { + assert_eq!(maximal_square(&mut vec![]), 0); + + let mut matrix = vec![vec![0, 1], vec![1, 0]]; + assert_eq!(maximal_square(&mut matrix), 1); + + let mut matrix = vec![ + vec![1, 0, 1, 0, 0], + vec![1, 0, 1, 1, 1], + vec![1, 1, 1, 1, 1], + vec![1, 0, 0, 1, 0], + ]; + assert_eq!(maximal_square(&mut matrix), 4); + + let mut matrix = vec![vec![0]]; + assert_eq!(maximal_square(&mut matrix), 0); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/maximal-subarray.md b/codes/src/algorithms/dynamic-programming/maximal-subarray.md new file mode 100644 index 00000000..a8e770e7 --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/maximal-subarray.md @@ -0,0 +1,66 @@ +# 最大子数组 + +```rust +/// ## maximum subarray via Dynamic Programming + +/// maximum_subarray(array) find the subarray (containing at least one number) which has the largest sum +/// and return its sum. +/// +/// A subarray is a contiguous part of an array. +/// +/// Arguments: +/// * `array` - an integer array +/// Complexity +/// - time complexity: O(array.length), +/// - space complexity: O(array.length), +pub fn maximum_subarray(array: &[i32]) -> i32 { + let mut dp = vec![0; array.len()]; + dp[0] = array[0]; + let mut result = dp[0]; + + for i in 1..array.len() { + if dp[i - 1] > 0 { + dp[i] = dp[i - 1] + array[i]; + } else { + dp[i] = array[i]; + } + result = result.max(dp[i]); + } + + result +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn non_negative() { + //the maximum value: 1 + 0 + 5 + 8 = 14 + let array = vec![1, 0, 5, 8]; + assert_eq!(maximum_subarray(&array), 14); + } + + #[test] + fn negative() { + //the maximum value: -1 + let array = vec![-3, -1, -8, -2]; + assert_eq!(maximum_subarray(&array), -1); + } + + #[test] + fn normal() { + //the maximum value: 3 + (-2) + 5 = 6 + let array = vec![-4, 3, -2, 5, -8]; + assert_eq!(maximum_subarray(&array), 6); + } + + #[test] + fn single_element() { + let array = vec![6]; + assert_eq!(maximum_subarray(&array), 6); + let array = vec![-6]; + assert_eq!(maximum_subarray(&array), -6); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/dynamic-programming/rod-cutting.md b/codes/src/algorithms/dynamic-programming/rod-cutting.md new file mode 100644 index 00000000..07f289bf --- /dev/null +++ b/codes/src/algorithms/dynamic-programming/rod-cutting.md @@ -0,0 +1,59 @@ +# 棒的切割 + +```rust +//! Solves the rod-cutting problem +use std::cmp::max; + +/// `rod_cut(p)` returns the maximum possible profit if a rod of length `n` = `p.len()` +/// is cut into up to `n` pieces, where the profit gained from each piece of length +/// `l` is determined by `p[l - 1]` and the total profit is the sum of the profit +/// gained from each piece. +/// +/// # Arguments +/// - `p` - profit for rods of length 1 to n inclusive +/// +/// # Complexity +/// - time complexity: O(n^2), +/// - space complexity: O(n^2), +/// +/// where n is the length of `p`. +pub fn rod_cut(p: &[usize]) -> usize { + let n = p.len(); + // f is the dynamic programming table + let mut f = vec![0; n]; + + for i in 0..n { + let mut max_price = p[i]; + for j in 1..=i { + max_price = max(max_price, p[j - 1] + f[i - j]); + } + f[i] = max_price; + } + + // accomodate for input with length zero + if n != 0 { + f[n - 1] + } else { + 0 + } +} + +#[cfg(test)] +mod tests { + use super::rod_cut; + + #[test] + fn test_rod_cut() { + assert_eq!(0, rod_cut(&[])); + assert_eq!(15, rod_cut(&[5, 8, 2])); + assert_eq!(10, rod_cut(&[1, 5, 8, 9])); + assert_eq!(25, rod_cut(&[5, 8, 2, 1, 7])); + assert_eq!(87, rod_cut(&[0, 0, 0, 0, 0, 87])); + assert_eq!(49, rod_cut(&[7, 6, 5, 4, 3, 2, 1])); + assert_eq!(22, rod_cut(&[1, 5, 8, 9, 10, 17, 17, 20])); + assert_eq!(60, rod_cut(&[6, 4, 8, 2, 5, 8, 2, 3, 7, 11])); + assert_eq!(30, rod_cut(&[1, 5, 8, 9, 10, 17, 17, 20, 24, 30])); + assert_eq!(12, rod_cut(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/general/convex-hull.md b/codes/src/algorithms/general/convex-hull.md new file mode 100644 index 00000000..0c84eb2b --- /dev/null +++ b/codes/src/algorithms/general/convex-hull.md @@ -0,0 +1,177 @@ +# 凸包算法 + +```rust +use std::cmp::Ordering::Equal; + +fn sort_by_min_angle(pts: &[(f64, f64)], min: &(f64, f64)) -> Vec<(f64, f64)> { + let mut points: Vec<(f64, f64, (f64, f64))> = pts + .iter() + .map(|x| { + ( + ((x.1 - min.1) as f64).atan2((x.0 - min.0) as f64), + // angle + ((x.1 - min.1) as f64).hypot((x.0 - min.0) as f64), + // distance (we want the closest to be first) + *x, + ) + }) + .collect(); + points.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Equal)); + points.into_iter().map(|x| x.2).collect() +} + +// calculates the z coordinate of the vector product of vectors ab and ac +fn calc_z_coord_vector_product(a: &(f64, f64), b: &(f64, f64), c: &(f64, f64)) -> f64 { + (b.0 - a.0) * (c.1 - a.1) - (c.0 - a.0) * (b.1 - a.1) +} + +/* + If three points are aligned and are part of the convex hull then the three are kept. + If one doesn't want to keep those points, it is easy to iterate the answer and remove them. + The first point is the one with the lowest y-coordinate and the lowest x-coordinate. + Points are then given counter-clockwise, and the closest one is given first if needed. +*/ +pub fn convex_hull_graham(pts: &[(f64, f64)]) -> Vec<(f64, f64)> { + if pts.is_empty() { + return vec![]; + } + + let mut stack: Vec<(f64, f64)> = vec![]; + let min = pts + .iter() + .min_by(|a, b| { + let ord = a.1.partial_cmp(&b.1).unwrap_or(Equal); + match ord { + Equal => a.0.partial_cmp(&b.0).unwrap_or(Equal), + o => o, + } + }) + .unwrap(); + let points = sort_by_min_angle(pts, min); + + if points.len() <= 3 { + return points; + } + + for point in points { + while stack.len() > 1 + && calc_z_coord_vector_product(&stack[stack.len() - 2], &stack[stack.len() - 1], &point) + < 0. + { + stack.pop(); + } + stack.push(point); + } + + stack +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty() { + assert_eq!(convex_hull_graham(&vec![]), vec![]); + } + + #[test] + fn not_enough_points() { + let list = vec![(0f64, 0f64)]; + assert_eq!(convex_hull_graham(&list), list); + } + + #[test] + fn not_enough_points1() { + let list = vec![(2f64, 2f64), (1f64, 1f64), (0f64, 0f64)]; + let ans = vec![(0f64, 0f64), (1f64, 1f64), (2f64, 2f64)]; + assert_eq!(convex_hull_graham(&list), ans); + } + + #[test] + fn not_enough_points2() { + let list = vec![(2f64, 2f64), (1f64, 2f64), (0f64, 0f64)]; + let ans = vec![(0f64, 0f64), (2f64, 2f64), (1f64, 2f64)]; + assert_eq!(convex_hull_graham(&list), ans); + } + + #[test] + // from https://codegolf.stackexchange.com/questions/11035/find-the-convex-hull-of-a-set-of-2d-points + fn lots_of_points() { + let list = vec![ + (4.4, 14.), + (6.7, 15.25), + (6.9, 12.8), + (2.1, 11.1), + (9.5, 14.9), + (13.2, 11.9), + (10.3, 12.3), + (6.8, 9.5), + (3.3, 7.7), + (0.6, 5.1), + (5.3, 2.4), + (8.45, 4.7), + (11.5, 9.6), + (13.8, 7.3), + (12.9, 3.1), + (11., 1.1), + ]; + let ans = vec![ + (11., 1.1), + (12.9, 3.1), + (13.8, 7.3), + (13.2, 11.9), + (9.5, 14.9), + (6.7, 15.25), + (4.4, 14.), + (2.1, 11.1), + (0.6, 5.1), + (5.3, 2.4), + ]; + + assert_eq!(convex_hull_graham(&list), ans); + } + + #[test] + // from https://codegolf.stackexchange.com/questions/11035/find-the-convex-hull-of-a-set-of-2d-points + fn lots_of_points2() { + let list = vec![ + (1., 0.), + (1., 1.), + (1., -1.), + (0.68957, 0.283647), + (0.909487, 0.644276), + (0.0361877, 0.803816), + (0.583004, 0.91555), + (-0.748169, 0.210483), + (-0.553528, -0.967036), + (0.316709, -0.153861), + (-0.79267, 0.585945), + (-0.700164, -0.750994), + (0.452273, -0.604434), + (-0.79134, -0.249902), + (-0.594918, -0.397574), + (-0.547371, -0.434041), + (0.958132, -0.499614), + (0.039941, 0.0990732), + (-0.891471, -0.464943), + (0.513187, -0.457062), + (-0.930053, 0.60341), + (0.656995, 0.854205), + ]; + let ans = vec![ + (1., -1.), + (1., 0.), + (1., 1.), + (0.583004, 0.91555), + (0.0361877, 0.803816), + (-0.930053, 0.60341), + (-0.891471, -0.464943), + (-0.700164, -0.750994), + (-0.553528, -0.967036), + ]; + + assert_eq!(convex_hull_graham(&list), ans); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/general/hanoi.md b/codes/src/algorithms/general/hanoi.md new file mode 100644 index 00000000..fc078caf --- /dev/null +++ b/codes/src/algorithms/general/hanoi.md @@ -0,0 +1,25 @@ +# 汉诺塔算法 + +```rust +pub fn hanoi(n: i32, from: i32, to: i32, via: i32, moves: &mut Vec<(i32, i32)>) { + if n > 0 { + hanoi(n - 1, from, via, to, moves); + moves.push((from, to)); + hanoi(n - 1, via, to, from, moves); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn hanoi_simple() { + let correct_solution: Vec<(i32, i32)> = + vec![(1, 3), (1, 2), (3, 2), (1, 3), (2, 1), (2, 3), (1, 3)]; + let mut our_solution: Vec<(i32, i32)> = Vec::new(); + hanoi(3, 1, 3, 2, &mut our_solution); + assert_eq!(correct_solution, our_solution); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/general/index.md b/codes/src/algorithms/general/index.md new file mode 100644 index 00000000..91da4b5c --- /dev/null +++ b/codes/src/algorithms/general/index.md @@ -0,0 +1 @@ +# 常用算法 diff --git a/codes/src/algorithms/general/kmeans.md b/codes/src/algorithms/general/kmeans.md new file mode 100644 index 00000000..da8cccb7 --- /dev/null +++ b/codes/src/algorithms/general/kmeans.md @@ -0,0 +1,180 @@ +# K-Means算法 + +```rust +// Macro to implement kmeans for both f64 and f32 without writing everything +// twice or importing the `num` crate +macro_rules! impl_kmeans { + ($kind: ty, $modname: ident) => { + // Since we can't overload methods in rust, we have to use namespacing + pub mod $modname { + use std::$modname::INFINITY; + + /// computes sum of squared deviation between two identically sized vectors + /// `x`, and `y`. + fn distance(x: &[$kind], y: &[$kind]) -> $kind { + x.iter() + .zip(y.iter()) + .fold(0.0, |dist, (&xi, &yi)| dist + (xi - yi).powi(2)) + } + + /// Returns a vector containing the indices zi in {0, ..., K-1} of + /// the centroid nearest to each datum. + fn nearest_centroids(xs: &[Vec<$kind>], centroids: &[Vec<$kind>]) -> Vec { + xs.iter() + .map(|xi| { + // Find the argmin by folding using a tuple containing the argmin + // and the minimum distance. + let (argmin, _) = centroids.iter().enumerate().fold( + (0_usize, INFINITY), + |(min_ix, min_dist), (ix, ci)| { + let dist = distance(xi, ci); + if dist < min_dist { + (ix, dist) + } else { + (min_ix, min_dist) + } + }, + ); + argmin + }) + .collect() + } + + /// Recompute the centroids given the current clustering + fn recompute_centroids( + xs: &[Vec<$kind>], + clustering: &[usize], + k: usize, + ) -> Vec> { + let ndims = xs[0].len(); + + // NOTE: Kind of inefficient because we sweep all the data from each of the + // k centroids. + (0..k) + .map(|cluster_ix| { + let mut centroid: Vec<$kind> = vec![0.0; ndims]; + let mut n_cluster: $kind = 0.0; + xs.iter().zip(clustering.iter()).for_each(|(xi, &zi)| { + if zi == cluster_ix { + n_cluster += 1.0; + xi.iter().enumerate().for_each(|(j, &x_ij)| { + centroid[j] += x_ij; + }); + } + }); + centroid.iter().map(|&c_j| c_j / n_cluster).collect() + }) + .collect() + } + + /// Assign the N D-dimensional data, `xs`, to `k` clusters using K-Means clustering + pub fn kmeans(xs: Vec>, k: usize) -> Vec { + assert!(xs.len() >= k); + + // Rather than pulling in a dependency to radomly select the staring + // points for the centroids, we're going to deterministally choose them by + // slecting evenly spaced points in `xs` + let n_per_cluster: usize = xs.len() / k; + let centroids: Vec> = + (0..k).map(|j| xs[j * n_per_cluster].clone()).collect(); + + let mut clustering = nearest_centroids(&xs, ¢roids); + + loop { + let centroids = recompute_centroids(&xs, &clustering, k); + let new_clustering = nearest_centroids(&xs, ¢roids); + + // loop until the clustering doesn't change after the new centroids are computed + if new_clustering + .iter() + .zip(clustering.iter()) + .all(|(&za, &zb)| za == zb) + { + // We need to use `return` to break out of the `loop` + return clustering; + } else { + clustering = new_clustering; + } + } + } + } + }; +} + +// generate code for kmeans for f32 and f64 data +impl_kmeans!(f64, f64); +impl_kmeans!(f32, f32); + +#[cfg(test)] +mod test { + use self::super::f64::kmeans; + + #[test] + fn easy_univariate_clustering() { + let xs: Vec> = vec![ + vec![-1.1], + vec![-1.2], + vec![-1.3], + vec![-1.4], + vec![1.1], + vec![1.2], + vec![1.3], + vec![1.4], + ]; + let clustering = kmeans(xs, 2); + assert_eq!(clustering, vec![0, 0, 0, 0, 1, 1, 1, 1]); + } + + #[test] + fn easy_univariate_clustering_odd_number_of_data() { + let xs: Vec> = vec![ + vec![-1.1], + vec![-1.2], + vec![-1.3], + vec![-1.4], + vec![1.1], + vec![1.2], + vec![1.3], + vec![1.4], + vec![1.5], + ]; + let clustering = kmeans(xs, 2); + assert_eq!(clustering, vec![0, 0, 0, 0, 1, 1, 1, 1, 1]); + } + + #[test] + fn easy_bivariate_clustering() { + let xs: Vec> = vec![ + vec![-1.1, 0.2], + vec![-1.2, 0.3], + vec![-1.3, 0.1], + vec![-1.4, 0.4], + vec![1.1, -1.1], + vec![1.2, -1.0], + vec![1.3, -1.2], + vec![1.4, -1.3], + ]; + let clustering = kmeans(xs, 2); + assert_eq!(clustering, vec![0, 0, 0, 0, 1, 1, 1, 1]); + } + + #[test] + fn high_dims() { + let xs: Vec> = vec![ + vec![-2.7825343, -1.7604825, -5.5550113, -2.9752946, -2.7874138], + vec![-2.9847919, -3.8209332, -2.1531757, -2.2710119, -2.3582877], + vec![-3.0109320, -2.2366132, -2.8048492, -1.2632331, -4.5755581], + vec![-2.8432186, -1.0383805, -2.2022826, -2.7435962, -2.0013399], + vec![-2.6638082, -3.5520086, -1.3684702, -2.1562444, -1.3186447], + vec![1.7409171, 1.9687576, 4.7162628, 4.5743537, 3.7905611], + vec![3.2932369, 2.8508700, 2.5580937, 2.0437325, 4.2192562], + vec![2.5843321, 2.8329818, 2.1329531, 3.2562319, 2.4878733], + vec![2.1859638, 3.2880048, 3.7018615, 2.3641232, 1.6281994], + vec![2.6201773, 0.9006588, 2.6774097, 1.8188620, 1.6076493], + ]; + + let clustering = kmeans(xs, 2); + assert_eq!(clustering, vec![0, 0, 0, 0, 0, 1, 1, 1, 1, 1]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/general/nqueens.md b/codes/src/algorithms/general/nqueens.md new file mode 100644 index 00000000..0b924d6d --- /dev/null +++ b/codes/src/algorithms/general/nqueens.md @@ -0,0 +1,152 @@ +# N皇后算法 + +```rust +#[allow(unused_imports)] +use std::env::args; + +#[allow(dead_code)] +fn main() { + let mut board_width = 0; + + for arg in args() { + board_width = match arg.parse() { + Ok(x) => x, + _ => 0, + }; + + if board_width != 0 { + break; + } + } + + if board_width < 4 { + println!( + "Running algorithm with 8 as a default. Specify an alternative Chess board size for \ + N-Queens as a command line argument.\n" + ); + board_width = 8; + } + + let board = match nqueens(board_width) { + Ok(success) => success, + Err(err) => panic!("{}", err), + }; + + println!("N-Queens {} by {} board result:", board_width, board_width); + print_board(&board); +} + +/* +The n-Queens search is a backtracking algorithm. Each row of the Chess board where a Queen is +placed is dependent on all earlier rows. As only one Queen can fit per row, a one-dimensional +integer array is used to represent the Queen's offset on each row. +*/ +pub fn nqueens(board_width: i64) -> Result, &'static str> { + let mut board_rows = vec![0; board_width as usize]; + let mut conflict; + let mut current_row = 0; + + //Process by row up to the current active row + loop { + conflict = false; + + //Column review of previous rows + for review_index in 0..current_row { + //Calculate the diagonals of earlier rows where a Queen would be a conflict + let left = board_rows[review_index] - (current_row as i64 - review_index as i64); + let right = board_rows[review_index] + (current_row as i64 - review_index as i64); + + if board_rows[current_row] == board_rows[review_index] + || (left >= 0 && left == board_rows[current_row]) + || (right < board_width as i64 && right == board_rows[current_row]) + { + conflict = true; + break; + } + } + + match conflict { + true => { + board_rows[current_row] += 1; + + if current_row == 0 && board_rows[current_row] == board_width { + return Err("No solution exists for specificed board size."); + } + + while board_rows[current_row] == board_width { + board_rows[current_row] = 0; + + if current_row == 0 { + return Err("No solution exists for specificed board size."); + } + + current_row -= 1; + board_rows[current_row] += 1; + } + } + _ => { + current_row += 1; + + if current_row as i64 == board_width { + break; + } + } + } + } + + Ok(board_rows) +} + +fn print_board(board: &[i64]) { + for row in 0..board.len() { + print!("{}\t", board[row as usize]); + + for column in 0..board.len() as i64 { + if board[row as usize] == column { + print!("Q"); + } else { + print!("."); + } + } + println!(); + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn check_board(board: &Vec) -> bool { + for current_row in 0..board.len() { + //Column review + for review_index in 0..current_row { + //Look for any conflict. + let left = board[review_index] - (current_row as i64 - review_index as i64); + let right = board[review_index] + (current_row as i64 - review_index as i64); + + if board[current_row] == board[review_index] + || (left >= 0 && left == board[current_row]) + || (right < board.len() as i64 && right == board[current_row]) + { + return false; + } + } + } + true + } + + #[test] + fn test_board_size_4() { + let board = nqueens(4).expect("Error propagated."); + assert_eq!(board, vec![1, 3, 0, 2]); + assert!(check_board(&board)); + } + + #[test] + fn test_board_size_7() { + let board = nqueens(7).expect("Error propagated."); + assert_eq!(board, vec![0, 2, 4, 6, 1, 3, 5]); + assert!(check_board(&board)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/general/two-sum.md b/codes/src/algorithms/general/two-sum.md new file mode 100644 index 00000000..8895544b --- /dev/null +++ b/codes/src/algorithms/general/two-sum.md @@ -0,0 +1,43 @@ +# 两数之和 + +```rust +use std::collections::HashMap; +use std::convert::TryInto; + +// Given an array of integers nums and an integer target, +// return indices of the two numbers such that they add up to target. + +pub fn two_sum(nums: Vec, target: i32) -> Vec { + let mut hash_map: HashMap = HashMap::new(); + + for (i, item) in nums.iter().enumerate() { + match hash_map.get(&(target - item)) { + Some(value) => { + return vec![i.try_into().unwrap(), *value]; + } + None => { + hash_map.insert(*item, i.try_into().unwrap()); + } + } + } + + vec![] +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test() { + let nums = vec![2, 7, 11, 15]; + assert_eq!(two_sum(nums, 9), vec![1, 0]); + + let nums = vec![3, 2, 4]; + assert_eq!(two_sum(nums, 6), vec![2, 1]); + + let nums = vec![3, 3]; + assert_eq!(two_sum(nums, 6), vec![1, 0]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/geometry/closet-points.md b/codes/src/algorithms/geometry/closet-points.md new file mode 100644 index 00000000..50e5fcf6 --- /dev/null +++ b/codes/src/algorithms/geometry/closet-points.md @@ -0,0 +1,225 @@ +# 最近点算法 + +```rust +type Point = (f64, f64); +use std::cmp::Ordering; + +fn point_cmp((a1, a2): &Point, (b1, b2): &Point) -> Ordering { + let acmp = f64_cmp(a1, b1); + match acmp { + Ordering::Equal => f64_cmp(a2, b2), + _ => acmp, + } +} + +fn f64_cmp(a: &f64, b: &f64) -> Ordering { + a.partial_cmp(b).unwrap() +} + +/// returns the two closest points +/// or None if there are zero or one point +pub fn closest_points(points: &[Point]) -> Option<(Point, Point)> { + let mut points: Vec = points.to_vec(); + points.sort_by(point_cmp); + + closest_points_aux(&points, 0, points.len()) +} + +fn dist((x1, y1): &Point, (x2, y2): &Point) -> f64 { + let dx = *x1 - *x2; + let dy = *y1 - *y2; + + (dx * dx + dy * dy).sqrt() +} + +fn closest_points_aux( + points: &[Point], + mut start: usize, + mut end: usize, +) -> Option<(Point, Point)> { + let n = end - start; + + if n <= 1 { + return None; + } + + if n <= 3 { + // bruteforce + let mut min = dist(&points[0], &points[1]); + let mut pair = (points[0], points[1]); + + for i in 0..n { + for j in (i + 1)..n { + let new = dist(&points[i], &points[j]); + if new < min { + min = new; + pair = (points[i], points[j]); + } + } + } + return Some(pair); + } + + let mid = (start + end) / 2; + let left = closest_points_aux(points, start, mid); + let right = closest_points_aux(points, mid, end); + + let (mut min_dist, mut pair) = match (left, right) { + (Some((l1, l2)), Some((r1, r2))) => { + let dl = dist(&l1, &l2); + let dr = dist(&r1, &r2); + if dl < dr { + (dl, (l1, l2)) + } else { + (dr, (r1, r2)) + } + } + (Some((a, b)), None) => (dist(&a, &b), (a, b)), + (None, Some((a, b))) => (dist(&a, &b), (a, b)), + (None, None) => unreachable!(), + }; + + let mid_x = points[mid].0; + while points[start].0 < mid_x - min_dist { + start += 1; + } + while points[end - 1].0 > mid_x + min_dist { + end -= 1; + } + + let mut mids: Vec<&Point> = points[start..end].iter().collect(); + mids.sort_by(|a, b| f64_cmp(&a.1, &b.1)); + + for (i, e) in mids.iter().enumerate() { + for k in 1..8 { + if i + k >= mids.len() { + break; + } + + let new = dist(e, mids[i + k]); + if new < min_dist { + min_dist = new; + pair = (**e, *mids[i + k]); + } + } + } + + Some(pair) +} + +#[cfg(test)] +mod tests { + use super::closest_points; + use super::Point; + + fn eq(p1: Option<(Point, Point)>, p2: Option<(Point, Point)>) -> bool { + match (p1, p2) { + (None, None) => true, + (Some((p1, p2)), Some((p3, p4))) => (p1 == p3 && p2 == p4) || (p1 == p4 && p2 == p3), + _ => false, + } + } + + macro_rules! assert_display { + ($left: expr, $right: expr) => { + assert!( + eq($left, $right), + "assertion failed: `(left == right)`\nleft: `{:?}`,\nright: `{:?}`", + $left, + $right + ) + }; + } + + #[test] + fn zero_points() { + let vals: [Point; 0] = []; + assert_display!(closest_points(&vals), None::<(Point, Point)>); + } + + #[test] + fn one_points() { + let vals = [(0., 0.)]; + assert_display!(closest_points(&vals), None::<(Point, Point)>); + } + + #[test] + fn two_points() { + let vals = [(0., 0.), (1., 1.)]; + assert_display!(closest_points(&vals), Some(((0., 0.), (1., 1.)))); + } + + #[test] + fn three_points() { + let vals = [(0., 0.), (1., 1.), (3., 3.)]; + assert_display!(closest_points(&vals), Some(((0., 0.), (1., 1.)))); + } + + #[test] + fn list_1() { + let vals = [ + (0., 0.), + (2., 1.), + (5., 2.), + (2., 3.), + (4., 0.), + (0., 4.), + (5., 6.), + (4., 4.), + (7., 3.), + (-1., 2.), + (2., 6.), + ]; + assert_display!(closest_points(&vals), Some(((2., 1.), (2., 3.)))); + } + + #[test] + fn list_2() { + let vals = [ + (1., 3.), + (4., 6.), + (8., 8.), + (7., 5.), + (5., 3.), + (10., 3.), + (7., 1.), + (8., 3.), + (4., 9.), + (4., 12.), + (4., 15.), + (7., 14.), + (8., 12.), + (6., 10.), + (4., 14.), + (2., 7.), + (3., 8.), + (5., 8.), + (6., 7.), + (8., 10.), + (6., 12.), + ]; + assert_display!(closest_points(&vals), Some(((4., 14.), (4., 15.)))); + } + + #[test] + fn vertical_points() { + let vals = [ + (0., 0.), + (0., 50.), + (0., -25.), + (0., 40.), + (0., 42.), + (0., 100.), + (0., 17.), + (0., 29.), + (0., -50.), + (0., 37.), + (0., 34.), + (0., 8.), + (0., 3.), + (0., 46.), + ]; + assert_display!(closest_points(&vals), Some(((0., 40.), (0., 42.)))); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/geometry/index.md b/codes/src/algorithms/geometry/index.md new file mode 100644 index 00000000..63edf1b1 --- /dev/null +++ b/codes/src/algorithms/geometry/index.md @@ -0,0 +1 @@ +# 几何 diff --git a/codes/src/algorithms/graph/bellman-ford.md b/codes/src/algorithms/graph/bellman-ford.md new file mode 100644 index 00000000..fd391466 --- /dev/null +++ b/codes/src/algorithms/graph/bellman-ford.md @@ -0,0 +1,272 @@ +# 最短路径-Bellman Ford + +```rust +use std::collections::BTreeMap; +use std::ops::Add; + +use std::ops::Neg; + +type Graph = BTreeMap>; + +// performs the Bellman-Ford algorithm on the given graph from the given start +// the graph is an undirected graph +// +// if there is a negative weighted loop it returns None +// else it returns a map that for each reachable vertex associates the distance and the predecessor +// since the start has no predecessor but is reachable, map[start] will be None +pub fn bellman_ford< + V: Ord + Copy, + E: Ord + Copy + Add + Neg + std::ops::Sub, +>( + graph: &Graph, + start: &V, +) -> Option>> { + let mut ans: BTreeMap> = BTreeMap::new(); + + ans.insert(*start, None); + + for _ in 1..(graph.len()) { + for (u, edges) in graph { + let dist_u = match ans.get(u) { + Some(Some((_, d))) => Some(*d), + Some(None) => None, + None => continue, + }; + + for (v, d) in edges { + match ans.get(v) { + Some(Some((_, dist))) + // if this is a longer path, do nothing + if match dist_u { + Some(dist_u) => dist_u + *d >= *dist, + None => d >= dist, + } => {} + Some(None) => { + match dist_u { + // if dist_u + d < 0 there is a negative loop going by start + // else it's just a longer path + Some(dist_u) if dist_u >= -*d => {} + // negative self edge or negative loop + _ => { + if *d > *d + *d { + return None; + } + } + }; + } + // it's a shorter path: either dist_v was infinite or it was longer than dist_u + d + _ => { + ans.insert( + *v, + Some(( + *u, + match dist_u { + Some(dist) => dist + *d, + None => *d, + }, + )), + ); + } + } + } + } + } + + for (u, edges) in graph { + for (v, d) in edges { + match (ans.get(u), ans.get(v)) { + (Some(None), Some(None)) if *d > *d + *d => return None, + (Some(None), Some(Some((_, dv)))) if d < dv => return None, + (Some(Some((_, du))), Some(None)) if *du < -*d => return None, + (Some(Some((_, du))), Some(Some((_, dv)))) if *du + *d < *dv => return None, + (_, _) => {} + } + } + } + + Some(ans) +} + +#[cfg(test)] +mod tests { + use super::{bellman_ford, Graph}; + use std::collections::BTreeMap; + + fn add_edge(graph: &mut Graph, v1: V, v2: V, c: E) { + graph.entry(v1).or_insert_with(BTreeMap::new).insert(v2, c); + graph.entry(v2).or_insert_with(BTreeMap::new); + } + + #[test] + fn single_vertex() { + let mut graph: Graph = BTreeMap::new(); + graph.insert(0, BTreeMap::new()); + + let mut dists = BTreeMap::new(); + dists.insert(0, None); + + assert_eq!(bellman_ford(&graph, &0), Some(dists)); + } + + #[test] + fn single_edge() { + let mut graph = BTreeMap::new(); + add_edge(&mut graph, 0, 1, 2); + + let mut dists_0 = BTreeMap::new(); + dists_0.insert(0, None); + dists_0.insert(1, Some((0, 2))); + + assert_eq!(bellman_ford(&graph, &0), Some(dists_0)); + + let mut dists_1 = BTreeMap::new(); + dists_1.insert(1, None); + + assert_eq!(bellman_ford(&graph, &1), Some(dists_1)); + } + + #[test] + fn tree_1() { + let mut graph = BTreeMap::new(); + let mut dists = BTreeMap::new(); + dists.insert(1, None); + for i in 1..100 { + add_edge(&mut graph, i, i * 2, i * 2); + add_edge(&mut graph, i, i * 2 + 1, i * 2 + 1); + + match dists[&i] { + Some((_, d)) => { + dists.insert(i * 2, Some((i, d + i * 2))); + dists.insert(i * 2 + 1, Some((i, d + i * 2 + 1))); + } + None => { + dists.insert(i * 2, Some((i, i * 2))); + dists.insert(i * 2 + 1, Some((i, i * 2 + 1))); + } + } + } + + assert_eq!(bellman_ford(&graph, &1), Some(dists)); + } + + #[test] + fn graph_1() { + let mut graph = BTreeMap::new(); + add_edge(&mut graph, 'a', 'c', 12); + add_edge(&mut graph, 'a', 'd', 60); + add_edge(&mut graph, 'b', 'a', 10); + add_edge(&mut graph, 'c', 'b', 20); + add_edge(&mut graph, 'c', 'd', 32); + add_edge(&mut graph, 'e', 'a', 7); + + let mut dists_a = BTreeMap::new(); + dists_a.insert('a', None); + dists_a.insert('c', Some(('a', 12))); + dists_a.insert('d', Some(('c', 44))); + dists_a.insert('b', Some(('c', 32))); + assert_eq!(bellman_ford(&graph, &'a'), Some(dists_a)); + + let mut dists_b = BTreeMap::new(); + dists_b.insert('b', None); + dists_b.insert('a', Some(('b', 10))); + dists_b.insert('c', Some(('a', 22))); + dists_b.insert('d', Some(('c', 54))); + assert_eq!(bellman_ford(&graph, &'b'), Some(dists_b)); + + let mut dists_c = BTreeMap::new(); + dists_c.insert('c', None); + dists_c.insert('b', Some(('c', 20))); + dists_c.insert('d', Some(('c', 32))); + dists_c.insert('a', Some(('b', 30))); + assert_eq!(bellman_ford(&graph, &'c'), Some(dists_c)); + + let mut dists_d = BTreeMap::new(); + dists_d.insert('d', None); + assert_eq!(bellman_ford(&graph, &'d'), Some(dists_d)); + + let mut dists_e = BTreeMap::new(); + dists_e.insert('e', None); + dists_e.insert('a', Some(('e', 7))); + dists_e.insert('c', Some(('a', 19))); + dists_e.insert('d', Some(('c', 51))); + dists_e.insert('b', Some(('c', 39))); + assert_eq!(bellman_ford(&graph, &'e'), Some(dists_e)); + } + + #[test] + fn graph_2() { + let mut graph = BTreeMap::new(); + add_edge(&mut graph, 0, 1, 6); + add_edge(&mut graph, 0, 3, 7); + add_edge(&mut graph, 1, 2, 5); + add_edge(&mut graph, 1, 3, 8); + add_edge(&mut graph, 1, 4, -4); + add_edge(&mut graph, 2, 1, -2); + add_edge(&mut graph, 3, 2, -3); + add_edge(&mut graph, 3, 4, 9); + add_edge(&mut graph, 4, 0, 3); + add_edge(&mut graph, 4, 2, 7); + + let mut dists_0 = BTreeMap::new(); + dists_0.insert(0, None); + dists_0.insert(1, Some((2, 2))); + dists_0.insert(2, Some((3, 4))); + dists_0.insert(3, Some((0, 7))); + dists_0.insert(4, Some((1, -2))); + assert_eq!(bellman_ford(&graph, &0), Some(dists_0)); + + let mut dists_1 = BTreeMap::new(); + dists_1.insert(0, Some((4, -1))); + dists_1.insert(1, None); + dists_1.insert(2, Some((4, 3))); + dists_1.insert(3, Some((0, 6))); + dists_1.insert(4, Some((1, -4))); + assert_eq!(bellman_ford(&graph, &1), Some(dists_1)); + + let mut dists_2 = BTreeMap::new(); + dists_2.insert(0, Some((4, -3))); + dists_2.insert(1, Some((2, -2))); + dists_2.insert(2, None); + dists_2.insert(3, Some((0, 4))); + dists_2.insert(4, Some((1, -6))); + assert_eq!(bellman_ford(&graph, &2), Some(dists_2)); + + let mut dists_3 = BTreeMap::new(); + dists_3.insert(0, Some((4, -6))); + dists_3.insert(1, Some((2, -5))); + dists_3.insert(2, Some((3, -3))); + dists_3.insert(3, None); + dists_3.insert(4, Some((1, -9))); + assert_eq!(bellman_ford(&graph, &3), Some(dists_3)); + + let mut dists_4 = BTreeMap::new(); + dists_4.insert(0, Some((4, 3))); + dists_4.insert(1, Some((2, 5))); + dists_4.insert(2, Some((4, 7))); + dists_4.insert(3, Some((0, 10))); + dists_4.insert(4, None); + assert_eq!(bellman_ford(&graph, &4), Some(dists_4)); + } + + #[test] + fn graph_with_negative_loop() { + let mut graph = BTreeMap::new(); + add_edge(&mut graph, 0, 1, 6); + add_edge(&mut graph, 0, 3, 7); + add_edge(&mut graph, 1, 2, 5); + add_edge(&mut graph, 1, 3, 8); + add_edge(&mut graph, 1, 4, -4); + add_edge(&mut graph, 2, 1, -4); + add_edge(&mut graph, 3, 2, -3); + add_edge(&mut graph, 3, 4, 9); + add_edge(&mut graph, 4, 0, 3); + add_edge(&mut graph, 4, 2, 7); + + assert_eq!(bellman_ford(&graph, &0), None); + assert_eq!(bellman_ford(&graph, &1), None); + assert_eq!(bellman_ford(&graph, &2), None); + assert_eq!(bellman_ford(&graph, &3), None); + assert_eq!(bellman_ford(&graph, &4), None); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/graph/breadth-first-search.md b/codes/src/algorithms/graph/breadth-first-search.md new file mode 100644 index 00000000..d2398fb4 --- /dev/null +++ b/codes/src/algorithms/graph/breadth-first-search.md @@ -0,0 +1,207 @@ +# 广度优先搜索 + +```rust +use std::collections::HashSet; +use std::collections::VecDeque; + +/// Perform a breadth-first search on Graph `graph`. +/// +/// # Parameters +/// +/// - `graph`: The graph to search. +/// - `root`: The starting node of the graph from which to begin searching. +/// - `target`: The target node for the search. +/// +/// # Returns +/// +/// If the target is found, an Optional vector is returned with the history +/// of nodes visited as its contents. +/// +/// If the target is not found or there is no path from the root, +/// `None` is returned. +/// +pub fn breadth_first_search(graph: &Graph, root: Node, target: Node) -> Option> { + let mut visited: HashSet = HashSet::new(); + let mut history: Vec = Vec::new(); + let mut queue = VecDeque::new(); + + visited.insert(root); + queue.push_back(root); + while let Some(currentnode) = queue.pop_front() { + history.push(currentnode.value()); + + // If we reach the goal, return our travel history. + if currentnode == target { + return Some(history); + } + + // Check the neighboring nodes for any that we've not visited yet. + for neighbor in currentnode.neighbors(graph) { + if !visited.contains(&neighbor) { + visited.insert(neighbor); + queue.push_back(neighbor); + } + } + } + + // All nodes were visited, yet the target was not found. + None +} + +// Data Structures + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Node(u32); + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Edge(u32, u32); + +#[derive(Clone)] +pub struct Graph { + nodes: Vec, + edges: Vec, +} + +impl Graph { + pub fn new(nodes: Vec, edges: Vec) -> Self { + Graph { nodes, edges } + } +} + +impl From for Node { + fn from(item: u32) -> Self { + Node(item) + } +} + +impl Node { + pub fn value(&self) -> u32 { + self.0 + } + + pub fn neighbors(&self, graph: &Graph) -> Vec { + graph + .edges + .iter() + .filter(|e| e.0 == self.0) + .map(|e| e.1.into()) + .collect() + } +} + +impl From<(u32, u32)> for Edge { + fn from(item: (u32, u32)) -> Self { + Edge(item.0, item.1) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /* Example graph #1: + * + * (1) <--- Root + * / \ + * (2) (3) + * / | | \ + * (4) (5) (6) (7) + * | + * (8) + */ + fn graph1() -> Graph { + let nodes = vec![1, 2, 3, 4, 5, 6, 7]; + let edges = vec![(1, 2), (1, 3), (2, 4), (2, 5), (3, 6), (3, 7), (5, 8)]; + + Graph::new( + nodes.into_iter().map(|v| v.into()).collect(), + edges.into_iter().map(|e| e.into()).collect(), + ) + } + + #[test] + fn breadth_first_search_graph1_when_node_not_found_returns_none() { + let graph = graph1(); + let root = 1; + let target = 10; + + assert_eq!( + breadth_first_search(&graph, root.into(), target.into()), + None + ); + } + + #[test] + fn breadth_first_search_graph1_when_target_8_should_evaluate_all_nodes_first() { + let graph = graph1(); + let root = 1; + let target = 8; + + let expected_path = vec![1, 2, 3, 4, 5, 6, 7, 8]; + + assert_eq!( + breadth_first_search(&graph, root.into(), target.into()), + Some(expected_path) + ); + } + + /* Example graph #2: + * + * (1) --- (2) (3) --- (4) + * / | / / + * / | / / + * / | / / + * (5) (6) --- (7) (8) + */ + fn graph2() -> Graph { + let nodes = vec![1, 2, 3, 4, 5, 6, 7, 8]; + let undirected_edges = vec![ + (1, 2), + (2, 1), + (2, 5), + (5, 2), + (2, 6), + (6, 2), + (3, 4), + (4, 3), + (3, 6), + (6, 3), + (4, 7), + (7, 4), + (6, 7), + (7, 6), + ]; + + Graph::new( + nodes.into_iter().map(|v| v.into()).collect(), + undirected_edges.into_iter().map(|e| e.into()).collect(), + ) + } + + #[test] + fn breadth_first_search_graph2_when_no_path_to_node_returns_none() { + let graph = graph2(); + let root = 8; + let target = 4; + + assert_eq!( + breadth_first_search(&graph, root.into(), target.into()), + None + ); + } + + #[test] + fn breadth_first_search_graph2_should_find_path_from_4_to_1() { + let graph = graph2(); + let root = 4; + let target = 1; + + let expected_path = vec![4, 3, 7, 6, 2, 1]; + + assert_eq!( + breadth_first_search(&graph, root.into(), target.into()), + Some(expected_path) + ); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/graph/depth-first-search.md b/codes/src/algorithms/graph/depth-first-search.md new file mode 100644 index 00000000..0d475455 --- /dev/null +++ b/codes/src/algorithms/graph/depth-first-search.md @@ -0,0 +1,195 @@ +# 深度优先搜索 + +```rust +use std::collections::HashSet; +use std::collections::VecDeque; + +// Perform a Depth First Search Algorithm to find a element in a graph +// +// Return a Optional with a vector with history of vertex visiteds +// or a None if the element not exists on the graph +pub fn depth_first_search(graph: &Graph, root: Vertex, objective: Vertex) -> Option> { + let mut visited: HashSet = HashSet::new(); + let mut history: Vec = Vec::new(); + let mut queue = VecDeque::new(); + queue.push_back(root); + + // While there is an element in the queue + // get the first element of the vertex queue + while let Some(current_vertex) = queue.pop_front() { + // Added current vertex in the history of visiteds vertex + history.push(current_vertex.value()); + + // Verify if this vertex is the objective + if current_vertex == objective { + // Return the Optional with the history of visiteds vertex + return Some(history); + } + + // For each over the neighbors of current vertex + for neighbor in current_vertex.neighbors(graph).into_iter().rev() { + // Insert in the HashSet of visiteds if this value not exist yet + if visited.insert(neighbor) { + // Add the neighbor on front of queue + queue.push_front(neighbor); + } + } + } + + // If all vertex is visited and the objective is not found + // return a Optional with None value + None +} + +// Data Structures + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Vertex(u32); +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Edge(u32, u32); +#[derive(Clone)] +pub struct Graph { + vertices: Vec, + edges: Vec, +} + +impl Graph { + pub fn new(vertices: Vec, edges: Vec) -> Self { + Graph { vertices, edges } + } +} + +impl From for Vertex { + fn from(item: u32) -> Self { + Vertex(item) + } +} + +impl Vertex { + pub fn value(&self) -> u32 { + self.0 + } + + pub fn neighbors(&self, graph: &Graph) -> VecDeque { + graph + .edges + .iter() + .filter(|e| e.0 == self.0) + .map(|e| e.1.into()) + .collect() + } +} + +impl From<(u32, u32)> for Edge { + fn from(item: (u32, u32)) -> Self { + Edge(item.0, item.1) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn find_1_fail() { + let vertices = vec![1, 2, 3, 4, 5, 6, 7]; + let edges = vec![(1, 2), (1, 3), (2, 4), (2, 5), (3, 6), (3, 7)]; + + let root = 1; + let objective = 99; + + let graph = Graph::new( + vertices.into_iter().map(|v| v.into()).collect(), + edges.into_iter().map(|e| e.into()).collect(), + ); + + assert_eq!( + depth_first_search(&graph, root.into(), objective.into()), + None + ); + } + + #[test] + fn find_1_sucess() { + let vertices = vec![1, 2, 3, 4, 5, 6, 7]; + let edges = vec![(1, 2), (1, 3), (2, 4), (2, 5), (3, 6), (3, 7)]; + + let root = 1; + let objective = 7; + + let correct_path = vec![1, 2, 4, 5, 3, 6, 7]; + + let graph = Graph::new( + vertices.into_iter().map(|v| v.into()).collect(), + edges.into_iter().map(|e| e.into()).collect(), + ); + + assert_eq!( + depth_first_search(&graph, root.into(), objective.into()), + Some(correct_path) + ); + } + + #[test] + fn find_2_sucess() { + let vertices = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let edges = vec![ + (0, 1), + (1, 3), + (3, 2), + (2, 1), + (3, 4), + (4, 5), + (5, 7), + (7, 6), + (6, 4), + ]; + + let root = 0; + let objective = 6; + + let correct_path = vec![0, 1, 3, 2, 4, 5, 7, 6]; + + let graph = Graph::new( + vertices.into_iter().map(|v| v.into()).collect(), + edges.into_iter().map(|e| e.into()).collect(), + ); + + assert_eq!( + depth_first_search(&graph, root.into(), objective.into()), + Some(correct_path) + ); + } + + #[test] + fn find_3_sucess() { + let vertices = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let edges = vec![ + (0, 1), + (1, 3), + (3, 2), + (2, 1), + (3, 4), + (4, 5), + (5, 7), + (7, 6), + (6, 4), + ]; + + let root = 0; + let objective = 4; + + let correct_path = vec![0, 1, 3, 2, 4]; + + let graph = Graph::new( + vertices.into_iter().map(|v| v.into()).collect(), + edges.into_iter().map(|e| e.into()).collect(), + ); + + assert_eq!( + depth_first_search(&graph, root.into(), objective.into()), + Some(correct_path) + ); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/graph/depth-first-tic-tac-toe.md b/codes/src/algorithms/graph/depth-first-tic-tac-toe.md new file mode 100644 index 00000000..ce39fbe1 --- /dev/null +++ b/codes/src/algorithms/graph/depth-first-tic-tac-toe.md @@ -0,0 +1,392 @@ +# 深度优先Tic Tac Toe + +```rust +#[allow(unused_imports)] +use std::io; + +//Interactive Tic-Tac-Toe play needs the "rand = "0.8.3" crate. +//#[cfg(not(test))] +//extern crate rand; +//#[cfg(not(test))] +//use rand::Rng; + +#[derive(Copy, Clone, PartialEq, Debug)] +struct Position { + x: u8, + y: u8, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Players { + Blank, + PlayerX, + PlayerO, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +struct SinglePlayAction { + position: Position, + side: Players, +} + +#[derive(Clone, PartialEq, Debug)] +pub struct PlayActions { + positions: Vec, + side: Players, +} + +#[allow(dead_code)] +#[cfg(not(test))] +fn main() { + let mut board = vec![vec![Players::Blank; 3]; 3]; + + while !available_positions(&board).is_empty() + && !win_check(Players::PlayerX, &board) + && !win_check(Players::PlayerO, &board) + { + display_board(&board); + println!("Type in coordinate for X mark to be played. ie. a1 etc."); + let mut input = String::new(); + io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + + let mut move_position: Option = None; + input.make_ascii_lowercase(); + let bytes = input.trim().trim_start().as_bytes(); + if bytes.len() as u32 == 2 + && (bytes[0] as char).is_alphabetic() + && (bytes[1] as char).is_numeric() + { + let column: u8 = bytes[0] - b'a'; + let row: u8 = bytes[1] - b'1'; + + if column <= 2 && row <= 2 { + move_position = Some(Position { x: column, y: row }); + } + } + + //Take the validated user input coordinate and use it. + if let Some(move_pos) = move_position { + let open_positions = available_positions(&board); + + let mut search = open_positions.iter(); + let result = search.find(|&&x| x == move_pos); + if result.is_none() { + println!("Not a valid empty coordinate."); + continue; + } else { + board[move_pos.y as usize][move_pos.x as usize] = Players::PlayerX; + + if win_check(Players::PlayerX, &board) { + display_board(&board); + println!("Player X Wins!"); + return; + } + } + + //Find the best game plays from the current board state + let recusion_result = minimax(Players::PlayerO, &board); + match recusion_result { + Some(x) => { + //Interactive Tic-Tac-Toe play needs the "rand = "0.8.3" crate. + //#[cfg(not(test))] + //let random_selection = rand::thread_rng().gen_range(0..x.positions.len()); + let random_selection = 0; + + let response_pos = x.positions[random_selection]; + board[response_pos.y as usize][response_pos.x as usize] = Players::PlayerO; + if win_check(Players::PlayerO, &board) { + display_board(&board); + println!("Player O Wins!"); + return; + } + } + + None => { + display_board(&board); + println!("Draw game."); + return; + } + } + } + } +} + +#[allow(dead_code)] +fn display_board(board: &[Vec]) { + println!(); + for (y, board_row) in board.iter().enumerate() { + print!("{} ", (y + 1)); + for board_cell in board_row { + match board_cell { + Players::PlayerX => print!("X "), + Players::PlayerO => print!("O "), + Players::Blank => print!("_ "), + } + } + println!(); + } + println!(" a b c"); +} + +fn available_positions(board: &[Vec]) -> Vec { + let mut available: Vec = Vec::new(); + for (y, board_row) in board.iter().enumerate() { + for (x, board_cell) in board_row.iter().enumerate() { + if *board_cell == Players::Blank { + available.push(Position { + x: x as u8, + y: y as u8, + }); + } + } + } + available +} + +fn win_check(player: Players, board: &[Vec]) -> bool { + if player == Players::Blank { + return false; + } + + //Check for a win on the diagonals. + if (board[0][0] == board[1][1]) && (board[1][1] == board[2][2]) && (board[2][2] == player) + || (board[2][0] == board[1][1]) && (board[1][1] == board[0][2]) && (board[0][2] == player) + { + return true; + } + + for i in 0..3 { + //Check for a win on the horizontals. + if (board[i][0] == board[i][1]) && (board[i][1] == board[i][2]) && (board[i][2] == player) { + return true; + } + + //Check for a win on the verticals. + if (board[0][i] == board[1][i]) && (board[1][i] == board[2][i]) && (board[2][i] == player) { + return true; + } + } + + false +} + +//Minimize the actions of the opponent while maximizing the game state of the current player. +pub fn minimax(side: Players, board: &[Vec]) -> Option { + //Check that board is in a valid state. + if win_check(Players::PlayerX, board) || win_check(Players::PlayerO, board) { + return None; + } + + let opposite = match side { + Players::PlayerX => Players::PlayerO, + Players::PlayerO => Players::PlayerX, + Players::Blank => panic!("Minimax can't operate when a player isn't specified."), + }; + + let positions = available_positions(board); + if positions.is_empty() { + return None; + } + + //Play position + let mut best_move: Option = None; + + for pos in positions { + let mut board_next = board.to_owned(); + board_next[pos.y as usize][pos.x as usize] = side; + + //Check for a win condition before recursion to determine if this node is terminal. + if win_check(Players::PlayerX, &board_next) { + append_playaction( + side, + &mut best_move, + SinglePlayAction { + position: pos, + side: Players::PlayerX, + }, + ); + continue; + } + + if win_check(Players::PlayerO, &board_next) { + append_playaction( + side, + &mut best_move, + SinglePlayAction { + position: pos, + side: Players::PlayerO, + }, + ); + continue; + } + + let result = minimax(opposite, &board_next); + let current_score = match result { + Some(x) => x.side, + _ => Players::Blank, + }; + + append_playaction( + side, + &mut best_move, + SinglePlayAction { + position: pos, + side: current_score, + }, + ) + } + best_move +} + +//Promote only better or collate equally scored game plays +fn append_playaction( + current_side: Players, + opt_play_actions: &mut Option, + appendee: SinglePlayAction, +) { + if opt_play_actions.is_none() { + *opt_play_actions = Some(PlayActions { + positions: vec![appendee.position], + side: appendee.side, + }); + return; + } + + let mut play_actions = opt_play_actions.as_mut().unwrap(); + + //New game action is scored from the current side and the current saved best score against the new game action. + match (current_side, play_actions.side, appendee.side) { + (Players::Blank, _, _) => panic!("Unreachable state."), + + //Winning scores + (Players::PlayerX, Players::PlayerX, Players::PlayerX) => { + play_actions.positions.push(appendee.position); + } + (Players::PlayerX, Players::PlayerX, _) => {} + (Players::PlayerO, Players::PlayerO, Players::PlayerO) => { + play_actions.positions.push(appendee.position); + } + (Players::PlayerO, Players::PlayerO, _) => {} + + //Non-winning to Winning scores + (Players::PlayerX, _, Players::PlayerX) => { + play_actions.side = Players::PlayerX; + play_actions.positions.clear(); + play_actions.positions.push(appendee.position); + } + (Players::PlayerO, _, Players::PlayerO) => { + play_actions.side = Players::PlayerO; + play_actions.positions.clear(); + play_actions.positions.push(appendee.position); + } + + //Losing to Neutral scores + (Players::PlayerX, Players::PlayerO, Players::Blank) => { + play_actions.side = Players::Blank; + play_actions.positions.clear(); + play_actions.positions.push(appendee.position); + } + + (Players::PlayerO, Players::PlayerX, Players::Blank) => { + play_actions.side = Players::Blank; + play_actions.positions.clear(); + play_actions.positions.push(appendee.position); + } + + //Ignoring lower scored plays + (Players::PlayerX, Players::Blank, Players::PlayerO) => {} + (Players::PlayerO, Players::Blank, Players::PlayerX) => {} + + //No change hence append only + (_, _, _) => { + assert!(play_actions.side == appendee.side); + play_actions.positions.push(appendee.position); + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn win_state_check() { + let mut board = vec![vec![Players::Blank; 3]; 3]; + board[0][0] = Players::PlayerX; + board[0][1] = Players::PlayerX; + board[0][2] = Players::PlayerX; + let responses = minimax(Players::PlayerO, &board); + assert_eq!(responses, None); + } + + #[test] + fn win_state_check2() { + let mut board = vec![vec![Players::Blank; 3]; 3]; + board[0][0] = Players::PlayerX; + board[0][1] = Players::PlayerO; + board[1][0] = Players::PlayerX; + board[1][1] = Players::PlayerO; + board[2][1] = Players::PlayerO; + let responses = minimax(Players::PlayerO, &board); + assert_eq!(responses, None); + } + + #[test] + fn block_win_move() { + let mut board = vec![vec![Players::Blank; 3]; 3]; + board[0][0] = Players::PlayerX; + board[0][1] = Players::PlayerX; + board[1][2] = Players::PlayerO; + board[2][2] = Players::PlayerO; + let responses = minimax(Players::PlayerX, &board); + assert_eq!( + responses, + Some(PlayActions { + positions: vec![Position { x: 2, y: 0 }], + side: Players::PlayerX + }) + ); + } + + #[test] + fn block_move() { + let mut board = vec![vec![Players::Blank; 3]; 3]; + board[0][1] = Players::PlayerX; + board[0][2] = Players::PlayerO; + board[2][0] = Players::PlayerO; + let responses = minimax(Players::PlayerX, &board); + assert_eq!( + responses, + Some(PlayActions { + positions: vec![Position { x: 1, y: 1 }], + side: Players::Blank + }) + ); + } + + #[test] + fn expected_loss() { + let mut board = vec![vec![Players::Blank; 3]; 3]; + board[0][0] = Players::PlayerX; + board[0][2] = Players::PlayerO; + board[1][0] = Players::PlayerX; + board[2][0] = Players::PlayerO; + board[2][2] = Players::PlayerO; + let responses = minimax(Players::PlayerX, &board); + assert_eq!( + responses, + Some(PlayActions { + positions: vec![ + Position { x: 1, y: 0 }, + Position { x: 1, y: 1 }, + Position { x: 2, y: 1 }, + Position { x: 1, y: 2 } + ], + side: Players::PlayerO + }) + ); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/graph/dijkstra.md b/codes/src/algorithms/graph/dijkstra.md new file mode 100644 index 00000000..5c11d62a --- /dev/null +++ b/codes/src/algorithms/graph/dijkstra.md @@ -0,0 +1,162 @@ +# 最短路径-Dijkstra + +```rust +use std::cmp::Reverse; +use std::collections::{BTreeMap, BinaryHeap}; +use std::ops::Add; + +type Graph = BTreeMap>; + +// performs Dijsktra's algorithm on the given graph from the given start +// the graph is a positively-weighted undirected graph +// +// returns a map that for each reachable vertex associates the distance and the predecessor +// since the start has no predecessor but is reachable, map[start] will be None +pub fn dijkstra>( + graph: &Graph, + start: &V, +) -> BTreeMap> { + let mut ans = BTreeMap::new(); + let mut prio = BinaryHeap::new(); + + // start is the special case that doesn't have a predecessor + ans.insert(*start, None); + + for (new, weight) in &graph[start] { + ans.insert(*new, Some((*start, *weight))); + prio.push(Reverse((*weight, new, start))); + } + + while let Some(Reverse((dist_new, new, prev))) = prio.pop() { + match ans[new] { + // what we popped is what is in ans, we'll compute it + Some((p, d)) if p == *prev && d == dist_new => {} + // otherwise it's not interesting + _ => continue, + } + + for (next, weight) in &graph[new] { + match ans.get(next) { + // if ans[next] is a lower dist than the alternative one, we do nothing + Some(Some((_, dist_next))) if dist_new + *weight >= *dist_next => {} + // if ans[next] is None then next is start and so the distance won't be changed, it won't be added again in prio + Some(None) => {} + // the new path is shorter, either new was not in ans or it was farther + _ => { + ans.insert(*next, Some((*new, *weight + dist_new))); + prio.push(Reverse((*weight + dist_new, next, new))); + } + } + } + } + + ans +} + +#[cfg(test)] +mod tests { + use super::{dijkstra, Graph}; + use std::collections::BTreeMap; + + fn add_edge(graph: &mut Graph, v1: V, v2: V, c: E) { + graph.entry(v1).or_insert_with(BTreeMap::new).insert(v2, c); + graph.entry(v2).or_insert_with(BTreeMap::new); + } + + #[test] + fn single_vertex() { + let mut graph: Graph = BTreeMap::new(); + graph.insert(0, BTreeMap::new()); + + let mut dists = BTreeMap::new(); + dists.insert(0, None); + + assert_eq!(dijkstra(&graph, &0), dists); + } + + #[test] + fn single_edge() { + let mut graph = BTreeMap::new(); + add_edge(&mut graph, 0, 1, 2); + + let mut dists_0 = BTreeMap::new(); + dists_0.insert(0, None); + dists_0.insert(1, Some((0, 2))); + + assert_eq!(dijkstra(&graph, &0), dists_0); + + let mut dists_1 = BTreeMap::new(); + dists_1.insert(1, None); + + assert_eq!(dijkstra(&graph, &1), dists_1); + } + + #[test] + fn tree_1() { + let mut graph = BTreeMap::new(); + let mut dists = BTreeMap::new(); + dists.insert(1, None); + for i in 1..100 { + add_edge(&mut graph, i, i * 2, i * 2); + add_edge(&mut graph, i, i * 2 + 1, i * 2 + 1); + + match dists[&i] { + Some((_, d)) => { + dists.insert(i * 2, Some((i, d + i * 2))); + dists.insert(i * 2 + 1, Some((i, d + i * 2 + 1))); + } + None => { + dists.insert(i * 2, Some((i, i * 2))); + dists.insert(i * 2 + 1, Some((i, i * 2 + 1))); + } + } + } + + assert_eq!(dijkstra(&graph, &1), dists); + } + + #[test] + fn graph_1() { + let mut graph = BTreeMap::new(); + add_edge(&mut graph, 'a', 'c', 12); + add_edge(&mut graph, 'a', 'd', 60); + add_edge(&mut graph, 'b', 'a', 10); + add_edge(&mut graph, 'c', 'b', 20); + add_edge(&mut graph, 'c', 'd', 32); + add_edge(&mut graph, 'e', 'a', 7); + + let mut dists_a = BTreeMap::new(); + dists_a.insert('a', None); + dists_a.insert('c', Some(('a', 12))); + dists_a.insert('d', Some(('c', 44))); + dists_a.insert('b', Some(('c', 32))); + assert_eq!(dijkstra(&graph, &'a'), dists_a); + + let mut dists_b = BTreeMap::new(); + dists_b.insert('b', None); + dists_b.insert('a', Some(('b', 10))); + dists_b.insert('c', Some(('a', 22))); + dists_b.insert('d', Some(('c', 54))); + assert_eq!(dijkstra(&graph, &'b'), dists_b); + + let mut dists_c = BTreeMap::new(); + dists_c.insert('c', None); + dists_c.insert('b', Some(('c', 20))); + dists_c.insert('d', Some(('c', 32))); + dists_c.insert('a', Some(('b', 30))); + assert_eq!(dijkstra(&graph, &'c'), dists_c); + + let mut dists_d = BTreeMap::new(); + dists_d.insert('d', None); + assert_eq!(dijkstra(&graph, &'d'), dists_d); + + let mut dists_e = BTreeMap::new(); + dists_e.insert('e', None); + dists_e.insert('a', Some(('e', 7))); + dists_e.insert('c', Some(('a', 19))); + dists_e.insert('d', Some(('c', 51))); + dists_e.insert('b', Some(('c', 39))); + assert_eq!(dijkstra(&graph, &'e'), dists_e); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/graph/index.md b/codes/src/algorithms/graph/index.md new file mode 100644 index 00000000..f05fab39 --- /dev/null +++ b/codes/src/algorithms/graph/index.md @@ -0,0 +1,3 @@ +# 图论 + +图论算法在计算机科学中扮演着很重要的角色,它提供了对很多问题都有效的一种简单而系统的建模方式。很多问题都可以转化为图论问题,然后用图论的基本算法加以解决。 \ No newline at end of file diff --git a/codes/src/algorithms/graph/minimum-spanning-tree.md b/codes/src/algorithms/graph/minimum-spanning-tree.md new file mode 100644 index 00000000..bac4a89d --- /dev/null +++ b/codes/src/algorithms/graph/minimum-spanning-tree.md @@ -0,0 +1,165 @@ +# 最小生成树 + +```rust +use std::vec::Vec; + +#[derive(Debug)] +pub struct Edge { + source: i64, + destination: i64, + cost: i64, +} + +impl PartialEq for Edge { + fn eq(&self, other: &Self) -> bool { + self.source == other.source + && self.destination == other.destination + && self.cost == other.cost + } +} + +impl Eq for Edge {} + +impl Edge { + fn new(source: i64, destination: i64, cost: i64) -> Self { + Self { + source, + destination, + cost, + } + } +} + +fn make_sets(number_of_vertices: i64) -> Vec { + let mut parent: Vec = Vec::with_capacity(number_of_vertices as usize); + for i in 0..number_of_vertices { + parent.push(i); + } + parent +} + +fn find(parent: &mut Vec, x: i64) -> i64 { + let idx: usize = x as usize; + if parent[idx] != x { + parent[idx] = find(parent, parent[idx]); + } + parent[idx] +} + +fn merge(parent: &mut Vec, x: i64, y: i64) { + let idx_x: usize = find(parent, x) as usize; + let parent_y: i64 = find(parent, y); + parent[idx_x] = parent_y; +} + +fn is_same_set(parent: &mut Vec, x: i64, y: i64) -> bool { + find(parent, x) == find(parent, y) +} + +pub fn kruskal(mut edges: Vec, number_of_vertices: i64) -> (i64, Vec) { + let mut parent: Vec = make_sets(number_of_vertices); + + edges.sort_unstable_by(|a, b| a.cost.cmp(&b.cost)); + let mut total_cost: i64 = 0; + let mut final_edges: Vec = Vec::new(); + let mut merge_count: i64 = 0; + for edge in edges.iter() { + if merge_count >= number_of_vertices - 1 { + break; + } + + let source: i64 = edge.source; + let destination: i64 = edge.destination; + if !is_same_set(&mut parent, source, destination) { + merge(&mut parent, source, destination); + merge_count += 1; + let cost: i64 = edge.cost; + total_cost += cost; + let final_edge: Edge = Edge::new(source, destination, cost); + final_edges.push(final_edge); + } + } + (total_cost, final_edges) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_seven_vertices_eleven_edges() { + let mut edges: Vec = Vec::new(); + edges.push(Edge::new(0, 1, 7)); + edges.push(Edge::new(0, 3, 5)); + edges.push(Edge::new(1, 2, 8)); + edges.push(Edge::new(1, 3, 9)); + edges.push(Edge::new(1, 4, 7)); + edges.push(Edge::new(2, 4, 5)); + edges.push(Edge::new(3, 4, 15)); + edges.push(Edge::new(3, 5, 6)); + edges.push(Edge::new(4, 5, 8)); + edges.push(Edge::new(4, 6, 9)); + edges.push(Edge::new(5, 6, 11)); + + let number_of_vertices: i64 = 7; + + let expected_total_cost = 39; + let mut expected_used_edges: Vec = Vec::new(); + expected_used_edges.push(Edge::new(0, 3, 5)); + expected_used_edges.push(Edge::new(2, 4, 5)); + expected_used_edges.push(Edge::new(3, 5, 6)); + expected_used_edges.push(Edge::new(0, 1, 7)); + expected_used_edges.push(Edge::new(1, 4, 7)); + expected_used_edges.push(Edge::new(4, 6, 9)); + + let (actual_total_cost, actual_final_edges) = kruskal(edges, number_of_vertices); + + assert_eq!(actual_total_cost, expected_total_cost); + assert_eq!(actual_final_edges, expected_used_edges); + } + + #[test] + fn test_ten_vertices_twenty_edges() { + let mut edges: Vec = Vec::new(); + edges.push(Edge::new(0, 1, 3)); + edges.push(Edge::new(0, 3, 6)); + edges.push(Edge::new(0, 4, 9)); + edges.push(Edge::new(1, 2, 2)); + edges.push(Edge::new(1, 3, 4)); + edges.push(Edge::new(1, 4, 9)); + edges.push(Edge::new(2, 3, 2)); + edges.push(Edge::new(2, 5, 8)); + edges.push(Edge::new(2, 6, 9)); + edges.push(Edge::new(3, 6, 9)); + edges.push(Edge::new(4, 5, 8)); + edges.push(Edge::new(4, 9, 18)); + edges.push(Edge::new(5, 6, 7)); + edges.push(Edge::new(5, 8, 9)); + edges.push(Edge::new(5, 9, 10)); + edges.push(Edge::new(6, 7, 4)); + edges.push(Edge::new(6, 8, 5)); + edges.push(Edge::new(7, 8, 1)); + edges.push(Edge::new(7, 9, 4)); + edges.push(Edge::new(8, 9, 3)); + + let number_of_vertices: i64 = 10; + + let expected_total_cost = 38; + let mut expected_used_edges = Vec::new(); + expected_used_edges.push(Edge::new(7, 8, 1)); + expected_used_edges.push(Edge::new(1, 2, 2)); + expected_used_edges.push(Edge::new(2, 3, 2)); + expected_used_edges.push(Edge::new(0, 1, 3)); + expected_used_edges.push(Edge::new(8, 9, 3)); + expected_used_edges.push(Edge::new(6, 7, 4)); + expected_used_edges.push(Edge::new(5, 6, 7)); + expected_used_edges.push(Edge::new(2, 5, 8)); + expected_used_edges.push(Edge::new(4, 5, 8)); + + let (actual_total_cost, actual_final_edges) = kruskal(edges, number_of_vertices); + + assert_eq!(actual_total_cost, expected_total_cost); + assert_eq!(actual_final_edges, expected_used_edges); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/graph/prim.md b/codes/src/algorithms/graph/prim.md new file mode 100644 index 00000000..254d993a --- /dev/null +++ b/codes/src/algorithms/graph/prim.md @@ -0,0 +1,203 @@ +# Prim算法(最小生成树) + +```rust +use std::cmp::Reverse; +use std::collections::{BTreeMap, BinaryHeap}; +use std::ops::Add; + +type Graph = BTreeMap>; + +fn add_edge(graph: &mut Graph, v1: V, v2: V, c: E) { + graph.entry(v1).or_insert_with(BTreeMap::new).insert(v2, c); + graph.entry(v2).or_insert_with(BTreeMap::new).insert(v1, c); +} + +// selects a start and run the algorithm from it +pub fn prim( + graph: &Graph, +) -> Graph { + match graph.keys().next() { + Some(v) => prim_with_start(graph, *v), + None => BTreeMap::new(), + } +} + +// only works for a connected graph +// if the given graph is not connected it will return the MST of the connected subgraph +pub fn prim_with_start( + graph: &Graph, + start: V, +) -> Graph { + // will contain the MST + let mut mst: Graph = Graph::new(); + // a priority queue based on a binary heap, used to get the cheapest edge + // the elements are an edge: the cost, destination and source + let mut prio = BinaryHeap::new(); + + mst.insert(start, BTreeMap::new()); + + for (v, c) in &graph[&start] { + // the heap is a max heap, we have to use Reverse when adding to simulate a min heap + prio.push(Reverse((*c, v, start))); + } + + while let Some(Reverse((dist, t, prev))) = prio.pop() { + // the destination of the edge has already been seen + if mst.contains_key(t) { + continue; + } + + // the destination is a new vertex + add_edge(&mut mst, prev, *t, dist); + + for (v, c) in &graph[t] { + if !mst.contains_key(v) { + prio.push(Reverse((*c, v, *t))); + } + } + } + + mst +} + +#[cfg(test)] +mod tests { + use super::{add_edge, prim, Graph}; + use std::collections::BTreeMap; + + #[test] + fn empty() { + assert_eq!(prim::(&BTreeMap::new()), BTreeMap::new()); + } + + #[test] + fn single_vertex() { + let mut graph: Graph = BTreeMap::new(); + graph.insert(42, BTreeMap::new()); + + assert_eq!(prim(&graph), graph); + } + + #[test] + fn single_edge() { + let mut graph = BTreeMap::new(); + + add_edge(&mut graph, 42, 666, 12); + + assert_eq!(prim(&graph), graph); + } + + #[test] + fn tree_1() { + let mut graph = BTreeMap::new(); + + add_edge(&mut graph, 0, 1, 10); + add_edge(&mut graph, 0, 2, 11); + add_edge(&mut graph, 2, 3, 12); + add_edge(&mut graph, 2, 4, 13); + add_edge(&mut graph, 1, 5, 14); + add_edge(&mut graph, 1, 6, 15); + add_edge(&mut graph, 3, 7, 16); + + assert_eq!(prim(&graph), graph); + } + + #[test] + fn tree_2() { + let mut graph = BTreeMap::new(); + + add_edge(&mut graph, 1, 2, 11); + add_edge(&mut graph, 2, 3, 12); + add_edge(&mut graph, 2, 4, 13); + add_edge(&mut graph, 4, 5, 14); + add_edge(&mut graph, 4, 6, 15); + add_edge(&mut graph, 6, 7, 16); + + assert_eq!(prim(&graph), graph); + } + + #[test] + fn tree_3() { + let mut graph = BTreeMap::new(); + + for i in 1..100 { + add_edge(&mut graph, i, 2 * i, i); + add_edge(&mut graph, i, 2 * i + 1, -i); + } + + assert_eq!(prim(&graph), graph); + } + + #[test] + fn graph_1() { + let mut graph = BTreeMap::new(); + add_edge(&mut graph, 'a', 'b', 6); + add_edge(&mut graph, 'a', 'c', 7); + add_edge(&mut graph, 'a', 'e', 2); + add_edge(&mut graph, 'a', 'f', 3); + add_edge(&mut graph, 'b', 'c', 5); + add_edge(&mut graph, 'c', 'e', 5); + add_edge(&mut graph, 'd', 'e', 4); + add_edge(&mut graph, 'd', 'f', 1); + add_edge(&mut graph, 'e', 'f', 2); + + let mut ans = BTreeMap::new(); + add_edge(&mut ans, 'd', 'f', 1); + add_edge(&mut ans, 'e', 'f', 2); + add_edge(&mut ans, 'a', 'e', 2); + add_edge(&mut ans, 'b', 'c', 5); + add_edge(&mut ans, 'c', 'e', 5); + + assert_eq!(prim(&graph), ans); + } + + #[test] + fn graph_2() { + let mut graph = BTreeMap::new(); + add_edge(&mut graph, 1, 2, 6); + add_edge(&mut graph, 1, 3, 1); + add_edge(&mut graph, 1, 4, 5); + add_edge(&mut graph, 2, 3, 5); + add_edge(&mut graph, 2, 5, 3); + add_edge(&mut graph, 3, 4, 5); + add_edge(&mut graph, 3, 5, 6); + add_edge(&mut graph, 3, 6, 4); + add_edge(&mut graph, 4, 6, 2); + add_edge(&mut graph, 5, 6, 6); + + let mut ans = BTreeMap::new(); + add_edge(&mut ans, 1, 3, 1); + add_edge(&mut ans, 4, 6, 2); + add_edge(&mut ans, 2, 5, 3); + add_edge(&mut ans, 2, 3, 5); + add_edge(&mut ans, 3, 6, 4); + + assert_eq!(prim(&graph), ans); + } + + #[test] + fn graph_3() { + let mut graph = BTreeMap::new(); + add_edge(&mut graph, "v1", "v2", 1); + add_edge(&mut graph, "v1", "v3", 3); + add_edge(&mut graph, "v1", "v5", 6); + add_edge(&mut graph, "v2", "v3", 2); + add_edge(&mut graph, "v2", "v4", 3); + add_edge(&mut graph, "v2", "v5", 5); + add_edge(&mut graph, "v3", "v4", 5); + add_edge(&mut graph, "v3", "v6", 2); + add_edge(&mut graph, "v4", "v5", 2); + add_edge(&mut graph, "v4", "v6", 4); + add_edge(&mut graph, "v5", "v6", 1); + + let mut ans = BTreeMap::new(); + add_edge(&mut ans, "v1", "v2", 1); + add_edge(&mut ans, "v5", "v6", 1); + add_edge(&mut ans, "v2", "v3", 2); + add_edge(&mut ans, "v3", "v6", 2); + add_edge(&mut ans, "v4", "v5", 2); + + assert_eq!(prim(&graph), ans); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/index.md b/codes/src/algorithms/index.md new file mode 100644 index 00000000..cf18b27a --- /dev/null +++ b/codes/src/algorithms/index.md @@ -0,0 +1,10 @@ +# 算法 + +算法,一个高大上的词汇,在计算机领域也绝对是凡人的禁忌,但是其实算法又没那么神秘,我们在写代码时,无时无刻都在与算法打交道,只是绝大部分算法我们无法感知到而已,因为这些算法已经很好的被包装在其它库中,我们只需要输入数据然后调用它获得输出数据即可,因此这就引出了算法的基本定义: + +计算机算法是以一步接一步的方式来详细描述计算机如何将输入转化为所要求的输出的过程,或者说,算法是对计算机上执行的计算过程的具体描述。(以上内容摘抄自百度百科),简单点说计算机算法就是将输入转化为所要求的输出过程。 + +既然只要调用别人的算法库即可完成任务,我们为什么要学习算法呢?原因很简单:面试需要。哈哈,开个玩笑,当然面试很重要,但是提升自己的能力也很重要,学习算法往往能提升我们对于代码的深层次理解和认识,你会知道为何要优化代码,该如何优化代码,甚至在真的需要你手撸算法时,心中也有一个明确的思路:我该选择哪个算法,而不是一片茫然,只知道用遍历的方式来完成任务。 + +所以现在开始我们的算法之旅吧, 本章重点呈现各种常用算法的Rust实现,大部分章节都会链接一篇讲解该算法的文章。 + diff --git a/codes/src/algorithms/math/extended-euclidean.md b/codes/src/algorithms/math/extended-euclidean.md new file mode 100644 index 00000000..2835c7f1 --- /dev/null +++ b/codes/src/algorithms/math/extended-euclidean.md @@ -0,0 +1,41 @@ +# 扩展欧几里得算法 + +```rust +fn update_step(a: &mut i32, old_a: &mut i32, quotient: i32) { + let temp = *a; + *a = *old_a - quotient * temp; + *old_a = temp; +} + +pub fn extended_euclidean_algorithm(a: i32, b: i32) -> (i32, i32, i32) { + let (mut old_r, mut rem) = (a, b); + let (mut old_s, mut coeff_s) = (1, 0); + let (mut old_t, mut coeff_t) = (0, 1); + + while rem != 0 { + let quotient = old_r / rem; + + update_step(&mut rem, &mut old_r, quotient); + update_step(&mut coeff_s, &mut old_s, quotient); + update_step(&mut coeff_t, &mut old_t, quotient); + } + + (old_r, old_s, old_t) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + assert_eq!(extended_euclidean_algorithm(101, 13), (1, 4, -31)); + assert_eq!(extended_euclidean_algorithm(123, 19), (1, -2, 13)); + assert_eq!(extended_euclidean_algorithm(25, 36), (1, 13, -9)); + assert_eq!(extended_euclidean_algorithm(69, 54), (3, -7, 9)); + assert_eq!(extended_euclidean_algorithm(55, 79), (1, 23, -16)); + assert_eq!(extended_euclidean_algorithm(33, 44), (11, -1, 1)); + assert_eq!(extended_euclidean_algorithm(50, 70), (10, 3, -2)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/math/greatest-common-divisor.md b/codes/src/algorithms/math/greatest-common-divisor.md new file mode 100644 index 00000000..5033f0e7 --- /dev/null +++ b/codes/src/algorithms/math/greatest-common-divisor.md @@ -0,0 +1,88 @@ +# 最大公约数 + +```rust +/// Greatest Common Divisor. +/// +/// greatest_common_divisor(num1, num2) returns the greatest number of num1 and num2. +/// +/// Wikipedia reference: https://en.wikipedia.org/wiki/Greatest_common_divisor +/// gcd(a, b) = gcd(a, -b) = gcd(-a, b) = gcd(-a, -b) by definition of divisibility + +pub fn greatest_common_divisor_recursive(a: i64, b: i64) -> i64 { + if a == 0 { + b.abs() + } else { + greatest_common_divisor_recursive(b % a, a) + } +} + +pub fn greatest_common_divisor_iterative(mut a: i64, mut b: i64) -> i64 { + while a != 0 { + let remainder = b % a; + b = a; + a = remainder; + } + b.abs() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn positive_number_recursive() { + assert_eq!(greatest_common_divisor_recursive(4, 16), 4); + assert_eq!(greatest_common_divisor_recursive(16, 4), 4); + assert_eq!(greatest_common_divisor_recursive(3, 5), 1); + assert_eq!(greatest_common_divisor_recursive(40, 40), 40); + assert_eq!(greatest_common_divisor_recursive(27, 12), 3); + } + + #[test] + fn positive_number_iterative() { + assert_eq!(greatest_common_divisor_iterative(4, 16), 4); + assert_eq!(greatest_common_divisor_iterative(16, 4), 4); + assert_eq!(greatest_common_divisor_iterative(3, 5), 1); + assert_eq!(greatest_common_divisor_iterative(40, 40), 40); + assert_eq!(greatest_common_divisor_iterative(27, 12), 3); + } + + #[test] + fn negative_number_recursive() { + assert_eq!(greatest_common_divisor_recursive(-32, -8), 8); + assert_eq!(greatest_common_divisor_recursive(-8, -32), 8); + assert_eq!(greatest_common_divisor_recursive(-3, -5), 1); + assert_eq!(greatest_common_divisor_recursive(-40, -40), 40); + assert_eq!(greatest_common_divisor_recursive(-12, -27), 3); + } + + #[test] + fn negative_number_iterative() { + assert_eq!(greatest_common_divisor_iterative(-32, -8), 8); + assert_eq!(greatest_common_divisor_iterative(-8, -32), 8); + assert_eq!(greatest_common_divisor_iterative(-3, -5), 1); + assert_eq!(greatest_common_divisor_iterative(-40, -40), 40); + assert_eq!(greatest_common_divisor_iterative(-12, -27), 3); + } + + #[test] + fn mix_recursive() { + assert_eq!(greatest_common_divisor_recursive(0, -5), 5); + assert_eq!(greatest_common_divisor_recursive(-5, 0), 5); + assert_eq!(greatest_common_divisor_recursive(-64, 32), 32); + assert_eq!(greatest_common_divisor_recursive(-32, 64), 32); + assert_eq!(greatest_common_divisor_recursive(-40, 40), 40); + assert_eq!(greatest_common_divisor_recursive(12, -27), 3); + } + + #[test] + fn mix_iterative() { + assert_eq!(greatest_common_divisor_iterative(0, -5), 5); + assert_eq!(greatest_common_divisor_iterative(-5, 0), 5); + assert_eq!(greatest_common_divisor_iterative(-64, 32), 32); + assert_eq!(greatest_common_divisor_iterative(-32, 64), 32); + assert_eq!(greatest_common_divisor_iterative(-40, 40), 40); + assert_eq!(greatest_common_divisor_iterative(12, -27), 3); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/math/index.md b/codes/src/algorithms/math/index.md new file mode 100644 index 00000000..72fb2838 --- /dev/null +++ b/codes/src/algorithms/math/index.md @@ -0,0 +1,2 @@ +# 数学 + diff --git a/codes/src/algorithms/math/pascal-triange.md b/codes/src/algorithms/math/pascal-triange.md new file mode 100644 index 00000000..4e96561d --- /dev/null +++ b/codes/src/algorithms/math/pascal-triange.md @@ -0,0 +1,55 @@ +# 帕斯卡三角 + +```rust +/// ## Paslcal's triangle problem + +/// pascal_triangle(num_rows) returns the first num_rows of Pascal's triangle. +/// About Pascal's triangle: https://en.wikipedia.org/wiki/Pascal%27s_triangle +/// +/// Arguments: +/// * `num_rows` - number of rows of triangle +/// Complexity +/// - time complexity: O(n^2), +/// - space complexity: O(n^2), +pub fn pascal_triangle(num_rows: i32) -> Vec> { + let mut ans: Vec> = vec![]; + + for i in 1..num_rows + 1 { + let mut vec: Vec = vec![1]; + + let mut res: i32 = 1; + for k in 1..i { + res *= i - k; + res /= k; + vec.push(res); + } + ans.push(vec); + } + + ans +} + +#[cfg(test)] +mod tests { + use super::pascal_triangle; + + #[test] + fn test() { + assert_eq!(pascal_triangle(3), vec![vec![1], vec![1, 1], vec![1, 2, 1]]); + assert_eq!( + pascal_triangle(4), + vec![vec![1], vec![1, 1], vec![1, 2, 1], vec![1, 3, 3, 1]] + ); + assert_eq!( + pascal_triangle(5), + vec![ + vec![1], + vec![1, 1], + vec![1, 2, 1], + vec![1, 3, 3, 1], + vec![1, 4, 6, 4, 1] + ] + ); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/math/perfect-numbers.md b/codes/src/algorithms/math/perfect-numbers.md new file mode 100644 index 00000000..5095c25c --- /dev/null +++ b/codes/src/algorithms/math/perfect-numbers.md @@ -0,0 +1,51 @@ +# 寻找完美数 + +```rust +pub fn is_perfect_number(num: usize) -> bool { + let mut sum = 0; + + for i in 1..num - 1 { + if num % i == 0 { + sum += i; + } + } + + num == sum +} + +pub fn perfect_numbers(max: usize) -> Vec { + let mut result: Vec = Vec::new(); + + // It is not known if there are any odd perfect numbers, so we go around all the numbers. + for i in 1..max + 1 { + if is_perfect_number(i) { + result.push(i); + } + } + + result +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + assert_eq!(is_perfect_number(6), true); + assert_eq!(is_perfect_number(28), true); + assert_eq!(is_perfect_number(496), true); + assert_eq!(is_perfect_number(8128), true); + + assert_eq!(is_perfect_number(5), false); + assert_eq!(is_perfect_number(86), false); + assert_eq!(is_perfect_number(497), false); + assert_eq!(is_perfect_number(8120), false); + + assert_eq!(perfect_numbers(10), vec![6]); + assert_eq!(perfect_numbers(100), vec![6, 28]); + assert_eq!(perfect_numbers(496), vec![6, 28, 496]); + assert_eq!(perfect_numbers(1000), vec![6, 28, 496]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/math/prime-check.md b/codes/src/algorithms/math/prime-check.md new file mode 100644 index 00000000..a01a0bc5 --- /dev/null +++ b/codes/src/algorithms/math/prime-check.md @@ -0,0 +1,37 @@ +# 质数检测 + +```rust +pub fn prime_check(num: usize) -> bool { + if (num > 1) & (num < 4) { + return true; + } else if (num < 2) || (num % 2 == 0) { + return false; + } + + let stop: usize = (num as f64).sqrt() as usize + 1; + for i in (3..stop).step_by(2) { + if num % i == 0 { + return false; + } + } + true +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + assert_eq!(prime_check(3), true); + assert_eq!(prime_check(7), true); + assert_eq!(prime_check(11), true); + assert_eq!(prime_check(2003), true); + + assert_eq!(prime_check(4), false); + assert_eq!(prime_check(6), false); + assert_eq!(prime_check(21), false); + assert_eq!(prime_check(2004), false); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/math/prime-numbers.md b/codes/src/algorithms/math/prime-numbers.md new file mode 100644 index 00000000..fa87db95 --- /dev/null +++ b/codes/src/algorithms/math/prime-numbers.md @@ -0,0 +1,42 @@ +# 质数筛法 + +```rust +pub fn prime_numbers(max: usize) -> Vec { + let mut result: Vec = Vec::new(); + + if max >= 2 { + result.push(2) + } + for i in (3..max + 1).step_by(2) { + let stop: usize = (i as f64).sqrt() as usize + 1; + let mut status: bool = true; + + for j in (3..stop).step_by(2) { + if i % j == 0 { + status = false + } + } + if status { + result.push(i) + } + } + + result +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + assert_eq!(prime_numbers(0), vec![]); + assert_eq!(prime_numbers(11), vec![2, 3, 5, 7, 11]); + assert_eq!(prime_numbers(25), vec![2, 3, 5, 7, 11, 13, 17, 19, 23]); + assert_eq!( + prime_numbers(33), + vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31] + ); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/math/trial-division.md b/codes/src/algorithms/math/trial-division.md new file mode 100644 index 00000000..1613f58f --- /dev/null +++ b/codes/src/algorithms/math/trial-division.md @@ -0,0 +1,53 @@ +# 试除法 + +```rust +fn floor(value: f64, scale: u8) -> f64 { + let multiplier = 10i64.pow(scale as u32) as f64; + (value * multiplier).floor() +} + +fn double_to_int(amount: f64) -> i128 { + amount.round() as i128 +} + +pub fn trial_division(mut num: i128) -> Vec { + let mut result: Vec = vec![]; + + while num % 2 == 0 { + result.push(2); + num /= 2; + num = double_to_int(floor(num as f64, 0)) + } + let mut f: i128 = 3; + + while f.pow(2) <= num { + if num % f == 0 { + result.push(f); + num /= f; + num = double_to_int(floor(num as f64, 0)) + } else { + f += 2 + } + } + + if num != 1 { + result.push(num) + } + result +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + assert_eq!(trial_division(9), vec!(3, 3)); + assert_eq!(trial_division(10), vec!(2, 5)); + assert_eq!(trial_division(11), vec!(11)); + assert_eq!(trial_division(33), vec!(3, 11)); + assert_eq!(trial_division(2003), vec!(2003)); + assert_eq!(trial_division(100001), vec!(11, 9091)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/searching/binary-search-recursive.md b/codes/src/algorithms/searching/binary-search-recursive.md new file mode 100644 index 00000000..f211e6ec --- /dev/null +++ b/codes/src/algorithms/searching/binary-search-recursive.md @@ -0,0 +1,135 @@ +# 递归二分查找 + +```rust +use std::cmp::Ordering; + +pub fn binary_search_rec( + list_of_items: &[T], + target: &T, + left: &usize, + right: &usize, +) -> Option { + if left >= right { + return None; + } + + let middle: usize = left + (right - left) / 2; + match target.cmp(&list_of_items[middle]) { + Ordering::Less => binary_search_rec(list_of_items, target, left, &middle), + Ordering::Greater => binary_search_rec(list_of_items, target, &(middle + 1), right), + Ordering::Equal => Some(middle), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const LEFT: usize = 0; + + #[test] + fn fail_empty_list() { + let list_of_items = vec![]; + assert_eq!( + binary_search_rec(&list_of_items, &1, &LEFT, &list_of_items.len()), + None + ); + } + + #[test] + fn success_one_item() { + let list_of_items = vec![30]; + assert_eq!( + binary_search_rec(&list_of_items, &30, &LEFT, &list_of_items.len()), + Some(0) + ); + } + + #[test] + fn success_search_strings() { + let say_hello_list = vec!["hi", "olá", "salut"]; + let right = say_hello_list.len(); + assert_eq!( + binary_search_rec(&say_hello_list, &"hi", &LEFT, &right), + Some(0) + ); + assert_eq!( + binary_search_rec(&say_hello_list, &"salut", &LEFT, &right), + Some(2) + ); + } + + #[test] + fn fail_search_strings() { + let say_hello_list = vec!["hi", "olá", "salut"]; + for target in &["adiós", "你好"] { + assert_eq!( + binary_search_rec(&say_hello_list, target, &LEFT, &say_hello_list.len()), + None + ); + } + } + + #[test] + fn success_search_integers() { + let integers = vec![0, 10, 20, 30, 40, 50, 60, 70, 80, 90]; + for (index, target) in integers.iter().enumerate() { + assert_eq!( + binary_search_rec(&integers, target, &LEFT, &integers.len()), + Some(index) + ) + } + } + + #[test] + fn fail_search_integers() { + let integers = vec![0, 10, 20, 30, 40, 50, 60, 70, 80, 90]; + for target in &[100, 444, 336] { + assert_eq!( + binary_search_rec(&integers, target, &LEFT, &integers.len()), + None + ); + } + } + + #[test] + fn fail_search_unsorted_strings_list() { + let unsorted_strings = vec!["salut", "olá", "hi"]; + for target in &["hi", "salut"] { + assert_eq!( + binary_search_rec(&unsorted_strings, target, &LEFT, &unsorted_strings.len()), + None + ); + } + } + + #[test] + fn fail_search_unsorted_integers_list() { + let unsorted_integers = vec![90, 80, 70, 60, 50, 40, 30, 20, 10, 0]; + for target in &[0, 80, 90] { + assert_eq!( + binary_search_rec(&unsorted_integers, target, &LEFT, &unsorted_integers.len()), + None + ); + } + } + + #[test] + fn success_search_string_in_middle_of_unsorted_list() { + let unsorted_strings = vec!["salut", "olá", "hi"]; + assert_eq!( + binary_search_rec(&unsorted_strings, &"olá", &LEFT, &unsorted_strings.len()), + Some(1) + ); + } + + #[test] + fn success_search_integer_in_middle_of_unsorted_list() { + let unsorted_integers = vec![90, 80, 70]; + assert_eq!( + binary_search_rec(&unsorted_integers, &80, &LEFT, &unsorted_integers.len()), + Some(1) + ); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/searching/binary-search.md b/codes/src/algorithms/searching/binary-search.md new file mode 100644 index 00000000..564337bd --- /dev/null +++ b/codes/src/algorithms/searching/binary-search.md @@ -0,0 +1,78 @@ +# 二分搜索 + +![alt text][binary-image] + +From [Wikipedia][binary-wiki]: Binary search, also known as half-interval search or logarithmic search, is a search algorithm that finds the position of a target value within a sorted array. It compares the target value to the middle element of the array; if they are unequal, the half in which the target cannot lie is eliminated and the search continues on the remaining half until it is successful. + +__Properties__ +* Worst case performance O(log n) +* Best case performance O(1) +* Average case performance O(log n) +* Worst case space complexity O(1) + +```rust +use std::cmp::Ordering; + +pub fn binary_search(item: &T, arr: &[T]) -> Option { + let mut left = 0; + let mut right = arr.len(); + + while left < right { + let mid = left + (right - left) / 2; + + match item.cmp(&arr[mid]) { + Ordering::Less => right = mid, + Ordering::Equal => return Some(mid), + Ordering::Greater => left = mid + 1, + } + } + None +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty() { + let index = binary_search(&"a", &vec![]); + assert_eq!(index, None); + } + + #[test] + fn one_item() { + let index = binary_search(&"a", &vec!["a"]); + assert_eq!(index, Some(0)); + } + + #[test] + fn search_strings() { + let index = binary_search(&"a", &vec!["a", "b", "c", "d", "google", "zoo"]); + assert_eq!(index, Some(0)); + } + + #[test] + fn search_ints() { + let index = binary_search(&4, &vec![1, 2, 3, 4]); + assert_eq!(index, Some(3)); + + let index = binary_search(&3, &vec![1, 2, 3, 4]); + assert_eq!(index, Some(2)); + + let index = binary_search(&2, &vec![1, 2, 3, 4]); + assert_eq!(index, Some(1)); + + let index = binary_search(&1, &vec![1, 2, 3, 4]); + assert_eq!(index, Some(0)); + } + + #[test] + fn not_found() { + let index = binary_search(&5, &vec![1, 2, 3, 4]); + assert_eq!(index, None); + } +} +``` + +[binary-wiki]: https://en.wikipedia.org/wiki/Binary_search_algorithm +[binary-image]: https://upload.wikimedia.org/wikipedia/commons/f/f7/Binary_search_into_array.png \ No newline at end of file diff --git a/codes/src/algorithms/searching/index.md b/codes/src/algorithms/searching/index.md new file mode 100644 index 00000000..051553c0 --- /dev/null +++ b/codes/src/algorithms/searching/index.md @@ -0,0 +1,5 @@ +# 查找算法 + +查找是在大量的信息中寻找一个特定的信息元素,在计算机应用中,查找是常用的基本运算,例如编译程序中符号表的查找。 + +把数据按照合适的方式进行排列,往往是查找的关键。 \ No newline at end of file diff --git a/codes/src/algorithms/searching/kth-smallest.md b/codes/src/algorithms/searching/kth-smallest.md new file mode 100644 index 00000000..d4e77c12 --- /dev/null +++ b/codes/src/algorithms/searching/kth-smallest.md @@ -0,0 +1,76 @@ +# 查找第K小的元素 + +```rust +use crate::sorting::partition; +use std::cmp::{Ordering, PartialOrd}; + +/// Returns k-th smallest element of an array, i.e. its order statistics. +/// Time complexity is O(n^2) in the worst case, but only O(n) on average. +/// It mutates the input, and therefore does not require additional space. +pub fn kth_smallest(input: &mut [T], k: usize) -> Option +where + T: PartialOrd + Copy, +{ + if input.is_empty() { + return None; + } + + let kth = _kth_smallest(input, k, 0, input.len() - 1); + Some(kth) +} + +fn _kth_smallest(input: &mut [T], k: usize, lo: usize, hi: usize) -> T +where + T: PartialOrd + Copy, +{ + if lo == hi { + return input[lo]; + } + + let pivot = partition(input, lo as isize, hi as isize) as usize; + let i = pivot - lo + 1; + + match k.cmp(&i) { + Ordering::Equal => input[pivot], + Ordering::Less => _kth_smallest(input, k, lo, pivot - 1), + Ordering::Greater => _kth_smallest(input, k - i, pivot + 1, hi), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty() { + let mut zero: [u8; 0] = []; + let first = kth_smallest(&mut zero, 1); + + assert_eq!(None, first); + } + + #[test] + fn one_element() { + let mut one = [1]; + let first = kth_smallest(&mut one, 1); + + assert_eq!(1, first.unwrap()); + } + + #[test] + fn many_elements() { + // 0 1 3 4 5 7 8 9 9 10 12 13 16 17 + let mut many = [9, 17, 3, 16, 13, 10, 1, 5, 7, 12, 4, 8, 9, 0]; + + let first = kth_smallest(&mut many, 1); + let third = kth_smallest(&mut many, 3); + let sixth = kth_smallest(&mut many, 6); + let fourteenth = kth_smallest(&mut many, 14); + + assert_eq!(0, first.unwrap()); + assert_eq!(3, third.unwrap()); + assert_eq!(7, sixth.unwrap()); + assert_eq!(17, fourteenth.unwrap()); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/searching/linear-search.md b/codes/src/algorithms/searching/linear-search.md new file mode 100644 index 00000000..d5cb3eaa --- /dev/null +++ b/codes/src/algorithms/searching/linear-search.md @@ -0,0 +1,68 @@ +# 线性搜索 + +![alt text][linear-image] + +From [Wikipedia][linear-wiki]: linear search or sequential search is a method for finding a target value within a list. It sequentially checks each element of the list for the target value until a match is found or until all the elements have been searched. + Linear search runs in at worst linear time and makes at most n comparisons, where n is the length of the list. + +__Properties__ +* Worst case performance O(n) +* Best case performance O(1) +* Average case performance O(n) +* Worst case space complexity O(1) iterative + +```rust +use std::cmp::PartialEq; + +pub fn linear_search(item: &T, arr: &[T]) -> Option { + for (i, data) in arr.iter().enumerate() { + if item == data { + return Some(i); + } + } + + None +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn search_strings() { + let index = linear_search(&"a", &vec!["a", "b", "c", "d", "google", "zoo"]); + assert_eq!(index, Some(0)); + } + + #[test] + fn search_ints() { + let index = linear_search(&4, &vec![1, 2, 3, 4]); + assert_eq!(index, Some(3)); + + let index = linear_search(&3, &vec![1, 2, 3, 4]); + assert_eq!(index, Some(2)); + + let index = linear_search(&2, &vec![1, 2, 3, 4]); + assert_eq!(index, Some(1)); + + let index = linear_search(&1, &vec![1, 2, 3, 4]); + assert_eq!(index, Some(0)); + } + + #[test] + fn not_found() { + let index = linear_search(&5, &vec![1, 2, 3, 4]); + assert_eq!(index, None); + } + + #[test] + fn empty() { + let index = linear_search(&1, &vec![]); + assert_eq!(index, None); + } +} +``` + + +[linear-wiki]: https://en.wikipedia.org/wiki/Linear_search +[linear-image]: http://www.tutorialspoint.com/data_structures_algorithms/images/linear_search.gif \ No newline at end of file diff --git a/codes/src/algorithms/sorting/bubble-sort.md b/codes/src/algorithms/sorting/bubble-sort.md new file mode 100644 index 00000000..d02a86ec --- /dev/null +++ b/codes/src/algorithms/sorting/bubble-sort.md @@ -0,0 +1,38 @@ +# 冒泡排序 + +```rust +pub fn bubble_sort(arr: &mut [T]) { + for i in 0..arr.len() { + for j in 0..arr.len() - 1 - i { + if arr[j] > arr[j + 1] { + arr.swap(j, j + 1); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn descending() { + //降序排列 + let mut ve1 = vec![6, 5, 4, 3, 2, 1]; + bubble_sort(&mut ve1); + for i in 0..ve1.len() - 1 { + assert!(ve1[i] <= ve1[i + 1]); + } + } + + #[test] + fn ascending() { + //升序,预排序 + let mut ve2 = vec![1, 2, 3, 4, 5, 6]; + bubble_sort(&mut ve2); + for i in 0..ve2.len() - 1 { + assert!(ve2[i] <= ve2[i + 1]); + } + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/bucket-sort.md b/codes/src/algorithms/sorting/bucket-sort.md new file mode 100644 index 00000000..5f7c0735 --- /dev/null +++ b/codes/src/algorithms/sorting/bucket-sort.md @@ -0,0 +1,84 @@ +# 桶排序 + +```rust +/// Sort a slice using bucket sort algorithm. +/// +/// Time complexity is `O(n + k)` on average, where `n` is the number of elements, +/// `k` is the number of buckets used in process. +/// +/// Space complexity is `O(n + k)`, as it sorts not in-place. +pub fn bucket_sort(arr: &[usize]) -> Vec { + if arr.is_empty() { + return vec![]; + } + + let max = *arr.iter().max().unwrap(); + let len = arr.len(); + let mut buckets = vec![vec![]; len + 1]; + + for x in arr { + buckets[len * *x / max].push(*x); + } + + for bucket in buckets.iter_mut() { + super::insertion_sort(bucket); + } + + let mut result = vec![]; + for bucket in buckets { + for x in bucket { + result.push(x); + } + } + + result +} + +#[cfg(test)] +mod tests { + use super::super::is_sorted; + use super::*; + + #[test] + fn empty() { + let arr: [usize; 0] = []; + let res = bucket_sort(&arr); + assert!(is_sorted(&res)); + } + + #[test] + fn one_element() { + let arr: [usize; 1] = [4]; + let res = bucket_sort(&arr); + assert!(is_sorted(&res)); + } + + #[test] + fn already_sorted() { + let arr: [usize; 3] = [10, 9, 105]; + let res = bucket_sort(&arr); + assert!(is_sorted(&res)); + } + + #[test] + fn basic() { + let arr: [usize; 4] = [35, 53, 1, 0]; + let res = bucket_sort(&arr); + assert!(is_sorted(&res)); + } + + #[test] + fn odd_number_of_elements() { + let arr: Vec = vec![1, 21, 5, 11, 58]; + let res = bucket_sort(&arr); + assert!(is_sorted(&res)); + } + + #[test] + fn repeated_elements() { + let arr: Vec = vec![542, 542, 542, 542]; + let res = bucket_sort(&arr); + assert!(is_sorted(&res)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/cocktail-shaker-sort.md b/codes/src/algorithms/sorting/cocktail-shaker-sort.md new file mode 100644 index 00000000..b7584b6a --- /dev/null +++ b/codes/src/algorithms/sorting/cocktail-shaker-sort.md @@ -0,0 +1,72 @@ +# 鸡尾酒排序 + +```rust +pub fn cocktail_shaker_sort(arr: &mut [T]) { + let len = arr.len(); + + if len == 0 { + return; + } + + loop { + let mut swapped = false; + + for i in 0..(len - 1).clamp(0, len) { + if arr[i] > arr[i + 1] { + arr.swap(i, i + 1); + swapped = true; + } + } + + if !swapped { + break; + } + + swapped = false; + + for i in (0..(len - 1).clamp(0, len)).rev() { + if arr[i] > arr[i + 1] { + arr.swap(i, i + 1); + swapped = true; + } + } + + if !swapped { + break; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + let mut arr = vec![5, 2, 1, 3, 4, 6]; + cocktail_shaker_sort(&mut arr); + assert_eq!(arr, vec![1, 2, 3, 4, 5, 6]); + } + + #[test] + fn empty() { + let mut arr = Vec::::new(); + cocktail_shaker_sort(&mut arr); + assert_eq!(arr, vec![]); + } + + #[test] + fn one_element() { + let mut arr = vec![1]; + cocktail_shaker_sort(&mut arr); + assert_eq!(arr, vec![1]); + } + + #[test] + fn pre_sorted() { + let mut arr = vec![1, 2, 3, 4, 5, 6]; + cocktail_shaker_sort(&mut arr); + assert_eq!(arr, vec![1, 2, 3, 4, 5, 6]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/comb-sort.md b/codes/src/algorithms/sorting/comb-sort.md new file mode 100644 index 00000000..91866753 --- /dev/null +++ b/codes/src/algorithms/sorting/comb-sort.md @@ -0,0 +1,49 @@ +# 梳排序 + +```rust +pub fn comb_sort(arr: &mut [T]) { + let mut gap = arr.len(); + let shrink = 1.3; + let mut sorted = false; + + while !sorted { + gap = (gap as f32 / shrink).floor() as usize; + if gap <= 1 { + gap = 1; + sorted = true; + } + for i in 0..arr.len() - gap { + let j = i + gap; + if arr[i] > arr[j] { + arr.swap(i, j); + sorted = false; + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn descending() { + //descending + let mut ve1 = vec![6, 5, 4, 3, 2, 1]; + comb_sort(&mut ve1); + for i in 0..ve1.len() - 1 { + assert!(ve1[i] <= ve1[i + 1]); + } + } + + #[test] + fn ascending() { + //pre-sorted + let mut ve2 = vec![1, 2, 3, 4, 5, 6]; + comb_sort(&mut ve2); + for i in 0..ve2.len() - 1 { + assert!(ve2[i] <= ve2[i + 1]); + } + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/counting-sort.md b/codes/src/algorithms/sorting/counting-sort.md new file mode 100644 index 00000000..8d0da9a0 --- /dev/null +++ b/codes/src/algorithms/sorting/counting-sort.md @@ -0,0 +1,92 @@ +# 计数排序 + +```rust +/// In place counting sort for collections of u32 +/// O(n + maxval) in time, where maxval is the biggest value an input can possibly take +/// O(maxval) in memory +/// u32 is chosen arbitrarly, a counting sort probably should'nt be used on data that requires bigger types. + +pub fn counting_sort(arr: &mut [u32], maxval: usize) { + let mut occurences: Vec = vec![0; maxval + 1]; + + for &data in arr.iter() { + occurences[data as usize] += 1; + } + + let mut i = 0; + for (data, &number) in occurences.iter().enumerate() { + for _ in 0..number { + arr[i] = data as u32; + i += 1; + } + } +} + +use std::ops::AddAssign; +/// Generic implementation of a counting sort for all usigned types +pub fn generic_counting_sort + From + AddAssign + Copy>( + arr: &mut [T], + maxval: usize, +) { + let mut occurences: Vec = vec![0; maxval + 1]; + + for &data in arr.iter() { + occurences[data.into() as usize] += 1; + } + + // Current index in output array + let mut i = 0; + + // current data point, necessary to be type-safe + let mut data = T::from(0); + + // This will iterate from 0 to the largest data point in `arr` + // `number` contains the occurances of the data point `data` + for &number in occurences.iter() { + for _ in 0..number { + arr[i] = data; + i += 1; + } + + data += T::from(1); + } +} + +#[cfg(test)] +mod test { + use super::super::is_sorted; + use super::*; + + #[test] + fn counting_sort_descending() { + let mut ve1 = vec![6, 5, 4, 3, 2, 1]; + counting_sort(&mut ve1, 6); + + assert!(is_sorted(&ve1)); + } + + #[test] + fn counting_sort_pre_sorted() { + let mut ve2 = vec![1, 2, 3, 4, 5, 6]; + counting_sort(&mut ve2, 6); + + assert!(is_sorted(&ve2)); + } + + #[test] + fn generic_counting_sort() { + let mut ve1: Vec = vec![100, 30, 60, 10, 20, 120, 1]; + super::generic_counting_sort(&mut ve1, 120); + + assert!(is_sorted(&ve1)); + } + + #[test] + fn presorted_u64_counting_sort() { + let mut ve2: Vec = vec![1, 2, 3, 4, 5, 6]; + super::generic_counting_sort(&mut ve2, 6); + + assert!(is_sorted(&ve2)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/gnome-sort.md b/codes/src/algorithms/sorting/gnome-sort.md new file mode 100644 index 00000000..aa83f4c1 --- /dev/null +++ b/codes/src/algorithms/sorting/gnome-sort.md @@ -0,0 +1,64 @@ +# 地精排序 + +```rust +use std::cmp; + +pub fn gnome_sort(arr: &[T]) -> Vec +where + T: cmp::PartialEq + cmp::PartialOrd + Clone, +{ + let mut arr = arr.to_vec(); + let mut i: usize = 1; + let mut j: usize = 2; + + while i < arr.len() { + if arr[i - 1] < arr[i] { + i = j; + j = i + 1; + } else { + arr.swap(i - 1, i); + i -= 1; + if i == 0 { + i = j; + j += 1; + } + } + } + arr +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + let res = gnome_sort(&vec![6, 5, -8, 3, 2, 3]); + assert_eq!(res, vec![-8, 2, 3, 3, 5, 6]); + } + + #[test] + fn already_sorted() { + let res = gnome_sort(&vec!["a", "b", "c"]); + assert_eq!(res, vec!["a", "b", "c"]); + } + + #[test] + fn odd_number_of_elements() { + let res = gnome_sort(&vec!["d", "a", "c", "e", "b"]); + assert_eq!(res, vec!["a", "b", "c", "d", "e"]); + } + + #[test] + fn one_element() { + let res = gnome_sort(&vec![3]); + assert_eq!(res, vec![3]); + } + + #[test] + fn empty() { + let res = gnome_sort(&Vec::::new()); + assert_eq!(res, vec![]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/heap-sort.md b/codes/src/algorithms/sorting/heap-sort.md new file mode 100644 index 00000000..6e8dfff0 --- /dev/null +++ b/codes/src/algorithms/sorting/heap-sort.md @@ -0,0 +1,143 @@ +# 堆排序 + +```rust +/// Sort a mutable slice using heap sort. +/// +/// Heap sort is an in-place O(n log n) sorting algorithm. It is based on a +/// max heap, a binary tree data structure whose main feature is that +/// parent nodes are always greater or equal to their child nodes. +/// +/// # Max Heap Implementation +/// +/// A max heap can be efficiently implemented with an array. +/// For example, the binary tree: +/// ```text +/// 1 +/// 2 3 +/// 4 5 6 7 +/// ``` +/// +/// ... is represented by the following array: +/// ```text +/// 1 23 4567 +/// ``` +/// +/// Given the index `i` of a node, parent and child indices can be calculated +/// as follows: +/// ```text +/// parent(i) = (i-1) / 2 +/// left_child(i) = 2*i + 1 +/// right_child(i) = 2*i + 2 +/// ``` + +/// # Algorithm +/// +/// Heap sort has two steps: +/// 1. Convert the input array to a max heap. +/// 2. Partition the array into heap part and sorted part. Initially the +/// heap consists of the whole array and the sorted part is empty: +/// ```text +/// arr: [ heap |] +/// ``` +/// +/// Repeatedly swap the root (i.e. the largest) element of the heap with +/// the last element of the heap and increase the sorted part by one: +/// ```text +/// arr: [ root ... last | sorted ] +/// --> [ last ... | root sorted ] +/// ``` +/// +/// After each swap, fix the heap to make it a valid max heap again. +/// Once the heap is empty, `arr` is completely sorted. +pub fn heap_sort(arr: &mut [T]) { + if arr.len() <= 1 { + return; // already sorted + } + + heapify(arr); + + for end in (1..arr.len()).rev() { + arr.swap(0, end); + move_down(&mut arr[..end], 0); + } +} + +/// Convert `arr` into a max heap. +fn heapify(arr: &mut [T]) { + let last_parent = (arr.len() - 2) / 2; + for i in (0..=last_parent).rev() { + move_down(arr, i); + } +} + +/// Move the element at `root` down until `arr` is a max heap again. +/// +/// This assumes that the subtrees under `root` are valid max heaps already. +fn move_down(arr: &mut [T], mut root: usize) { + let last = arr.len() - 1; + loop { + let left = 2 * root + 1; + if left > last { + break; + } + let right = left + 1; + let max = if right <= last && arr[right] > arr[left] { + right + } else { + left + }; + + if arr[max] > arr[root] { + arr.swap(root, max); + } + root = max; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty() { + let mut arr: Vec = Vec::new(); + heap_sort(&mut arr); + assert_eq!(&arr, &[]); + } + + #[test] + fn single_element() { + let mut arr = vec![1]; + heap_sort(&mut arr); + assert_eq!(&arr, &[1]); + } + + #[test] + fn sorted_array() { + let mut arr = vec![1, 2, 3, 4]; + heap_sort(&mut arr); + assert_eq!(&arr, &[1, 2, 3, 4]); + } + + #[test] + fn unsorted_array() { + let mut arr = vec![3, 4, 2, 1]; + heap_sort(&mut arr); + assert_eq!(&arr, &[1, 2, 3, 4]); + } + + #[test] + fn odd_number_of_elements() { + let mut arr = vec![3, 4, 2, 1, 7]; + heap_sort(&mut arr); + assert_eq!(&arr, &[1, 2, 3, 4, 7]); + } + + #[test] + fn repeated_elements() { + let mut arr = vec![542, 542, 542, 542]; + heap_sort(&mut arr); + assert_eq!(&arr, &vec![542, 542, 542, 542]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/index.md b/codes/src/algorithms/sorting/index.md new file mode 100644 index 00000000..23e2bec7 --- /dev/null +++ b/codes/src/algorithms/sorting/index.md @@ -0,0 +1,4 @@ +# 排序算法 + +所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。 + diff --git a/codes/src/algorithms/sorting/insertion-sort.md b/codes/src/algorithms/sorting/insertion-sort.md new file mode 100644 index 00000000..6b94e3d1 --- /dev/null +++ b/codes/src/algorithms/sorting/insertion-sort.md @@ -0,0 +1,75 @@ +# 插入排序 + +```rust +use std::cmp; + +/// Sorts a mutable slice using in-place insertion sort algorithm. +/// +/// Time complexity is `O(n^2)`, where `n` is the number of elements. +/// Space complexity is `O(1)` as it sorts elements in-place. +pub fn insertion_sort(arr: &mut [T]) +where + T: cmp::PartialOrd + Copy, +{ + for i in 1..arr.len() { + let cur = arr[i]; + let mut j = i - 1; + + while arr[j] > cur { + arr.swap(j + 1, j); + if j == 0 { + break; + } + j -= 1; + } + } +} + +#[cfg(test)] +mod tests { + use super::super::is_sorted; + use super::*; + + #[test] + fn empty() { + let mut arr: [u8; 0] = []; + insertion_sort(&mut arr); + assert!(is_sorted(&arr)); + } + + #[test] + fn one_element() { + let mut arr: [char; 1] = ['a']; + insertion_sort(&mut arr); + assert!(is_sorted(&arr)); + } + + #[test] + fn already_sorted() { + let mut arr: [&str; 3] = ["a", "b", "c"]; + insertion_sort(&mut arr); + assert!(is_sorted(&arr)); + } + + #[test] + fn basic() { + let mut arr: [&str; 4] = ["d", "a", "c", "b"]; + insertion_sort(&mut arr); + assert!(is_sorted(&arr)); + } + + #[test] + fn odd_number_of_elements() { + let mut arr: Vec<&str> = vec!["d", "a", "c", "e", "b"]; + insertion_sort(&mut arr); + assert!(is_sorted(&arr)); + } + + #[test] + fn repeated_elements() { + let mut arr: Vec = vec![542, 542, 542, 542]; + insertion_sort(&mut arr); + assert!(is_sorted(&arr)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/merge-sort.md b/codes/src/algorithms/sorting/merge-sort.md new file mode 100644 index 00000000..00b5c404 --- /dev/null +++ b/codes/src/algorithms/sorting/merge-sort.md @@ -0,0 +1,111 @@ +# 归并排序 + +```rust +fn _merge(arr: &mut [T], lo: usize, mid: usize, hi: usize) { + // create temporary arrays to support merge + let mut left_half = Vec::new(); + let mut right_half = Vec::new(); + for v in arr.iter().take(mid + 1).skip(lo) { + left_half.push(*v); + } + for v in arr.iter().take(hi + 1).skip(mid + 1) { + right_half.push(*v); + } + + let lsize = left_half.len(); + let rsize = right_half.len(); + + // pointers to track the positions while merging + let mut l = 0; + let mut r = 0; + let mut a = lo; + + // pick smaller element one by one from either left or right half + while l < lsize && r < rsize { + if left_half[l] < right_half[r] { + arr[a] = left_half[l]; + l += 1; + } else { + arr[a] = right_half[r]; + r += 1; + } + a += 1; + } + + // put all the remaining ones + while l < lsize { + arr[a] = left_half[l]; + l += 1; + a += 1; + } + + while r < rsize { + arr[a] = right_half[r]; + r += 1; + a += 1; + } +} + +fn _merge_sort(arr: &mut [T], lo: usize, hi: usize) { + if lo < hi { + let mid = lo + (hi - lo) / 2; + _merge_sort(arr, lo, mid); + _merge_sort(arr, mid + 1, hi); + _merge(arr, lo, mid, hi); + } +} + +pub fn merge_sort(arr: &mut [T]) { + let len = arr.len(); + if len > 1 { + _merge_sort(arr, 0, len - 1); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + let mut res = vec![10, 8, 4, 3, 1, 9, 2, 7, 5, 6]; + merge_sort(&mut res); + assert_eq!(res, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + } + + #[test] + fn basic_string() { + let mut res = vec!["a", "bb", "d", "cc"]; + merge_sort(&mut res); + assert_eq!(res, vec!["a", "bb", "cc", "d"]); + } + + #[test] + fn empty() { + let mut res = Vec::::new(); + merge_sort(&mut res); + assert_eq!(res, vec![]); + } + + #[test] + fn one_element() { + let mut res = vec![1]; + merge_sort(&mut res); + assert_eq!(res, vec![1]); + } + + #[test] + fn pre_sorted() { + let mut res = vec![1, 2, 3, 4]; + merge_sort(&mut res); + assert_eq!(res, vec![1, 2, 3, 4]); + } + + #[test] + fn reverse_sorted() { + let mut res = vec![4, 3, 2, 1]; + merge_sort(&mut res); + assert_eq!(res, vec![1, 2, 3, 4]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/odd-even.md b/codes/src/algorithms/sorting/odd-even.md new file mode 100644 index 00000000..6a8b1900 --- /dev/null +++ b/codes/src/algorithms/sorting/odd-even.md @@ -0,0 +1,62 @@ +# 奇偶排序 + +```rust +pub fn odd_even_sort(arr: &mut [T]) { + let len = arr.len(); + if len == 0 { + return; + } + + let mut sorted = false; + while !sorted { + sorted = true; + + for i in (1..len - 1).step_by(2) { + if arr[i] > arr[i + 1] { + arr.swap(i, i + 1); + sorted = false; + } + } + + for i in (0..len - 1).step_by(2) { + if arr[i] > arr[i + 1] { + arr.swap(i, i + 1); + sorted = false; + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + let mut arr = vec![3, 5, 1, 2, 4, 6]; + odd_even_sort(&mut arr); + assert_eq!(arr, vec![1, 2, 3, 4, 5, 6]); + } + + #[test] + fn empty() { + let mut arr = Vec::::new(); + odd_even_sort(&mut arr); + assert_eq!(arr, vec![]); + } + + #[test] + fn one_element() { + let mut arr = vec![3]; + odd_even_sort(&mut arr); + assert_eq!(arr, vec![3]); + } + + #[test] + fn pre_sorted() { + let mut arr = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + odd_even_sort(&mut arr); + assert_eq!(arr, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/quick-sort.md b/codes/src/algorithms/sorting/quick-sort.md new file mode 100644 index 00000000..2e0b7b1e --- /dev/null +++ b/codes/src/algorithms/sorting/quick-sort.md @@ -0,0 +1,40 @@ +# 快速排序 + +```rust +use std::cmp::PartialOrd; + +pub fn partition(arr: &mut [T], lo: isize, hi: isize) -> isize { + let pivot = hi as usize; + let mut i = lo - 1; + let mut j = hi; + + loop { + i += 1; + while arr[i as usize] < arr[pivot] { + i += 1; + } + j -= 1; + while j >= 0 && arr[j as usize] > arr[pivot] { + j -= 1; + } + if i >= j { + break; + } else { + arr.swap(i as usize, j as usize); + } + } + arr.swap(i as usize, pivot as usize); + i +} +fn _quick_sort(arr: &mut [T], lo: isize, hi: isize) { + if lo < hi { + let p = partition(arr, lo, hi); + _quick_sort(arr, lo, p - 1); + _quick_sort(arr, p + 1, hi); + } +} +pub fn quick_sort(arr: &mut [T]) { + let len = arr.len(); + _quick_sort(arr, 0, (len - 1) as isize); +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/radix-sort.md b/codes/src/algorithms/sorting/radix-sort.md new file mode 100644 index 00000000..e1895de9 --- /dev/null +++ b/codes/src/algorithms/sorting/radix-sort.md @@ -0,0 +1,66 @@ +# 基数排序 + +```rust +/// Sorts the elements of `arr` in-place using radix sort. +/// +/// Time complexity is `O((n + b) * logb(k))`, where `n` is the number of elements, +/// `b` is the base (the radix), and `k` is the largest element. +/// When `n` and `b` are roughly the same maginitude, this algorithm runs in linear time. +/// +/// Space complexity is `O(n + b)`. +pub fn radix_sort(arr: &mut [u64]) { + let max: usize = match arr.iter().max() { + Some(&x) => x as usize, + None => return, + }; + // Make radix a power of 2 close to arr.len() for optimal runtime + let radix = arr.len().next_power_of_two(); + // Counting sort by each digit from least to most significant + let mut place = 1; + while place <= max { + let digit_of = |x| x as usize / place % radix; + // Count digit occurrences + let mut counter = vec![0; radix]; + for &x in arr.iter() { + counter[digit_of(x)] += 1; + } + // Compute last index of each digit + for i in 1..radix { + counter[i] += counter[i - 1]; + } + // Write elements to their new indices + for &x in arr.to_owned().iter().rev() { + counter[digit_of(x)] -= 1; + arr[counter[digit_of(x)]] = x; + } + place *= radix; + } +} + +#[cfg(test)] +mod tests { + use super::super::is_sorted; + use super::radix_sort; + + #[test] + fn empty() { + let mut a: [u64; 0] = []; + radix_sort(&mut a); + assert!(is_sorted(&a)); + } + + #[test] + fn descending() { + let mut v = vec![201, 127, 64, 37, 24, 4, 1]; + radix_sort(&mut v); + assert!(is_sorted(&v)); + } + + #[test] + fn ascending() { + let mut v = vec![1, 4, 24, 37, 64, 127, 201]; + radix_sort(&mut v); + assert!(is_sorted(&v)); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/selection-sort.md b/codes/src/algorithms/sorting/selection-sort.md new file mode 100644 index 00000000..73b3dcfa --- /dev/null +++ b/codes/src/algorithms/sorting/selection-sort.md @@ -0,0 +1,49 @@ +# 选择排序 + +```rust +pub fn selection_sort(arr: &mut [T]) { + let len = arr.len(); + for left in 0..len { + let mut smallest = left; + for right in (left + 1)..len { + if arr[right] < arr[smallest] { + smallest = right; + } + } + arr.swap(smallest, left); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + let mut res = vec!["d", "a", "c", "b"]; + selection_sort(&mut res); + assert_eq!(res, vec!["a", "b", "c", "d"]); + } + + #[test] + fn empty() { + let mut res = Vec::::new(); + selection_sort(&mut res); + assert_eq!(res, vec![]); + } + + #[test] + fn one_element() { + let mut res = vec!["a"]; + selection_sort(&mut res); + assert_eq!(res, vec!["a"]); + } + + #[test] + fn pre_sorted() { + let mut res = vec!["a", "b", "c"]; + selection_sort(&mut res); + assert_eq!(res, vec!["a", "b", "c"]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/shell-sort.md b/codes/src/algorithms/sorting/shell-sort.md new file mode 100644 index 00000000..bbd46c61 --- /dev/null +++ b/codes/src/algorithms/sorting/shell-sort.md @@ -0,0 +1,66 @@ +# 希尔排序 + +```rust +pub fn shell_sort(values: &mut Vec) { + // shell sort works by swiping the value at a given gap and decreasing the gap to 1 + fn insertion(values: &mut Vec, start: usize, gap: usize) { + for i in ((start + gap)..values.len()).step_by(gap) { + let val_current = values[i]; + let mut pos = i; + // make swaps + while pos >= gap && values[pos - gap] > val_current { + values[pos] = values[pos - gap]; + pos -= gap; + } + values[pos] = val_current; + } + } + + let mut count_sublist = values.len() / 2; // makes gap as long as half of the array + while count_sublist > 0 { + for pos_start in 0..count_sublist { + insertion(values, pos_start, count_sublist); + } + count_sublist /= 2; // makes gap as half of previous + } +} + +#[cfg(test)] +mod test { + use super::shell_sort; + + #[test] + fn basic() { + let mut vec = vec![3, 5, 6, 3, 1, 4]; + shell_sort(&mut vec); + for i in 0..vec.len() - 1 { + assert!(vec[i] <= vec[i + 1]); + } + } + + #[test] + fn empty() { + let mut vec: Vec = vec![]; + shell_sort(&mut vec); + assert_eq!(vec, vec![]); + } + + #[test] + fn reverse() { + let mut vec = vec![6, 5, 4, 3, 2, 1]; + shell_sort(&mut vec); + for i in 0..vec.len() - 1 { + assert!(vec[i] <= vec[i + 1]); + } + } + + #[test] + fn already_sorted() { + let mut vec = vec![1, 2, 3, 4, 5, 6]; + shell_sort(&mut vec); + for i in 0..vec.len() - 1 { + assert!(vec[i] <= vec[i + 1]); + } + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/sorting/stooge-sort.md b/codes/src/algorithms/sorting/stooge-sort.md new file mode 100644 index 00000000..766ca0b7 --- /dev/null +++ b/codes/src/algorithms/sorting/stooge-sort.md @@ -0,0 +1,67 @@ +# 臭皮匠排序 + +```rust +fn _stooge_sort(arr: &mut [T], start: usize, end: usize) { + if arr[start] > arr[end] { + arr.swap(start, end); + } + + if start + 1 >= end { + return; + } + + let k = (end - start + 1) / 3; + + _stooge_sort(arr, start, end - k); + _stooge_sort(arr, start + k, end); + _stooge_sort(arr, start, end - k); +} + +pub fn stooge_sort(arr: &mut [T]) { + let len = arr.len(); + if len == 0 { + return; + } + + _stooge_sort(arr, 0, len - 1); +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn basic() { + let mut vec = vec![3, 5, 6, 3, 1, 4]; + stooge_sort(&mut vec); + for i in 0..vec.len() - 1 { + assert!(vec[i] <= vec[i + 1]); + } + } + + #[test] + fn empty() { + let mut vec: Vec = vec![]; + stooge_sort(&mut vec); + assert_eq!(vec, vec![]); + } + + #[test] + fn reverse() { + let mut vec = vec![6, 5, 4, 3, 2, 1]; + stooge_sort(&mut vec); + for i in 0..vec.len() - 1 { + assert!(vec[i] <= vec[i + 1]); + } + } + + #[test] + fn already_sorted() { + let mut vec = vec![1, 2, 3, 4, 5, 6]; + stooge_sort(&mut vec); + for i in 0..vec.len() - 1 { + assert!(vec[i] <= vec[i + 1]); + } + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/string/burrows-wheeler-transform.md b/codes/src/algorithms/string/burrows-wheeler-transform.md new file mode 100644 index 00000000..9b1faf19 --- /dev/null +++ b/codes/src/algorithms/string/burrows-wheeler-transform.md @@ -0,0 +1,98 @@ +# 数据转换算法(Burrows Wheeler Transform) + +```rust +pub fn burrows_wheeler_transform(input: String) -> (String, usize) { + let len = input.len(); + + let mut table = Vec::::with_capacity(len); + for i in 0..len { + table.push(input[i..].to_owned() + &input[..i]); + } + table.sort_by_key(|a| a.to_lowercase()); + + let mut encoded = String::new(); + let mut index: usize = 0; + for (i, item) in table.iter().enumerate().take(len) { + encoded.push(item.chars().last().unwrap()); + if item.eq(&input) { + index = i; + } + } + + (encoded, index) +} + +pub fn inv_burrows_wheeler_transform(input: (String, usize)) -> String { + let len = input.0.len(); + let mut table = Vec::<(usize, char)>::with_capacity(len); + for i in 0..len { + table.push((i, input.0.chars().nth(i).unwrap())); + } + + table.sort_by(|a, b| a.1.cmp(&b.1)); + + let mut decoded = String::new(); + let mut idx = input.1; + for _ in 0..len { + decoded.push(table[idx].1); + idx = table[idx].0; + } + + decoded +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + assert_eq!( + inv_burrows_wheeler_transform(burrows_wheeler_transform("CARROT".to_string())), + "CARROT" + ); + assert_eq!( + inv_burrows_wheeler_transform(burrows_wheeler_transform("TOMATO".to_string())), + "TOMATO" + ); + assert_eq!( + inv_burrows_wheeler_transform(burrows_wheeler_transform("THISISATEST".to_string())), + "THISISATEST" + ); + assert_eq!( + inv_burrows_wheeler_transform(burrows_wheeler_transform("THEALGORITHMS".to_string())), + "THEALGORITHMS" + ); + assert_eq!( + inv_burrows_wheeler_transform(burrows_wheeler_transform("RUST".to_string())), + "RUST" + ); + } + + #[test] + fn special_characters() { + assert_eq!( + inv_burrows_wheeler_transform(burrows_wheeler_transform("!.!.!??.=::".to_string())), + "!.!.!??.=::" + ); + assert_eq!( + inv_burrows_wheeler_transform(burrows_wheeler_transform( + "!{}{}(((&&%%!??.=::".to_string() + )), + "!{}{}(((&&%%!??.=::" + ); + assert_eq!( + inv_burrows_wheeler_transform(burrows_wheeler_transform("//&$[]".to_string())), + "//&$[]" + ); + } + + #[test] + fn empty() { + assert_eq!( + inv_burrows_wheeler_transform(burrows_wheeler_transform("".to_string())), + "" + ); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/string/index.md b/codes/src/algorithms/string/index.md new file mode 100644 index 00000000..2cd6ea9d --- /dev/null +++ b/codes/src/algorithms/string/index.md @@ -0,0 +1,3 @@ +# 字符串 + +字符串相关的算法往往和子串匹配、顺序调整相关,如何高效的处理字符串,有时会成为一个程序性能的关键。 \ No newline at end of file diff --git a/codes/src/algorithms/string/knuth-morris-pratt.md b/codes/src/algorithms/string/knuth-morris-pratt.md new file mode 100644 index 00000000..a1111568 --- /dev/null +++ b/codes/src/algorithms/string/knuth-morris-pratt.md @@ -0,0 +1,101 @@ +# KMP算法(Knuth Morris Pratt) + +```rust +pub fn knuth_morris_pratt(st: String, pat: String) -> Vec { + if st.is_empty() || pat.is_empty() { + return vec![]; + } + + let string = st.into_bytes(); + let pattern = pat.into_bytes(); + + // build the partial match table + let mut partial = vec![0]; + for i in 1..pattern.len() { + let mut j = partial[i - 1]; + while j > 0 && pattern[j] != pattern[i] { + j = partial[j - 1]; + } + partial.push(if pattern[j] == pattern[i] { j + 1 } else { j }); + } + + // and read 'string' to find 'pattern' + let mut ret = vec![]; + let mut j = 0; + + for (i, &c) in string.iter().enumerate() { + while j > 0 && c != pattern[j] { + j = partial[j - 1]; + } + if c == pattern[j] { + j += 1; + } + if j == pattern.len() { + ret.push(i + 1 - j); + j = partial[j - 1]; + } + } + + ret +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn each_letter_matches() { + let index = knuth_morris_pratt("aaa".to_string(), "a".to_string()); + assert_eq!(index, vec![0, 1, 2]); + } + + #[test] + fn a_few_separate_matches() { + let index = knuth_morris_pratt("abababa".to_string(), "ab".to_string()); + assert_eq!(index, vec![0, 2, 4]); + } + + #[test] + fn one_match() { + let index = + knuth_morris_pratt("ABC ABCDAB ABCDABCDABDE".to_string(), "ABCDABD".to_string()); + assert_eq!(index, vec![15]); + } + + #[test] + fn lots_of_matches() { + let index = knuth_morris_pratt("aaabaabaaaaa".to_string(), "aa".to_string()); + assert_eq!(index, vec![0, 1, 4, 7, 8, 9, 10]); + } + + #[test] + fn lots_of_intricate_matches() { + let index = knuth_morris_pratt("ababababa".to_string(), "aba".to_string()); + assert_eq!(index, vec![0, 2, 4, 6]); + } + + #[test] + fn not_found0() { + let index = knuth_morris_pratt("abcde".to_string(), "f".to_string()); + assert_eq!(index, vec![]); + } + + #[test] + fn not_found1() { + let index = knuth_morris_pratt("abcde".to_string(), "ac".to_string()); + assert_eq!(index, vec![]); + } + + #[test] + fn not_found2() { + let index = knuth_morris_pratt("ababab".to_string(), "bababa".to_string()); + assert_eq!(index, vec![]); + } + + #[test] + fn empty_string() { + let index = knuth_morris_pratt("".to_string(), "abcdef".to_string()); + assert_eq!(index, vec![]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/string/manacher.md b/codes/src/algorithms/string/manacher.md new file mode 100644 index 00000000..60cca7ba --- /dev/null +++ b/codes/src/algorithms/string/manacher.md @@ -0,0 +1,95 @@ +# 马拉车算法(Manacher) + +```rust +pub fn manacher(s: String) -> String { + let l = s.len(); + if l <= 1 { + return s; + } + + // MEMO: We need to detect odd palindrome as well, + // therefore, inserting dummy string so that + // we can find a pair with dummy center character. + let mut chars: Vec = Vec::with_capacity(s.len() * 2 + 1); + for c in s.chars() { + chars.push('#'); + chars.push(c); + } + chars.push('#'); + + // List: storing the length of palindrome at each index of string + let mut length_of_palindrome = vec![1usize; chars.len()]; + // Integer: Current checking palindrome's center index + let mut current_center: usize = 0; + // Integer: Right edge index existing the radius away from current center + let mut right_from_current_center: usize = 0; + + for i in 0..chars.len() { + // 1: Check if we are looking at right side of palindrome. + if right_from_current_center > i && i > current_center { + // 1-1: If so copy from the left side of palindrome. + // If the value + index exceeds the right edge index, we should cut and check palindrome later #3. + length_of_palindrome[i] = std::cmp::min( + right_from_current_center - i, + length_of_palindrome[2 * current_center - i], + ); + // 1-2: Move the checking palindrome to new index if it exceeds the right edge. + if length_of_palindrome[i] + i >= right_from_current_center { + current_center = i; + right_from_current_center = length_of_palindrome[i] + i; + // 1-3: If radius exceeds the end of list, it means checking is over. + // You will never get the larger value because the string will get only shorter. + if right_from_current_center >= chars.len() - 1 { + break; + } + } else { + // 1-4: If the checking index doesn't exceeds the right edge, + // it means the length is just as same as the left side. + // You don't need to check anymore. + continue; + } + } + + // Integer: Current radius from checking index + // If it's copied from left side and more than 1, + // it means it's ensured so you don't need to check inside radius. + let mut radius: usize = (length_of_palindrome[i] - 1) / 2; + radius += 1; + // 2: Checking palindrome. + // Need to care about overflow usize. + while i >= radius && i + radius <= chars.len() - 1 && chars[i - radius] == chars[i + radius] + { + length_of_palindrome[i] += 2; + radius += 1; + } + } + + // 3: Find the maximum length and generate answer. + let center_of_max = length_of_palindrome + .iter() + .enumerate() + .max_by_key(|(_, &value)| value) + .map(|(idx, _)| idx) + .unwrap(); + let radius_of_max = (length_of_palindrome[center_of_max] - 1) / 2; + let answer = &chars[(center_of_max - radius_of_max)..(center_of_max + radius_of_max + 1)] + .iter() + .collect::(); + answer.replace("#", "") +} + +#[cfg(test)] +mod tests { + use super::manacher; + + #[test] + fn get_longest_palindrome_by_manacher() { + assert_eq!(manacher("babad".to_string()), "aba".to_string()); + assert_eq!(manacher("cbbd".to_string()), "bb".to_string()); + assert_eq!(manacher("a".to_string()), "a".to_string()); + + let ac_ans = manacher("ac".to_string()); + assert!(ac_ans == "a".to_string() || ac_ans == "c".to_string()); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/string/rabin-karp.md b/codes/src/algorithms/string/rabin-karp.md new file mode 100644 index 00000000..c00d22ee --- /dev/null +++ b/codes/src/algorithms/string/rabin-karp.md @@ -0,0 +1,118 @@ +# Rabin Karp算法 + +```rust +pub fn rabin_karp(target: String, pattern: String) -> Vec { + // Quick exit + if target.is_empty() || pattern.is_empty() || pattern.len() > target.len() { + return vec![]; + } + + let string: String = (&pattern[0..pattern.len()]).to_string(); + let hash_pattern = hash(string.clone()); + let mut ret = vec![]; + for i in 0..(target.len() - pattern.len() + 1) { + let s = (&target[i..(i + pattern.len())]).to_string(); + let string_hash = hash(s.clone()); + + if string_hash == hash_pattern && s == string { + ret.push(i); + } + } + ret +} + +fn hash(mut s: String) -> u16 { + let prime: u16 = 101; + let last_char = s + .drain(s.len() - 1..) + .next() + .expect("Failed to get the last char of the string"); + let mut res: u16 = 0; + for (i, &c) in s.as_bytes().iter().enumerate() { + if i == 0 { + res = (c as u16 * 256) % prime; + } else { + res = (((res + c as u16) % 101) * 256) % 101; + } + } + (res + last_char as u16) % prime +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn hi_hash() { + let hash_result = hash("hi".to_string()); + assert_eq!(hash_result, 65); + } + + #[test] + fn abr_hash() { + let hash_result = hash("abr".to_string()); + assert_eq!(hash_result, 4); + } + + #[test] + fn bra_hash() { + let hash_result = hash("bra".to_string()); + assert_eq!(hash_result, 30); + } + + // Attribution to @pgimalac for his tests from Knuth-Morris-Pratt + #[test] + fn each_letter_matches() { + let index = rabin_karp("aaa".to_string(), "a".to_string()); + assert_eq!(index, vec![0, 1, 2]); + } + + #[test] + fn a_few_separate_matches() { + let index = rabin_karp("abababa".to_string(), "ab".to_string()); + assert_eq!(index, vec![0, 2, 4]); + } + + #[test] + fn one_match() { + let index = rabin_karp("ABC ABCDAB ABCDABCDABDE".to_string(), "ABCDABD".to_string()); + assert_eq!(index, vec![15]); + } + + #[test] + fn lots_of_matches() { + let index = rabin_karp("aaabaabaaaaa".to_string(), "aa".to_string()); + assert_eq!(index, vec![0, 1, 4, 7, 8, 9, 10]); + } + + #[test] + fn lots_of_intricate_matches() { + let index = rabin_karp("ababababa".to_string(), "aba".to_string()); + assert_eq!(index, vec![0, 2, 4, 6]); + } + + #[test] + fn not_found0() { + let index = rabin_karp("abcde".to_string(), "f".to_string()); + assert_eq!(index, vec![]); + } + + #[test] + fn not_found1() { + let index = rabin_karp("abcde".to_string(), "ac".to_string()); + assert_eq!(index, vec![]); + } + + #[test] + fn not_found2() { + let index = rabin_karp("ababab".to_string(), "bababa".to_string()); + assert_eq!(index, vec![]); + } + + #[test] + fn empty_string() { + let index = rabin_karp("".to_string(), "abcdef".to_string()); + assert_eq!(index, vec![]); + } +} +``` \ No newline at end of file diff --git a/codes/src/algorithms/string/reverse.md b/codes/src/algorithms/string/reverse.md new file mode 100644 index 00000000..e11b9e93 --- /dev/null +++ b/codes/src/algorithms/string/reverse.md @@ -0,0 +1,22 @@ +# 逆序倒转 + +```rust +pub fn reverse(text: &str) -> String { + text.chars().rev().collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + assert_eq!(reverse("racecar"), "racecar"); + } + + #[test] + fn test_sentence() { + assert_eq!(reverse("step on no pets"), "step on no pets"); + } +} +``` \ No newline at end of file diff --git a/codes/src/data-structures/avl-tree.md b/codes/src/data-structures/avl-tree.md new file mode 100644 index 00000000..158b760e --- /dev/null +++ b/codes/src/data-structures/avl-tree.md @@ -0,0 +1,392 @@ +# avl树 + +An AVL Tree is a self-balancing binary search tree. The heights of any two sibling +nodes must differ by at most one; the tree may rebalance itself after insertion or +deletion to uphold this property. + +__Properties__ +* Worst/Average time complexity for basic operations: O(log n) +* Worst/Average space complexity: O(n) + +```rust +use std::{ + cmp::{max, Ordering}, + iter::FromIterator, + mem, + ops::Not, +}; + +/// An internal node of an `AVLTree`. +struct AVLNode { + value: T, + height: usize, + left: Option>>, + right: Option>>, +} + +/// A set based on an AVL Tree. +/// +/// An AVL Tree is a self-balancing binary search tree. It tracks the height of each node +/// and performs internal rotations to maintain a height difference of at most 1 between +/// each sibling pair. +pub struct AVLTree { + root: Option>>, + length: usize, +} + +/// Refers to the left or right subtree of an `AVLNode`. +#[derive(Clone, Copy)] +enum Side { + Left, + Right, +} + +impl AVLTree { + /// Creates an empty `AVLTree`. + pub fn new() -> AVLTree { + AVLTree { + root: None, + length: 0, + } + } + + /// Returns `true` if the tree contains a value. + pub fn contains(&self, value: &T) -> bool { + let mut current = &self.root; + while let Some(node) = current { + current = match value.cmp(&node.value) { + Ordering::Equal => return true, + Ordering::Less => &node.left, + Ordering::Greater => &node.right, + } + } + false + } + + /// Adds a value to the tree. + /// + /// Returns `true` if the tree did not yet contain the value. + pub fn insert(&mut self, value: T) -> bool { + let inserted = insert(&mut self.root, value); + if inserted { + self.length += 1; + } + inserted + } + + /// Removes a value from the tree. + /// + /// Returns `true` if the tree contained the value. + pub fn remove(&mut self, value: &T) -> bool { + let removed = remove(&mut self.root, value); + if removed { + self.length -= 1; + } + removed + } + + /// Returns the number of values in the tree. + pub fn len(&self) -> usize { + self.length + } + + /// Returns `true` if the tree contains no values. + pub fn is_empty(&self) -> bool { + self.length == 0 + } + + /// Returns an iterator that visits the nodes in the tree in order. + fn node_iter(&self) -> NodeIter { + let cap = self.root.as_ref().map_or(0, |n| n.height); + let mut node_iter = NodeIter { + stack: Vec::with_capacity(cap), + }; + // Initialize stack with path to leftmost child + let mut child = &self.root; + while let Some(node) = child { + node_iter.stack.push(node.as_ref()); + child = &node.left; + } + node_iter + } + + /// Returns an iterator that visits the values in the tree in ascending order. + pub fn iter(&self) -> Iter { + Iter { + node_iter: self.node_iter(), + } + } +} + +/// Recursive helper function for `AVLTree` insertion. +fn insert(tree: &mut Option>>, value: T) -> bool { + if let Some(node) = tree { + let inserted = match value.cmp(&node.value) { + Ordering::Equal => false, + Ordering::Less => insert(&mut node.left, value), + Ordering::Greater => insert(&mut node.right, value), + }; + if inserted { + node.rebalance(); + } + inserted + } else { + *tree = Some(Box::new(AVLNode { + value, + height: 1, + left: None, + right: None, + })); + true + } +} + +/// Recursive helper function for `AVLTree` deletion. +fn remove(tree: &mut Option>>, value: &T) -> bool { + if let Some(node) = tree { + let removed = match value.cmp(&node.value) { + Ordering::Less => remove(&mut node.left, value), + Ordering::Greater => remove(&mut node.right, value), + Ordering::Equal => { + *tree = match (node.left.take(), node.right.take()) { + (None, None) => None, + (Some(b), None) | (None, Some(b)) => Some(b), + (Some(left), Some(right)) => Some(merge(left, right)), + }; + return true; + } + }; + if removed { + node.rebalance(); + } + removed + } else { + false + } +} + +/// Merges two trees and returns the root of the merged tree. +fn merge(left: Box>, right: Box>) -> Box> { + let mut op_right = Some(right); + // Guaranteed not to panic since right has at least one node + let mut root = take_min(&mut op_right).unwrap(); + root.left = Some(left); + root.right = op_right; + root.rebalance(); + root +} + +/// Removes the smallest node from the tree, if one exists. +fn take_min(tree: &mut Option>>) -> Option>> { + if let Some(mut node) = tree.take() { + // Recurse along the left side + if let Some(small) = take_min(&mut node.left) { + // Took the smallest from below; update this node and put it back in the tree + node.rebalance(); + *tree = Some(node); + Some(small) + } else { + // Take this node and replace it with its right child + *tree = node.right.take(); + Some(node) + } + } else { + None + } +} + +impl AVLNode { + /// Returns a reference to the left or right child. + fn child(&self, side: Side) -> &Option>> { + match side { + Side::Left => &self.left, + Side::Right => &self.right, + } + } + + /// Returns a mutable reference to the left or right child. + fn child_mut(&mut self, side: Side) -> &mut Option>> { + match side { + Side::Left => &mut self.left, + Side::Right => &mut self.right, + } + } + + /// Returns the height of the left or right subtree. + fn height(&self, side: Side) -> usize { + self.child(side).as_ref().map_or(0, |n| n.height) + } + + /// Returns the height difference between the left and right subtrees. + fn balance_factor(&self) -> i8 { + let (left, right) = (self.height(Side::Left), self.height(Side::Right)); + if left < right { + (right - left) as i8 + } else { + -((left - right) as i8) + } + } + + /// Recomputes the `height` field. + fn update_height(&mut self) { + self.height = 1 + max(self.height(Side::Left), self.height(Side::Right)); + } + + /// Performs a left or right rotation. + fn rotate(&mut self, side: Side) { + let mut subtree = self.child_mut(!side).take().unwrap(); + *self.child_mut(!side) = subtree.child_mut(side).take(); + self.update_height(); + // Swap root and child nodes in memory + mem::swap(self, subtree.as_mut()); + // Set old root (subtree) as child of new root (self) + *self.child_mut(side) = Some(subtree); + self.update_height(); + } + + /// Performs left or right tree rotations to balance this node. + fn rebalance(&mut self) { + self.update_height(); + let side = match self.balance_factor() { + -2 => Side::Left, + 2 => Side::Right, + _ => return, + }; + let subtree = self.child_mut(side).as_mut().unwrap(); + // Left-Right and Right-Left require rotation of heavy subtree + if let (Side::Left, 1) | (Side::Right, -1) = (side, subtree.balance_factor()) { + subtree.rotate(side); + } + // Rotate in opposite direction of heavy side + self.rotate(!side); + } +} + +impl Default for AVLTree { + fn default() -> Self { + Self::new() + } +} + +impl Not for Side { + type Output = Side; + + fn not(self) -> Self::Output { + match self { + Side::Left => Side::Right, + Side::Right => Side::Left, + } + } +} + +impl FromIterator for AVLTree { + fn from_iter>(iter: I) -> Self { + let mut tree = AVLTree::new(); + for value in iter { + tree.insert(value); + } + tree + } +} + +/// An iterator over the nodes of an `AVLTree`. +/// +/// This struct is created by the `node_iter` method of `AVLTree`. +struct NodeIter<'a, T: Ord> { + stack: Vec<&'a AVLNode>, +} + +impl<'a, T: Ord> Iterator for NodeIter<'a, T> { + type Item = &'a AVLNode; + + fn next(&mut self) -> Option { + if let Some(node) = self.stack.pop() { + // Push left path of right subtree to stack + let mut child = &node.right; + while let Some(subtree) = child { + self.stack.push(subtree.as_ref()); + child = &subtree.left; + } + Some(node) + } else { + None + } + } +} + +/// An iterator over the items of an `AVLTree`. +/// +/// This struct is created by the `iter` method of `AVLTree`. +pub struct Iter<'a, T: Ord> { + node_iter: NodeIter<'a, T>, +} + +impl<'a, T: Ord> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + match self.node_iter.next() { + Some(node) => Some(&node.value), + None => None, + } + } +} + +#[cfg(test)] +mod tests { + use super::AVLTree; + + /// Returns `true` if all nodes in the tree are balanced. + fn is_balanced(tree: &AVLTree) -> bool { + tree.node_iter() + .all(|n| (-1..=1).contains(&n.balance_factor())) + } + + #[test] + fn len() { + let tree: AVLTree<_> = (1..4).collect(); + assert_eq!(tree.len(), 3); + } + + #[test] + fn contains() { + let tree: AVLTree<_> = (1..4).collect(); + assert!(tree.contains(&1)); + assert!(!tree.contains(&4)); + } + + #[test] + fn insert() { + let mut tree = AVLTree::new(); + // First insert succeeds + assert!(tree.insert(1)); + // Second insert fails + assert!(!tree.insert(1)); + } + + #[test] + fn remove() { + let mut tree: AVLTree<_> = (1..8).collect(); + // First remove succeeds + assert!(tree.remove(&4)); + // Second remove fails + assert!(!tree.remove(&4)); + } + + #[test] + fn sorted() { + let tree: AVLTree<_> = (1..8).rev().collect(); + assert!((1..8).eq(tree.iter().map(|&x| x))); + } + + #[test] + fn balanced() { + let mut tree: AVLTree<_> = (1..8).collect(); + assert!(is_balanced(&tree)); + for x in 1..8 { + tree.remove(&x); + assert!(is_balanced(&tree)); + } + } +} +``` \ No newline at end of file diff --git a/codes/src/data-structures/b-tree.md b/codes/src/data-structures/b-tree.md new file mode 100644 index 00000000..1feb60fd --- /dev/null +++ b/codes/src/data-structures/b-tree.md @@ -0,0 +1,204 @@ +# B树 + +B-Trees are version of 2-3 trees, which are self-balancing. They are used to improve Disk reads and have a complexity of +O(log(n)), for every tree operations.The number of Childrens/Keys a particular node has, is +determined by the Branching Factor/Degree of that tree. +B-Trees will always have sorted keys. + +- Branching Factor(B) / Degree (D): + If B = n, n <= Children per Node < 2(n), n-1 <= Keys per Node < 2(n) - 1 + +__Properties__ +* Worst/Average case performance for all operations O(log n) +* Space complexity O(n) + +```rust +use std::convert::TryFrom; +use std::fmt::Debug; +use std::mem; + +struct Node { + keys: Vec, + children: Vec>, +} + +pub struct BTree { + root: Node, + props: BTreeProps, +} + +// Why to need a different Struct for props... +// Check - http://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts/#fnref:improvement +struct BTreeProps { + degree: usize, + max_keys: usize, + mid_key_index: usize, +} + +impl Node +where + T: Ord, +{ + fn new(degree: usize, _keys: Option>, _children: Option>>) -> Self { + Node { + keys: match _keys { + Some(_keys) => _keys, + None => Vec::with_capacity(degree - 1), + }, + children: match _children { + Some(_children) => _children, + None => Vec::with_capacity(degree), + }, + } + } + + fn is_leaf(&self) -> bool { + self.children.is_empty() + } +} + +impl BTreeProps { + fn new(degree: usize) -> Self { + BTreeProps { + degree, + max_keys: degree - 1, + mid_key_index: (degree - 1) / 2, + } + } + + fn is_maxed_out(&self, node: &Node) -> bool { + node.keys.len() == self.max_keys + } + + // Split Child expects the Child Node to be full + /// Move the middle_key to parent node and split the child_node's + /// keys/chilren_nodes into half + fn split_child(&self, parent: &mut Node, child_index: usize) { + let child = &mut parent.children[child_index]; + let middle_key = child.keys[self.mid_key_index]; + let right_keys = match child.keys.split_off(self.mid_key_index).split_first() { + Some((_first, _others)) => { + // We don't need _first, as it will move to parent node. + _others.to_vec() + } + None => Vec::with_capacity(self.max_keys), + }; + let right_children = if !child.is_leaf() { + Some(child.children.split_off(self.mid_key_index + 1)) + } else { + None + }; + let new_child_node: Node = Node::new(self.degree, Some(right_keys), right_children); + + parent.keys.insert(child_index, middle_key); + parent.children.insert(child_index + 1, new_child_node); + } + + fn insert_non_full(&mut self, node: &mut Node, key: T) { + let mut index: isize = isize::try_from(node.keys.len()).ok().unwrap() - 1; + while index >= 0 && node.keys[index as usize] >= key { + index -= 1; + } + + let mut u_index: usize = usize::try_from(index + 1).ok().unwrap(); + if node.is_leaf() { + // Just insert it, as we know this method will be called only when node is not full + node.keys.insert(u_index, key); + } else { + if self.is_maxed_out(&node.children[u_index]) { + self.split_child(node, u_index); + if node.keys[u_index] < key { + u_index += 1; + } + } + + self.insert_non_full(&mut node.children[u_index], key); + } + } + + fn traverse_node(&self, node: &Node, depth: usize) { + if node.is_leaf() { + print!(" {0:{<1$}{2:?}{0:}<1$} ", "", depth, node.keys); + } else { + let _depth = depth + 1; + for (index, key) in node.keys.iter().enumerate() { + self.traverse_node(&node.children[index], _depth); + // Check https://doc.rust-lang.org/std/fmt/index.html + // And https://stackoverflow.com/a/35280799/2849127 + print!("{0:{<1$}{2:?}{0:}<1$}", "", depth, key); + } + self.traverse_node(node.children.last().unwrap(), _depth); + } + } +} + +impl BTree +where + T: Ord + Copy + Debug + Default, +{ + pub fn new(branch_factor: usize) -> Self { + let degree = 2 * branch_factor; + BTree { + root: Node::new(degree, None, None), + props: BTreeProps::new(degree), + } + } + + pub fn insert(&mut self, key: T) { + if self.props.is_maxed_out(&self.root) { + // Create an empty root and split the old root... + let mut new_root = Node::new(self.props.degree, None, None); + mem::swap(&mut new_root, &mut self.root); + self.root.children.insert(0, new_root); + self.props.split_child(&mut self.root, 0); + } + self.props.insert_non_full(&mut self.root, key); + } + + pub fn traverse(&self) { + self.props.traverse_node(&self.root, 0); + println!(); + } + + pub fn search(&self, key: T) -> bool { + let mut current_node = &self.root; + let mut index: isize; + loop { + index = isize::try_from(current_node.keys.len()).ok().unwrap() - 1; + while index >= 0 && current_node.keys[index as usize] > key { + index -= 1; + } + + let u_index: usize = usize::try_from(index + 1).ok().unwrap(); + if index >= 0 && current_node.keys[u_index - 1] == key { + break true; + } else if current_node.is_leaf() { + break false; + } else { + current_node = ¤t_node.children[u_index]; + } + } + } +} + +#[cfg(test)] +mod test { + use super::BTree; + + #[test] + fn test_search() { + let mut tree = BTree::new(2); + tree.insert(10); + tree.insert(20); + tree.insert(30); + tree.insert(5); + tree.insert(6); + tree.insert(7); + tree.insert(11); + tree.insert(12); + tree.insert(15); + assert!(tree.search(15)); + assert_eq!(tree.search(16), false); + } +} +``` \ No newline at end of file diff --git a/codes/src/data-structures/binary-search-tree.md b/codes/src/data-structures/binary-search-tree.md new file mode 100644 index 00000000..95f46d63 --- /dev/null +++ b/codes/src/data-structures/binary-search-tree.md @@ -0,0 +1,352 @@ +# 二叉树 + +```rust +use std::cmp::Ordering; +use std::ops::Deref; + +/// This struct implements as Binary Search Tree (BST), which is a +/// simple data structure for storing sorted data +pub struct BinarySearchTree +where + T: Ord, +{ + value: Option, + left: Option>>, + right: Option>>, +} + +impl Default for BinarySearchTree +where + T: Ord, +{ + fn default() -> Self { + Self::new() + } +} + +impl BinarySearchTree +where + T: Ord, +{ + /// Create a new, empty BST + pub fn new() -> BinarySearchTree { + BinarySearchTree { + value: None, + left: None, + right: None, + } + } + + /// Find a value in this tree. Returns True iff value is in this + /// tree, and false otherwise + pub fn search(&self, value: &T) -> bool { + match &self.value { + Some(key) => { + match key.cmp(value) { + Ordering::Equal => { + // key == value + true + } + Ordering::Greater => { + // key > value + match &self.left { + Some(node) => node.search(value), + None => false, + } + } + Ordering::Less => { + // key < value + match &self.right { + Some(node) => node.search(value), + None => false, + } + } + } + } + None => false, + } + } + + /// Returns a new iterator which iterates over this tree in order + pub fn iter(&self) -> impl Iterator { + BinarySearchTreeIter::new(self) + } + + /// Insert a value into the appropriate location in this tree. + pub fn insert(&mut self, value: T) { + if self.value.is_none() { + self.value = Some(value); + } else { + match &self.value { + None => (), + Some(key) => { + let target_node = if value < *key { + &mut self.left + } else { + &mut self.right + }; + match target_node { + Some(ref mut node) => { + node.insert(value); + } + None => { + let mut node = BinarySearchTree::new(); + node.insert(value); + *target_node = Some(Box::new(node)); + } + } + } + } + } + } + + /// Returns the smallest value in this tree + pub fn minimum(&self) -> Option<&T> { + match &self.left { + Some(node) => node.minimum(), + None => match &self.value { + Some(value) => Some(value), + None => None, + }, + } + } + + /// Returns the largest value in this tree + pub fn maximum(&self) -> Option<&T> { + match &self.right { + Some(node) => node.maximum(), + None => match &self.value { + Some(value) => Some(value), + None => None, + }, + } + } + + /// Returns the largest value in this tree smaller than value + pub fn floor(&self, value: &T) -> Option<&T> { + match &self.value { + Some(key) => { + match key.cmp(value) { + Ordering::Greater => { + // key > value + match &self.left { + Some(node) => node.floor(value), + None => None, + } + } + Ordering::Less => { + // key < value + match &self.right { + Some(node) => { + let val = node.floor(value); + match val { + Some(_) => val, + None => Some(key), + } + } + None => Some(key), + } + } + Ordering::Equal => Some(key), + } + } + None => None, + } + } + + /// Returns the smallest value in this tree larger than value + pub fn ceil(&self, value: &T) -> Option<&T> { + match &self.value { + Some(key) => { + match key.cmp(value) { + Ordering::Less => { + // key < value + match &self.right { + Some(node) => node.ceil(value), + None => None, + } + } + Ordering::Greater => { + // key > value + match &self.left { + Some(node) => { + let val = node.ceil(value); + match val { + Some(_) => val, + None => Some(key), + } + } + None => Some(key), + } + } + Ordering::Equal => { + // key == value + Some(key) + } + } + } + None => None, + } + } +} + +struct BinarySearchTreeIter<'a, T> +where + T: Ord, +{ + stack: Vec<&'a BinarySearchTree>, +} + +impl<'a, T> BinarySearchTreeIter<'a, T> +where + T: Ord, +{ + pub fn new(tree: &BinarySearchTree) -> BinarySearchTreeIter { + let mut iter = BinarySearchTreeIter { stack: vec![tree] }; + iter.stack_push_left(); + iter + } + + fn stack_push_left(&mut self) { + while let Some(child) = &self.stack.last().unwrap().left { + self.stack.push(child); + } + } +} + +impl<'a, T> Iterator for BinarySearchTreeIter<'a, T> +where + T: Ord, +{ + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + if self.stack.is_empty() { + None + } else { + let node = self.stack.pop().unwrap(); + if node.right.is_some() { + self.stack.push(node.right.as_ref().unwrap().deref()); + self.stack_push_left(); + } + node.value.as_ref() + } + } +} + +#[cfg(test)] +mod test { + use super::BinarySearchTree; + + fn prequel_memes_tree() -> BinarySearchTree<&'static str> { + let mut tree = BinarySearchTree::new(); + tree.insert("hello there"); + tree.insert("general kenobi"); + tree.insert("you are a bold one"); + tree.insert("kill him"); + tree.insert("back away...I will deal with this jedi slime myself"); + tree.insert("your move"); + tree.insert("you fool"); + tree + } + + #[test] + fn test_search() { + let tree = prequel_memes_tree(); + assert!(tree.search(&"hello there")); + assert!(tree.search(&"you are a bold one")); + assert!(tree.search(&"general kenobi")); + assert!(tree.search(&"you fool")); + assert!(tree.search(&"kill him")); + assert!( + !tree.search(&"but i was going to tosche station to pick up some power converters",) + ); + assert!(!tree.search(&"only a sith deals in absolutes")); + assert!(!tree.search(&"you underestimate my power")); + } + + #[test] + fn test_maximum_and_minimum() { + let tree = prequel_memes_tree(); + assert_eq!(*tree.maximum().unwrap(), "your move"); + assert_eq!( + *tree.minimum().unwrap(), + "back away...I will deal with this jedi slime myself" + ); + let mut tree2: BinarySearchTree = BinarySearchTree::new(); + assert!(tree2.maximum().is_none()); + assert!(tree2.minimum().is_none()); + tree2.insert(0); + assert_eq!(*tree2.minimum().unwrap(), 0); + assert_eq!(*tree2.maximum().unwrap(), 0); + tree2.insert(-5); + assert_eq!(*tree2.minimum().unwrap(), -5); + assert_eq!(*tree2.maximum().unwrap(), 0); + tree2.insert(5); + assert_eq!(*tree2.minimum().unwrap(), -5); + assert_eq!(*tree2.maximum().unwrap(), 5); + } + + #[test] + fn test_floor_and_ceil() { + let tree = prequel_memes_tree(); + assert_eq!(*tree.floor(&"hello there").unwrap(), "hello there"); + assert_eq!( + *tree + .floor(&"these are not the droids you're looking for") + .unwrap(), + "kill him" + ); + assert!(tree.floor(&"another death star").is_none()); + assert_eq!(*tree.floor(&"you fool").unwrap(), "you fool"); + assert_eq!( + *tree.floor(&"but i was going to tasche station").unwrap(), + "back away...I will deal with this jedi slime myself" + ); + assert_eq!( + *tree.floor(&"you underestimate my power").unwrap(), + "you fool" + ); + assert_eq!(*tree.floor(&"your new empire").unwrap(), "your move"); + assert_eq!(*tree.ceil(&"hello there").unwrap(), "hello there"); + assert_eq!( + *tree + .ceil(&"these are not the droids you're looking for") + .unwrap(), + "you are a bold one" + ); + assert_eq!( + *tree.ceil(&"another death star").unwrap(), + "back away...I will deal with this jedi slime myself" + ); + assert_eq!(*tree.ceil(&"you fool").unwrap(), "you fool"); + assert_eq!( + *tree.ceil(&"but i was going to tasche station").unwrap(), + "general kenobi" + ); + assert_eq!( + *tree.ceil(&"you underestimate my power").unwrap(), + "your move" + ); + assert!(tree.ceil(&"your new empire").is_none()); + } + + #[test] + fn test_iterator() { + let tree = prequel_memes_tree(); + let mut iter = tree.iter(); + assert_eq!( + iter.next().unwrap(), + &"back away...I will deal with this jedi slime myself" + ); + assert_eq!(iter.next().unwrap(), &"general kenobi"); + assert_eq!(iter.next().unwrap(), &"hello there"); + assert_eq!(iter.next().unwrap(), &"kill him"); + assert_eq!(iter.next().unwrap(), &"you are a bold one"); + assert_eq!(iter.next().unwrap(), &"you fool"); + assert_eq!(iter.next().unwrap(), &"your move"); + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } +} +``` \ No newline at end of file diff --git a/codes/src/data-structures/graph.md b/codes/src/data-structures/graph.md new file mode 100644 index 00000000..4c3a0321 --- /dev/null +++ b/codes/src/data-structures/graph.md @@ -0,0 +1,224 @@ +# 图(graph) + +```rust +use std::collections::{HashMap, HashSet}; +use std::fmt; + +#[derive(Debug, Clone)] +pub struct NodeNotInGraph; + +impl fmt::Display for NodeNotInGraph { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "accessing a node that is not in the graph") + } +} + +pub struct DirectedGraph { + adjacency_table: HashMap>, +} + +impl Graph for DirectedGraph { + fn new() -> DirectedGraph { + DirectedGraph { + adjacency_table: HashMap::new(), + } + } + fn adjacency_table_mutable(&mut self) -> &mut HashMap> { + &mut self.adjacency_table + } + fn adjacency_table(&self) -> &HashMap> { + &self.adjacency_table + } +} + +pub struct UndirectedGraph { + adjacency_table: HashMap>, +} + +impl Graph for UndirectedGraph { + fn new() -> UndirectedGraph { + UndirectedGraph { + adjacency_table: HashMap::new(), + } + } + fn adjacency_table_mutable(&mut self) -> &mut HashMap> { + &mut self.adjacency_table + } + fn adjacency_table(&self) -> &HashMap> { + &self.adjacency_table + } + fn add_edge(&mut self, edge: (&str, &str, i32)) { + self.add_node(edge.0); + self.add_node(edge.1); + + self.adjacency_table + .entry(edge.0.to_string()) + .and_modify(|e| { + e.push((edge.1.to_string(), edge.2)); + }); + self.adjacency_table + .entry(edge.1.to_string()) + .and_modify(|e| { + e.push((edge.0.to_string(), edge.2)); + }); + } +} + +pub trait Graph { + fn new() -> Self; + fn adjacency_table_mutable(&mut self) -> &mut HashMap>; + fn adjacency_table(&self) -> &HashMap>; + + fn add_node(&mut self, node: &str) -> bool { + match self.adjacency_table().get(node) { + None => { + self.adjacency_table_mutable() + .insert((*node).to_string(), Vec::new()); + true + } + _ => false, + } + } + + fn add_edge(&mut self, edge: (&str, &str, i32)) { + self.add_node(edge.0); + self.add_node(edge.1); + + self.adjacency_table_mutable() + .entry(edge.0.to_string()) + .and_modify(|e| { + e.push((edge.1.to_string(), edge.2)); + }); + } + + fn neighbours(&self, node: &str) -> Result<&Vec<(String, i32)>, NodeNotInGraph> { + match self.adjacency_table().get(node) { + None => Err(NodeNotInGraph), + Some(i) => Ok(i), + } + } + + fn contains(&self, node: &str) -> bool { + self.adjacency_table().get(node).is_some() + } + + fn nodes(&self) -> HashSet<&String> { + self.adjacency_table().keys().collect() + } + + fn edges(&self) -> Vec<(&String, &String, i32)> { + let mut edges = Vec::new(); + for (from_node, from_node_neighbours) in self.adjacency_table() { + for (to_node, weight) in from_node_neighbours { + edges.push((from_node, to_node, *weight)); + } + } + edges + } +} + +#[cfg(test)] +mod test_undirected_graph { + use super::Graph; + use super::UndirectedGraph; + #[test] + fn test_add_edge() { + let mut graph = UndirectedGraph::new(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); + + let expected_edges = [ + (&String::from("a"), &String::from("b"), 5), + (&String::from("b"), &String::from("a"), 5), + (&String::from("c"), &String::from("a"), 7), + (&String::from("a"), &String::from("c"), 7), + (&String::from("b"), &String::from("c"), 10), + (&String::from("c"), &String::from("b"), 10), + ]; + for edge in expected_edges.iter() { + assert_eq!(graph.edges().contains(edge), true); + } + } + + #[test] + fn test_neighbours() { + let mut graph = UndirectedGraph::new(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); + + assert_eq!( + graph.neighbours("a").unwrap(), + &vec![(String::from("b"), 5), (String::from("c"), 7)] + ); + } +} + +#[cfg(test)] +mod test_directed_graph { + use super::DirectedGraph; + use super::Graph; + + #[test] + fn test_add_node() { + let mut graph = DirectedGraph::new(); + graph.add_node("a"); + graph.add_node("b"); + graph.add_node("c"); + assert_eq!( + graph.nodes(), + [&String::from("a"), &String::from("b"), &String::from("c")] + .iter() + .cloned() + .collect() + ); + } + + #[test] + fn test_add_edge() { + let mut graph = DirectedGraph::new(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("c", "a", 7)); + graph.add_edge(("b", "c", 10)); + + let expected_edges = [ + (&String::from("a"), &String::from("b"), 5), + (&String::from("c"), &String::from("a"), 7), + (&String::from("b"), &String::from("c"), 10), + ]; + for edge in expected_edges.iter() { + assert_eq!(graph.edges().contains(edge), true); + } + } + + #[test] + fn test_neighbours() { + let mut graph = DirectedGraph::new(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); + + assert_eq!( + graph.neighbours("a").unwrap(), + &vec![(String::from("b"), 5)] + ); + } + + #[test] + fn test_contains() { + let mut graph = DirectedGraph::new(); + graph.add_node("a"); + graph.add_node("b"); + graph.add_node("c"); + assert_eq!(graph.contains("a"), true); + assert_eq!(graph.contains("b"), true); + assert_eq!(graph.contains("c"), true); + assert_eq!(graph.contains("d"), false); + } +} +``` diff --git a/codes/src/data-structures/heap.md b/codes/src/data-structures/heap.md new file mode 100644 index 00000000..7e61a887 --- /dev/null +++ b/codes/src/data-structures/heap.md @@ -0,0 +1,219 @@ +# 堆(Heap) + +```rust +// Heap data structure +// Takes a closure as a comparator to allow for min-heap, max-heap, and works with custom key functions + +use std::cmp::Ord; +use std::default::Default; + +pub struct Heap +where + T: Default, +{ + count: usize, + items: Vec, + comparator: fn(&T, &T) -> bool, +} + +impl Heap +where + T: Default, +{ + pub fn new(comparator: fn(&T, &T) -> bool) -> Self { + Self { + count: 0, + // Add a default in the first spot to offset indexes + // for the parent/child math to work out. + // Vecs have to have all the same type so using Default + // is a way to add an unused item. + items: vec![T::default()], + comparator, + } + } + + pub fn len(&self) -> usize { + self.count + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn add(&mut self, value: T) { + self.count += 1; + self.items.push(value); + + // Heapify Up + let mut idx = self.count; + while self.parent_idx(idx) > 0 { + let pdx = self.parent_idx(idx); + if (self.comparator)(&self.items[idx], &self.items[pdx]) { + self.items.swap(idx, pdx); + } + idx = pdx; + } + } + + fn parent_idx(&self, idx: usize) -> usize { + idx / 2 + } + + fn children_present(&self, idx: usize) -> bool { + self.left_child_idx(idx) <= self.count + } + + fn left_child_idx(&self, idx: usize) -> usize { + idx * 2 + } + + fn right_child_idx(&self, idx: usize) -> usize { + self.left_child_idx(idx) + 1 + } + + fn smallest_child_idx(&self, idx: usize) -> usize { + if self.right_child_idx(idx) > self.count { + self.left_child_idx(idx) + } else { + let ldx = self.left_child_idx(idx); + let rdx = self.right_child_idx(idx); + if (self.comparator)(&self.items[ldx], &self.items[rdx]) { + ldx + } else { + rdx + } + } + } +} + +impl Heap +where + T: Default + Ord, +{ + /// Create a new MinHeap + pub fn new_min() -> Self { + Self::new(|a, b| a < b) + } + + /// Create a new MaxHeap + pub fn new_max() -> Self { + Self::new(|a, b| a > b) + } +} + +impl Iterator for Heap +where + T: Default, +{ + type Item = T; + + fn next(&mut self) -> Option { + if self.count == 0 { + return None; + } + // This feels like a function built for heap impl :) + // Removes an item at an index and fills in with the last item + // of the Vec + let next = Some(self.items.swap_remove(1)); + self.count -= 1; + + if self.count > 0 { + // Heapify Down + let mut idx = 1; + while self.children_present(idx) { + let cdx = self.smallest_child_idx(idx); + if !(self.comparator)(&self.items[idx], &self.items[cdx]) { + self.items.swap(idx, cdx); + } + idx = cdx; + } + } + + next + } +} + +pub struct MinHeap; + +impl MinHeap { + #[allow(clippy::new_ret_no_self)] + pub fn new() -> Heap + where + T: Default + Ord, + { + Heap::new(|a, b| a < b) + } +} + +pub struct MaxHeap; + +impl MaxHeap { + #[allow(clippy::new_ret_no_self)] + pub fn new() -> Heap + where + T: Default + Ord, + { + Heap::new(|a, b| a > b) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_empty_heap() { + let mut heap = MaxHeap::new::(); + assert_eq!(heap.next(), None); + } + + #[test] + fn test_min_heap() { + let mut heap = MinHeap::new(); + heap.add(4); + heap.add(2); + heap.add(9); + heap.add(11); + assert_eq!(heap.len(), 4); + assert_eq!(heap.next(), Some(2)); + assert_eq!(heap.next(), Some(4)); + assert_eq!(heap.next(), Some(9)); + heap.add(1); + assert_eq!(heap.next(), Some(1)); + } + + #[test] + fn test_max_heap() { + let mut heap = MaxHeap::new(); + heap.add(4); + heap.add(2); + heap.add(9); + heap.add(11); + assert_eq!(heap.len(), 4); + assert_eq!(heap.next(), Some(11)); + assert_eq!(heap.next(), Some(9)); + assert_eq!(heap.next(), Some(4)); + heap.add(1); + assert_eq!(heap.next(), Some(2)); + } + + struct Point(/* x */ i32, /* y */ i32); + impl Default for Point { + fn default() -> Self { + Self(0, 0) + } + } + + #[test] + fn test_key_heap() { + let mut heap: Heap = Heap::new(|a, b| a.0 < b.0); + heap.add(Point(1, 5)); + heap.add(Point(3, 10)); + heap.add(Point(-2, 4)); + assert_eq!(heap.len(), 3); + assert_eq!(heap.next().unwrap().0, -2); + assert_eq!(heap.next().unwrap().0, 1); + heap.add(Point(50, 34)); + assert_eq!(heap.next().unwrap().0, 3); + } +} +``` \ No newline at end of file diff --git a/codes/src/data-structures/index.md b/codes/src/data-structures/index.md new file mode 100644 index 00000000..eefd5dae --- /dev/null +++ b/codes/src/data-structures/index.md @@ -0,0 +1,29 @@ +# 数据结构 + +数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关 + +在计算机科学的发展过程中,数据结构也随之发展。程序设计中常用的数据结构包括如下几个。 + +1. 数组(Array) +数组是一种聚合数据类型,它是将具有相同类型的若干变量有序地组织在一起的集合。数组可以说是最基本的数据结构,在各种编程语言中都有对应。一个数组可以分解为多个数组元素,按照数据元素的类型,数组可以分为整型数组、字符型数组、浮点型数组、指针数组和结构数组等。数组还可以有一维、二维以及多维等表现形式。 + +2. 栈( Stack) +栈是一种特殊的线性表,它只能在一个表的一个固定端进行数据结点的插入和删除操作。栈按照后进先出的原则来存储数据,也就是说,先插入的数据将被压入栈底,最后插入的数据在栈顶,读出数据时,从栈顶开始逐个读出。栈在汇编语言程序中,经常用于重要数据的现场保护。栈中没有数据时,称为空栈。 + +3. 队列(Queue) +队列和栈类似,也是一种特殊的线性表。和栈不同的是,队列只允许在表的一端进行插入操作,而在另一端进行删除操作。一般来说,进行插入操作的一端称为队尾,进行删除操作的一端称为队头。队列中没有元素时,称为空队列。 + +4. 链表( Linked List) +链表是一种数据元素按照链式存储结构进行存储的数据结构,这种存储结构具有在物理上存在非连续的特点。链表由一系列数据结点构成,每个数据结点包括数据域和指针域两部分。其中,指针域保存了数据结构中下一个元素存放的地址。链表结构中数据元素的逻辑顺序是通过链表中的指针链接次序来实现的。 + +5. 树( Tree) +树是典型的非线性结构,它是包括,2个结点的有穷集合K。在树结构中,有且仅有一个根结点,该结点没有前驱结点。在树结构中的其他结点都有且仅有一个前驱结点,而且可以有两个后继结点,m≥0。 + +6. 图(Graph) +图是另一种非线性数据结构。在图结构中,数据结点一般称为顶点,而边是顶点的有序偶对。如果两个顶点之间存在一条边,那么就表示这两个顶点具有相邻关系。 + +7.堆(Heap) +堆是一种特殊的树形数据结构,一般讨论的堆都是二叉堆。堆的特点是根结点的值是所有结点中最小的或者最大的,并且根结点的两个子树也是一个堆结构。 + +8.散列表(Hash) +散列表源自于散列函数(Hash function),其思想是如果在结构中存在关键字和T相等的记录,那么必定在F(T)的存储位置可以找到该记录,这样就可以不用进行比较操作而直接取得所查记录。 \ No newline at end of file diff --git a/codes/src/data-structures/linked-list.md b/codes/src/data-structures/linked-list.md new file mode 100644 index 00000000..a942c1a8 --- /dev/null +++ b/codes/src/data-structures/linked-list.md @@ -0,0 +1,159 @@ +# 链表 + +A linked list is also a `linear` data structure, and each element in the linked list is actually a separate object while all the objects are `linked together by the reference filed` in each element. In a `doubly linked list`, each node contains, besides the `next` node link, a second link field pointing to the `previous` node in the sequence. The two links may be called `next` and `prev`. And many modern operating systems use doubly linked lists to maintain references to active processes, threads and other dynamic objects. + +__Properties__ +* Indexing O(n) +* Insertion O(1) + * Beginning O(1) + * Middle (Indexing time+O(1)) + * End O(n) +* Deletion O(1) + * Beginning O(1) + * Middle (Indexing time+O(1)) + * End O(n) +* Search O(n) + +```rust +use std::fmt::{self, Display, Formatter}; +use std::ptr::NonNull; + +struct Node { + val: T, + next: Option>>, + prev: Option>>, +} + +impl Node { + fn new(t: T) -> Node { + Node { + val: t, + prev: None, + next: None, + } + } +} + +pub struct LinkedList { + length: u32, + start: Option>>, + end: Option>>, +} + +impl Default for LinkedList { + fn default() -> Self { + Self::new() + } +} + +impl LinkedList { + pub fn new() -> Self { + Self { + length: 0, + start: None, + end: None, + } + } + + pub fn add(&mut self, obj: T) { + let mut node = Box::new(Node::new(obj)); + // Since we are adding node at the end, next will always be None + node.next = None; + node.prev = self.end; + // Get a pointer to node + let node_ptr = Some(unsafe { NonNull::new_unchecked(Box::into_raw(node)) }); + match self.end { + // This is the case of empty list + None => self.start = node_ptr, + Some(end_ptr) => unsafe { (*end_ptr.as_ptr()).next = node_ptr }, + } + self.end = node_ptr; + self.length += 1; + } + + pub fn get(&mut self, index: i32) -> Option<&T> { + self.get_ith_node(self.start, index) + } + + fn get_ith_node(&mut self, node: Option>>, index: i32) -> Option<&T> { + match node { + None => None, + Some(next_ptr) => match index { + 0 => Some(unsafe { &(*next_ptr.as_ptr()).val }), + _ => self.get_ith_node(unsafe { (*next_ptr.as_ptr()).next }, index - 1), + }, + } + } +} + +impl Display for LinkedList +where + T: Display, +{ + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self.start { + Some(node) => write!(f, "{}", unsafe { node.as_ref() }), + None => Ok(()), + } + } +} + +impl Display for Node +where + T: Display, +{ + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self.next { + Some(node) => write!(f, "{}, {}", self.val, unsafe { node.as_ref() }), + None => write!(f, "{}", self.val), + } + } +} + +#[cfg(test)] +mod tests { + use super::LinkedList; + + #[test] + fn create_numeric_list() { + let mut list = LinkedList::::new(); + list.add(1); + list.add(2); + list.add(3); + println!("Linked List is {}", list); + assert_eq!(3, list.length); + } + + #[test] + fn create_string_list() { + let mut list_str = LinkedList::::new(); + list_str.add("A".to_string()); + list_str.add("B".to_string()); + list_str.add("C".to_string()); + println!("Linked List is {}", list_str); + assert_eq!(3, list_str.length); + } + + #[test] + fn get_by_index_in_numeric_list() { + let mut list = LinkedList::::new(); + list.add(1); + list.add(2); + println!("Linked List is {}", list); + let retrived_item = list.get(1); + assert!(retrived_item.is_some()); + assert_eq!(2 as i32, *retrived_item.unwrap()); + } + + #[test] + fn get_by_index_in_string_list() { + let mut list_str = LinkedList::::new(); + list_str.add("A".to_string()); + list_str.add("B".to_string()); + println!("Linked List is {}", list_str); + let retrived_item = list_str.get(1); + assert!(retrived_item.is_some()); + assert_eq!("B", *retrived_item.unwrap()); + } +} +``` \ No newline at end of file diff --git a/codes/src/data-structures/queue.md b/codes/src/data-structures/queue.md new file mode 100644 index 00000000..682cef93 --- /dev/null +++ b/codes/src/data-structures/queue.md @@ -0,0 +1,91 @@ +# 队列 + +```rust +#[derive(Debug)] +pub struct Queue { + elements: Vec, +} + +impl Queue { + pub fn new() -> Queue { + Queue { + elements: Vec::new(), + } + } + + pub fn enqueue(&mut self, value: T) { + self.elements.push(value) + } + + pub fn dequeue(&mut self) -> Result { + if !self.elements.is_empty() { + Ok(self.elements.remove(0usize)) + } else { + Err("Queue is empty") + } + } + + pub fn peek(&self) -> Result<&T, &str> { + match self.elements.first() { + Some(value) => Ok(value), + None => Err("Queue is empty"), + } + } + + pub fn size(&self) -> usize { + self.elements.len() + } + + pub fn is_empty(&self) -> bool { + self.elements.is_empty() + } +} + +impl Default for Queue { + fn default() -> Queue { + Queue { + elements: Vec::new(), + } + } +} + +#[cfg(test)] +mod tests { + use super::Queue; + + #[test] + fn test_enqueue() { + let mut queue: Queue = Queue::new(); + queue.enqueue(64); + assert_eq!(queue.is_empty(), false); + } + + #[test] + fn test_dequeue() { + let mut queue: Queue = Queue::new(); + queue.enqueue(32); + queue.enqueue(64); + let retrieved_dequeue = queue.dequeue(); + assert!(retrieved_dequeue.is_ok()); + assert_eq!(32, retrieved_dequeue.unwrap()); + } + + #[test] + fn test_peek() { + let mut queue: Queue = Queue::new(); + queue.enqueue(8); + queue.enqueue(16); + let retrieved_peek = queue.peek(); + assert!(retrieved_peek.is_ok()); + assert_eq!(8, *retrieved_peek.unwrap()); + } + + #[test] + fn test_size() { + let mut queue: Queue = Queue::new(); + queue.enqueue(8); + queue.enqueue(16); + assert_eq!(2, queue.size()); + } +} +``` \ No newline at end of file diff --git a/codes/src/data-structures/stack.md b/codes/src/data-structures/stack.md new file mode 100644 index 00000000..e54b7830 --- /dev/null +++ b/codes/src/data-structures/stack.md @@ -0,0 +1,265 @@ +# 栈 + +From Wikipedia, a stack is an abstract data type that serves as a collection of elements, with two main principal operations, `Push` and `Pop`. + +__Properties__ +* Push O(1) +* Pop head.data O(1) tail.data O(n) +* Peek O(1) + +``` +// the public struct can hide the implementation detail +pub struct Stack { + head: Link, +} + +type Link = Option>>; + +struct Node { + elem: T, + next: Link, +} + +impl Stack { + // Self is an alias for Stack + // We implement associated function name new for single-linked-list + pub fn new() -> Self { + // for new function we need to return a new instance + Self { + // we refer to variants of an enum using :: the namespacing operator + head: None, + } // we need to return the variant, so there without the ; + } + + // As we know the primary forms that self can take: self, &mut self and &self, push will change the linked list + // so we need &mut + // The push method which the signature's first parameter is self + pub fn push(&mut self, elem: T) { + let new_node = Box::new(Node { + elem, + next: self.head.take(), + }); + // don't forget replace the head with new node for stack + self.head = Some(new_node); + } + /// + /// In pop function, we trying to: + /// * check if the list is empty, so we use enum Option, it can either be Some(T) or None + /// * if it's empty, return None + /// * if it's not empty + /// * remove the head of the list + /// * remove its elem + /// * replace the list's head with its next + /// * return Some(elem), as the situation if need + /// + /// so, we need to remove the head, and return the value of the head + pub fn pop(&mut self) -> Result { + match self.head.take() { + None => Err("Stack is empty"), + Some(node) => { + self.head = node.next; + Ok(node.elem) + } + } + } + + pub fn is_empty(&self) -> bool { + // Returns true if the option is a [None] value. + self.head.is_none() + } + + pub fn peek(&self) -> Option<&T> { + // Converts from &Option to Option<&T>. + match self.head.as_ref() { + None => None, + Some(node) => Some(&node.elem), + } + } + + pub fn peek_mut(&mut self) -> Option<&mut T> { + match self.head.as_mut() { + None => None, + Some(node) => Some(&mut node.elem), + } + } + + pub fn into_iter_for_stack(self) -> IntoIter { + IntoIter(self) + } + pub fn iter(&self) -> Iter<'_, T> { + Iter { + next: self.head.as_deref(), + } + } + // '_ is the "explicitly elided lifetime" syntax of Rust + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { + next: self.head.as_deref_mut(), + } + } +} + +impl Default for Stack { + fn default() -> Self { + Self::new() + } +} + +/// The drop method of singly linked list. There's a question that do we need to worry about cleaning up our list? +/// As we all know the ownership and borrow mechanism, so we know the type will clean automatically after it goes out the scope, +/// this implement by the Rust compiler automatically did which mean add trait `drop` for the automatically. +/// +/// So, the complier will implements Drop for `List->Link->Box ->Node` automatically and tail recursive to clean the elements +/// one by one. And we know the recursive will stop at Box +/// https://rust-unofficial.github.io/too-many-lists/first-drop.html +/// +/// As we know we can't drop the contents of the Box after deallocating, so we need to manually write the iterative drop + +impl Drop for Stack { + fn drop(&mut self) { + let mut cur_link = self.head.take(); + while let Some(mut boxed_node) = cur_link { + cur_link = boxed_node.next.take(); + // boxed_node goes out of scope and gets dropped here; + // but its Node's `next` field has been set to None + // so no unbound recursion occurs. + } + } +} + +/// Rust has nothing like a yield statement, and there's actually 3 different kinds of iterator should to implement + +// Collections are iterated in Rust using the Iterator trait, we define a struct implement Iterator +pub struct IntoIter(Stack); + +impl Iterator for IntoIter { + // This is declaring that every implementation of iterator has an associated type called Item + type Item = T; + // the reason iterator yield Option is because the interface coalesces the `has_next` and `get_next` concepts + fn next(&mut self) -> Option { + self.0.pop().ok() + } +} + +pub struct Iter<'a, T> { + next: Option<&'a Node>, +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option { + self.next.map(|node| { + // as_deref: Converts from Option (or &Option) to Option<&T::Target>. + self.next = node.next.as_deref(); + &node.elem + }) + } +} + +pub struct IterMut<'a, T> { + next: Option<&'a mut Node>, +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + fn next(&mut self) -> Option { + // we add take() here due to &mut self isn't Copy(& and Option<&> is Copy) + self.next.take().map(|node| { + self.next = node.next.as_deref_mut(); + &mut node.elem + }) + } +} + +#[cfg(test)] +mod test_stack { + + use super::*; + + #[test] + fn basics() { + let mut list = Stack::new(); + assert_eq!(list.pop(), Err("Stack is empty")); + + list.push(1); + list.push(2); + list.push(3); + + assert_eq!(list.pop(), Ok(3)); + assert_eq!(list.pop(), Ok(2)); + + list.push(4); + list.push(5); + + assert_eq!(list.is_empty(), false); + + assert_eq!(list.pop(), Ok(5)); + assert_eq!(list.pop(), Ok(4)); + + assert_eq!(list.pop(), Ok(1)); + assert_eq!(list.pop(), Err("Stack is empty")); + + assert_eq!(list.is_empty(), true); + } + + #[test] + fn peek() { + let mut list = Stack::new(); + assert_eq!(list.peek(), None); + list.push(1); + list.push(2); + list.push(3); + + assert_eq!(list.peek(), Some(&3)); + assert_eq!(list.peek_mut(), Some(&mut 3)); + + match list.peek_mut() { + None => None, + Some(value) => Some(*value = 42), + }; + + assert_eq!(list.peek(), Some(&42)); + assert_eq!(list.pop(), Ok(42)); + } + + #[test] + fn into_iter() { + let mut list = Stack::new(); + list.push(1); + list.push(2); + list.push(3); + + let mut iter = list.into_iter_for_stack(); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), Some(2)); + assert_eq!(iter.next(), Some(1)); + assert_eq!(iter.next(), None); + } + + #[test] + fn iter() { + let mut list = Stack::new(); + list.push(1); + list.push(2); + list.push(3); + + let mut iter = list.iter(); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&1)); + } + + #[test] + fn iter_mut() { + let mut list = Stack::new(); + list.push(1); + list.push(2); + list.push(3); + + let mut iter = list.iter_mut(); + assert_eq!(iter.next(), Some(&mut 3)); + assert_eq!(iter.next(), Some(&mut 2)); + assert_eq!(iter.next(), Some(&mut 1)); + } +} +``` \ No newline at end of file diff --git a/codes/src/data-structures/trie.md b/codes/src/data-structures/trie.md new file mode 100644 index 00000000..65e02d9c --- /dev/null +++ b/codes/src/data-structures/trie.md @@ -0,0 +1,101 @@ +# trie树 + +```rust +use std::collections::HashMap; +use std::hash::Hash; + +#[derive(Debug, Default)] +struct Node { + children: HashMap>, + value: Option, +} + +#[derive(Debug, Default)] +pub struct Trie +where + Key: Default + Eq + Hash, + Type: Default, +{ + root: Node, +} + +impl Trie +where + Key: Default + Eq + Hash, + Type: Default, +{ + pub fn new() -> Self { + Self { + root: Node::default(), + } + } + + pub fn insert(&mut self, key: impl IntoIterator, value: Type) + where + Key: Eq + Hash, + { + let mut node = &mut self.root; + for c in key.into_iter() { + node = node.children.entry(c).or_insert_with(Node::default); + } + node.value = Some(value); + } + + pub fn get(&self, key: impl IntoIterator) -> Option<&Type> + where + Key: Eq + Hash, + { + let mut node = &self.root; + for c in key.into_iter() { + if node.children.contains_key(&c) { + node = node.children.get(&c).unwrap() + } else { + return None; + } + } + node.value.as_ref() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_insertion() { + let mut trie = Trie::new(); + assert_eq!(trie.get("".chars()), None); + + trie.insert("foo".chars(), 1); + trie.insert("foobar".chars(), 2); + + let mut trie = Trie::new(); + assert_eq!(trie.get(vec![1, 2, 3]), None); + + trie.insert(vec![1, 2, 3], 1); + trie.insert(vec![3, 4, 5], 2); + } + + #[test] + fn test_get() { + let mut trie = Trie::new(); + trie.insert("foo".chars(), 1); + trie.insert("foobar".chars(), 2); + trie.insert("bar".chars(), 3); + trie.insert("baz".chars(), 4); + + assert_eq!(trie.get("foo".chars()), Some(&1)); + assert_eq!(trie.get("food".chars()), None); + + let mut trie = Trie::new(); + trie.insert(vec![1, 2, 3, 4], 1); + trie.insert(vec![42], 2); + trie.insert(vec![42, 6, 1000], 3); + trie.insert(vec![1, 2, 4, 16, 32], 4); + + assert_eq!(trie.get(vec![42, 6, 1000]), Some(&3)); + assert_eq!(trie.get(vec![43, 44, 45]), None); + } +} +``` \ No newline at end of file diff --git a/codes/src/deep-trait/deref.md b/codes/src/deep-trait/deref.md new file mode 100644 index 00000000..92447466 --- /dev/null +++ b/codes/src/deep-trait/deref.md @@ -0,0 +1,61 @@ +# Deref + +下面的代码会报错 +```rust +use std::ops::Index; +use std::rc::Rc; + +struct MyType { bytes: T } + +// Wow such clean much wow +impl, I> Index for MyType { + type Output = T::Output; + + fn index(&self, index: I) -> &Self::Output { + &self.bytes[index] + } +} + +fn main() { + let rc1 = Rc::new([1, 2, 3]); + rc1[0]; // OK + + let rc2 = MyType { bytes: Rc::new([1, 2, 3]) }; + rc2[0]; // error[E0608]: cannot index into a value of type `MyType>` + // WHY???!?!!?!?!?!? +} +``` + +报错的原因是`Rc`自身没有实现`Index`,因此需要先通过`Deref`转为内部的数据类型,再进行索引。 + +首先`let rc1 = Rc::new([1, 2, 3]);`能行,是因为`Rc`能通过`Deref`转为`&[]`类型,因此`Deref`后,等同于直接对内部的数组切片取索引. + +而` let rc2 = MyType { bytes: Rc::new([1, 2, 3]) };`不行,是因为`Rc::new([1,2,3])`实际上是泛型类型`T`,编译器并不知道T有没有实现`Deref`,因此也不能对`Rc::new([1,2,3])`进行索引。 + +简而言之,只有作为表达式才能自动进行`Deref`(`rc1`),作为泛型类型时,不能自动进行`Deref`(`rc2`)。 + + +你也可以为自己的类型实现`Deref`: +```rust +use std::ops::Deref; +use std::rc::Rc; + +struct MyType { bytes: T } + +impl Deref for MyType { + type Target = T; + + fn deref(&self) -> &T { + &self.bytes + } +} + +fn main() { + let rc = Rc::new([1, 2, 3]); + rc[0]; // OK + + let rc = MyType { bytes: Rc::new([1, 2, 3]) }; + rc[0]; +} +``` +但是这不符合官方推荐的使用方式,因为官方明确推荐`Deref`适用于智能指针,其它类型要小心使用。 \ No newline at end of file diff --git a/codes/src/deep-trait/index.md b/codes/src/deep-trait/index.md new file mode 100644 index 00000000..a5b0b81e --- /dev/null +++ b/codes/src/deep-trait/index.md @@ -0,0 +1 @@ +# 深入特征 diff --git a/codes/src/fight-compiler/index.md b/codes/src/fight-compiler/index.md new file mode 100644 index 00000000..63bb3901 --- /dev/null +++ b/codes/src/fight-compiler/index.md @@ -0,0 +1,7 @@ +# 对抗编译检查 + +在Rust的使用过程中,无论是新手还是老手,都会经常遇到一些棘手的难以通过编译的代码,本章,我们就收集这些代码,目的有二: +- 帮助你提前认识这些常见的编译问题 +- 当遇到编译检查问题时,你可以来此寻找相似的案例,以快速解决问题 + +章节的组织方式是按照主要类型来的,大家可以根据目录名快速索引自己想要的内容。 \ No newline at end of file diff --git a/codes/src/fight-compiler/iterator.md b/codes/src/fight-compiler/iterator.md new file mode 100644 index 00000000..68618729 --- /dev/null +++ b/codes/src/fight-compiler/iterator.md @@ -0,0 +1,83 @@ +# 迭代器 + +## 错误一 +一般情况下,可以用过iter.map().rev()对一个迭代器进行连续变换,但是有些特殊场景,我们需要这样的形式: +```rust +let iter = array.to_iter(); +iter = iter.map(); +iter = iter.rev(); +``` + +是的,这种用法很不常见,但是一旦遇到,那么编译检查将是你难以通过的天堑,例如以下代码: +```rust +fn main() { + let arr = [0u32; 100]; // big array or vector with random numbers + let mut iter= arr.iter(); + let map_func: Option &u32> = None; + let reverse = true; + + if let Some(closure) = map_func { + iter = iter.map(closure); + } + + if reverse { + iter = iter.rev(); + } + + // many more modification... + + let list: Vec<&u32> = iter.collect(); + println!("{:?}", list); +} +``` + +运行后,产生报错: +```console +error[E0308]: mismatched types + --> src/main.rs:8:14 + | +8 | iter = iter.map(closure); + | ^^^^^^^^^^^^^^^^^ expected struct `std::slice::Iter`, found struct `Map` + | + = note: expected struct `std::slice::Iter<'_, _>` + found struct `Map, for<'r> fn(&'r u32) -> &'r u32>` + +error[E0308]: mismatched types + --> src/main.rs:12:14 + | +12 | iter = iter.rev(); + | ^^^^^^^^^^ expected struct `std::slice::Iter`, found struct `Rev` + | + = note: expected struct `std::slice::Iter<'_, _>` + found struct `Rev>` +``` + +原因很简单,`let mut iter= arr.iter()`这里生成的`iter`是`std::slice::Iter`类型,但是`iter.map()`生成的是`Map>`类型,因此无法进行直接赋值,`iter.rev()`也是类似情况。 + +那么为什么我们可以这样使用: `iter.map().rev()`?,因为`Map>`实现了`Iterator`特征,因此可以直接在其上调用`rev`这个属于`Iterator`的方法。 + +回到上面的问题,因为`std::slice::Iter` 和 `Map>`都实现了`impl Iterator`的特征,因此我们可以考虑用一个`Box>`的类型来对iter进行包裹: + +```rust +fn main() { + let arr = [0u32; 100]; // big array or vector with random numbers + let mut iter: Box> = Box::new(arr.iter()); + let map_func: Option &u32> = None; + let reverse = true; + + if let Some(closure) = map_func { + iter = Box::new(iter.map(closure)); + } + + if reverse { + iter = Box::new(iter.rev()); + } + + // many more modification... + + let list: Vec<&u32> = iter.collect(); + println!("{:?}", list); +} +``` + +Bingo,代码编译通过! \ No newline at end of file diff --git a/codes/src/fun-but-useless/for-cant-while-can.md b/codes/src/fun-but-useless/for-cant-while-can.md new file mode 100644 index 00000000..e69de29b diff --git a/codes/src/fun-but-useless/index.md b/codes/src/fun-but-useless/index.md new file mode 100644 index 00000000..29f15dcc --- /dev/null +++ b/codes/src/fun-but-useless/index.md @@ -0,0 +1,3 @@ +# 有趣但是未必有啥用的代码 + +本章会呈现一些很有趣,但是其实没啥卵用的代码,或者也有一些卵用?那就要读者你自己来评断了。 diff --git a/codes/src/fun-but-useless/question-mark-nested.md b/codes/src/fun-but-useless/question-mark-nested.md new file mode 100644 index 00000000..d7a3d967 --- /dev/null +++ b/codes/src/fun-but-useless/question-mark-nested.md @@ -0,0 +1,40 @@ +# ?嵌套 + +```rust +pub fn parse_items(input: I) -> impl Iterator>> +where + I: BufRead, + O: FromStr, +{ + input.lines().filter_map(|line| { + let line = match line { + Ok(line) => line, + Err(e) => return Some(Err(e)), + }; + let line = line.trim(); + if line.is_empty() { + None + } else { + Some(Ok(line.parse())) + } + }) +} +``` + +然后下面的用法是完全合法的,注意`??`的使用: + +```rust +let numbers: io::Result, Error>> = aoc::parse_items(input).collect(); +let numbers = numbers??; +``` + +考虑到`FromIterator`的实现,好像这样使用也是有道理的: + +```rust +impl FromIterator> for Result where + V: FromIterator, +``` + +这是因为`Result>`实现了`FromIterator>`,所以`io::Result>>`就实现了`FromIterator>` + +虽然上面的代码很酷,但是仔细想起来还是怪怪的,是不?最主要是难以理解,所以不建议大家使用。 \ No newline at end of file diff --git a/codes/src/iterator/from-start-and-end.md b/codes/src/iterator/from-start-and-end.md new file mode 100644 index 00000000..e3c2e815 --- /dev/null +++ b/codes/src/iterator/from-start-and-end.md @@ -0,0 +1,34 @@ +# 同时从首尾遍历 + +下面的代码展示了如何同时从首尾两端同时开始,对数组进行迭代. + +```rust +fn main() { + let v = vec![1, 2, 3, 4, 5]; + + // 第一种方法 + // 将数组分成两段 + let (f, b) = v.split_at(v.len() / 2); + // 使用zip将两段数组合并成[(x1,y1),(x2,y2)..]形式的迭代器 + for (x, y) in f.iter().zip(b.iter().rev()) { + println!("{}, {}", x, y) + } + + // 第二种方法 + // 使用循环匹配的方式,不停的匹配出首尾元素,非常巧妙! + let mut s = &v[..]; + loop { + match s { + [a, rest @ .., b] => { + println!("{}, {}", a, b); + s = rest; + } + [a] => { + println!("{}", a); + break; + } + [] => break, + } + } +} +``` \ No newline at end of file diff --git a/codes/src/iterator/index.md b/codes/src/iterator/index.md new file mode 100644 index 00000000..4f37fde8 --- /dev/null +++ b/codes/src/iterator/index.md @@ -0,0 +1,4 @@ +# 迭代器Iterator + +Iterator是Rust中非常重要的类型,详情见[Rust语言圣经-迭代器](https://course.rs/advance/iterator.html). + diff --git a/codes/src/make-things-easier/1.md b/codes/src/make-things-easier/1.md new file mode 100644 index 00000000..4808eebd --- /dev/null +++ b/codes/src/make-things-easier/1.md @@ -0,0 +1,45 @@ +# 代码1 + +```rust +trait OptExt { + fn some_if(self, f: F) -> Option + where + F: Fn(&Self) -> bool, + Self: Sized, + { + if f(&self) { + Some(self) + } else { + None + } + } +} + +impl OptExt for T where T: Sized {} + +fn main() { + // this vector should be at least 3 elements + let v = vec![1, 2, 3]; + let maybe_v = v.some_if(|v| v.len() >= 3); + println!("{:?}",maybe_v); +} +``` + +这段代码初看还挺复杂的,实际上实现的目标很简单:对于任意给定的Sized类型A,调用F函数对其进行运算,若运算结果为true,则返回被Some(A),否则返回None。 + +下面我们给出简单的实现: + +#### 简单1号 +```rust +fn main() { + let v = vec![1, 2, 3]; + let maybe_v = (v.len() >= 3).then(|| v); + dbg!(maybe_v); +} +``` + +#### 简单2号 +```rust +let v = vec![1, 2, 3]; +let maybe_v = Some(v).filter(|v| v.len() >= 3); +``` \ No newline at end of file diff --git a/codes/src/make-things-easier/index.md b/codes/src/make-things-easier/index.md new file mode 100644 index 00000000..a62574e8 --- /dev/null +++ b/codes/src/make-things-easier/index.md @@ -0,0 +1,5 @@ +# 将复杂的实现简化 + +得益于Rust强大的语法和极度的灵活性,使条条大路通罗马成为可能性。但同时,这也是一把双刃剑,在某些时候,本可以简单实现的代码,却最终呈现的过于复杂。 + +本章的目标就是,让复杂的代码简化。 \ No newline at end of file diff --git a/codes/src/obscure/1.md b/codes/src/obscure/1.md new file mode 100644 index 00000000..8c2b245f --- /dev/null +++ b/codes/src/obscure/1.md @@ -0,0 +1,77 @@ +# 代码1 +下面代码不会编译: +```rust +fn main() { + let p: &mut dyn CanEat = &mut Person as &mut dyn CanEat; + let x: &mut [&mut dyn CanEat] = &mut [p]; + + eat_twice(x); +} + + +fn eat_twice<'a, I: ?Sized>(persons: &'a mut I) + where + &'a mut I: IntoIterator, +{ + for person in persons.into_iter() { + person.eat(); + } + + for person in persons.into_iter() { + person.eat(); + } +} + +struct Person; + +impl CanEat for Person { + fn eat(&mut self) { + println!("eat"); + } +} + +trait CanEat { + fn eat(&mut self); +} + +``` + +改成以下就可以: +```rust +fn main() { + let p: &mut dyn CanEat = &mut Person as &mut dyn CanEat; + let x: &mut [&mut dyn CanEat] = &mut [p]; + + eat_twice(x); +} + + +fn eat_twice<'slice, 'element, S: ?Sized>(persons: &'slice mut S) +where + for<'a> &'a mut S: IntoIterator, +{ + for person in persons.into_iter() { + person.eat(); + } + + for person in persons.into_iter() { + person.eat(); + } +} + +struct Person; + +impl CanEat for Person { + fn eat(&mut self) { + println!("eat"); + } +} + +trait CanEat { + fn eat(&mut self); +} + +``` + + +原因:https://www.reddit.com/r/rust/comments/rq43c6/generic_fn_impl_for_iterating_over_mut_items_twice/ \ No newline at end of file diff --git a/codes/src/obscure/index.md b/codes/src/obscure/index.md new file mode 100644 index 00000000..8412389b --- /dev/null +++ b/codes/src/obscure/index.md @@ -0,0 +1 @@ +# 复杂难懂的代码 diff --git a/codes/src/operator-override/index.md b/codes/src/operator-override/index.md new file mode 100644 index 00000000..db5f9e8d --- /dev/null +++ b/codes/src/operator-override/index.md @@ -0,0 +1,25 @@ +# 运算符重载 + +```rust +use std::ops::Index; + +struct MyType { + bytes: T +} + +impl> Index for MyType { + type Output = T::Output; + fn index(&self, index: I) -> &Self::Output { + &self.bytes[index] + } +} + + +fn main() { + let arr = MyType{ + bytes: [1,2,3] + }; + + println!("{}",arr[0]); +} +``` \ No newline at end of file diff --git a/codes/src/pattern-match/index.md b/codes/src/pattern-match/index.md new file mode 100644 index 00000000..5b84c7b9 --- /dev/null +++ b/codes/src/pattern-match/index.md @@ -0,0 +1,3 @@ +# 模式匹配 + +一些你会常遇到,但是未必好解决的模式匹配代码. \ No newline at end of file diff --git a/codes/src/pattern-match/return-with-match.md b/codes/src/pattern-match/return-with-match.md new file mode 100644 index 00000000..dae9e06f --- /dev/null +++ b/codes/src/pattern-match/return-with-match.md @@ -0,0 +1,32 @@ +# 使用match和if let返回值 + +## 1 +以下代码使用`if let`给外面的变量赋值(在循环中): +```rust +let file_stem; +let stem = source_path.file_stem(); +if let Some(x) = stem { + file_stem = x; +} else { + continue +} +``` + +实际上不必这么复杂,可以直接用`if let`作为一个表达式,直接进行赋值: +```rust +let file_stem = if let Some(x) = source_path.file_stem(){ + x +} else { + continue +} +``` + +也可以使用`match`: +```rust +let file_stem = match source_path.file_stem() { + Some(x) => x, + None => continue, +}; +``` + +从中可以看出, **在匹配中假如一个分支没有任何返回值,可以通过另外一个分支进行赋值**。 \ No newline at end of file diff --git a/codes/src/pitfalls/arithmetic-overflow.md b/codes/src/pitfalls/arithmetic-overflow.md new file mode 100644 index 00000000..0b4290d4 --- /dev/null +++ b/codes/src/pitfalls/arithmetic-overflow.md @@ -0,0 +1,62 @@ +# 算术溢出导致的panic + +在Rust中,溢出后的数值被截断是很正常的: +```rust +let x: u16 = 65535; +let v = x as u8; +println!("{}", v) +``` + +最终程序会输出`255`, 因此大家可能会下意识地就觉得算数操作在Rust中只会导致结果的不正确,并不会导致异常。但是实际上,如果是因为算术操作符导致的溢出,就会让整个程序panic: +```rust +fn main() { + let x: u8 = 10; + + let v = x + u8::MAX; + println!("{}", v) +} +``` + +输出结果如下: +```console +thread 'main' panicked at 'attempt to add with overflow', src/main.rs:5:13 +``` + +那么当我们确实有这种需求时,该如何做呢?可以使用Rust提供的`checked_xxx`系列方法: +```rust +fn main() { + let x: u8 = 10; + + let v = x.checked_add(u8::MAX).unwrap_or(0); + println!("{}", v) +} +``` + +也许你会觉得本章内容其实算不上什么陷阱,但是在实际项目快速迭代中,越是不起眼的地方越是容易出错: +```rust +fn main() { + let v = production_rate_per_hour(5); + println!("{}", v); +} + +pub fn production_rate_per_hour(speed: u8) -> f64 { + let cph: u8 = 221; + match speed { + 1..=4 => (speed * cph) as f64, + 5..=8 => (speed * cph) as f64 * 0.9, + 9..=10 => (speed * cph) as f64 * 0.77, + _ => 0 as f64, + } +} + +pub fn working_items_per_minute(speed: u8) -> u32 { + (production_rate_per_hour(speed) / 60 as f64) as u32 +} +``` + +上述代码中,`speed * cph`就会直接panic: +```console +thread 'main' panicked at 'attempt to multiply with overflow', src/main.rs:10:18 +``` + +是不是还藏的挺隐蔽的?因此大家在Rust中做数学运算时,要多留一个心眼,免得上了生产才发现问题所在。或者,你也可以做好单元测试:) \ No newline at end of file diff --git a/codes/src/pitfalls/closure-with-lifetime.md b/codes/src/pitfalls/closure-with-lifetime.md new file mode 100644 index 00000000..ab01758f --- /dev/null +++ b/codes/src/pitfalls/closure-with-lifetime.md @@ -0,0 +1,108 @@ +# 闭包上奇怪的生命周期 + +Rust一道独特的靓丽风景就是生命周期,也是反复折磨新手的最大黑手,就连老手,可能一不注意就会遇到一些生命周期上的陷阱,例如闭包上使用引用。 + +## 一段简单的代码 +先来看一段简单的代码: +```rust +fn fn_elision(x: &i32) -> &i32 { x } +let closure_slision = |x: &i32| -> &i32 { x }; +``` + +乍一看,这段代码比古天乐还平平无奇,能有什么问题呢?来,走两圈试试: +```console +error: lifetime may not live long enough + --> src/main.rs:39:39 + | +39 | let closure = |x: &i32| -> &i32 { x }; // fails + | - - ^ returning this value requires that `'1` must outlive `'2` + | | | + | | let's call the lifetime of this reference `'2` + | let's call the lifetime of this reference `'1` +``` + +咦?竟然报错了,明明两个一模一样功能的函数,一个正常编译,一个却报错,错误原因是编译器无法推测返回的引用和传入的引用谁活得更久! + +真的是非常奇怪的错误,学过[Rust生命周期](https://github.com/sunface/rust-course/blob/main/src/advance/lifetime/basic.md)的读者应该都记得这样一条生命周期消除规则: **如果函数参数中只有一个引用类型,那该引用的生命周期会被自动分配给所有的返回引用**。我们当前的情况完美符合,`function`函数的顺利编译通过,就充分说明了问题。 + +那为何闭包就出问题了? + +## 一段复杂的代码 +为了验证闭包无法应用生命周期消除规则,再来看一个复杂一些的例子: +```rust +use std::marker::PhantomData; + +trait Parser<'a>: Sized + Copy { + fn parse(&self, tail: &'a str) -> &'a str { + tail + } + fn wrap(self) -> Wrapper<'a, Self> { + Wrapper { + parser: self, + marker: PhantomData, + } + } +} + +#[derive(Copy, Clone)] +struct T<'x> { + int: &'x i32, +} + +impl<'a, 'x> Parser<'a> for T<'x> {} + +struct Wrapper<'a, P> +where + P: Parser<'a>, +{ + parser: P, + marker: PhantomData<&'a ()>, +} + +fn main() { + // Error. + let closure_wrap = |parser: T| parser.wrap(); + + // No error. + fn parser_wrap(parser: T<'_>) -> Wrapper<'_, T<'_>> { + parser.wrap() + } +} +``` + +该例子之所以这么复杂,纯粹是为了证明闭包上生命周期会失效,读者大大轻拍:) 编译后,不出所料的报错了: +```console +error: lifetime may not live long enough + --> src/main.rs:32:36 + | +32 | let closure_wrap = |parser: T| parser.wrap(); + | ------ - ^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2` + | | | + | | return type of closure is Wrapper<'_, T<'2>> + | has type `T<'1>` +``` + +## 深入调查 +一模一样的报错,说明在这种情况下,生命周期的消除规则也没有生效,看来事情确实不简单,我眉头一皱,决定深入调查,最后还真翻到了一些讨论,经过整理后,大概分享给大家。 + +首先给出一个结论:**这个问题,可能很难被解决,建议大家遇到后,还是老老实实用正常的函数,不要秀闭包了**。 + +对于函数的生命周期而言,它的消除规则之所以能生效是因为它的生命周期完全体现在签名的引用类型上,在函数体中无需任何体现: +```rust +fn fn_elision(x: &i32) -> &i32 {..} +``` +因此编译器可以做各种编译优化,也很容易根据参数和返回值进行生命周期的分析,最终得出消除规则。 + +可是闭包,并没有函数那么简单,它的生命周期分散在参数和闭包函数体中(主要是它没有确切的返回值签名): +```rust +let closure_slision = |x: &i32| -> &i32 { x }; +``` + +编译器就必须深入到闭包函数体中,去分析和推测生命周期,复杂度因此极具提升:试想一下,编译器该如何从复杂的上下文中分析出参数引用的生命周期和闭包体中生命周期的关系? + +由于上述原因(当然,实际情况复杂的多),Rust语言开发者其实目前是有意为之,针对函数和闭包实现了两种不同的生命周期消除规则。 + +## 总结 +虽然我言之凿凿,闭包的生命周期无法解决,但是未来谁又知道呢。最大的可能性就是之前开头那种简单的场景,可以被自动识别和消除。 + +总之,如果有这种需求,还是像古天乐一样做一个平平无奇的男人,老老实实使用函数吧. \ No newline at end of file diff --git a/codes/src/pitfalls/index.md b/codes/src/pitfalls/index.md new file mode 100644 index 00000000..74169d41 --- /dev/null +++ b/codes/src/pitfalls/index.md @@ -0,0 +1,3 @@ +# Rust陷阱系列 + +本章收录一些Rust常见的陷阱,一不小心就会坑你的那种(当然,这不是Rust语言的问题,而是一些边边角角知识点)。 \ No newline at end of file diff --git a/codes/src/pitfalls/multiple-mutable-references.md b/codes/src/pitfalls/multiple-mutable-references.md new file mode 100644 index 00000000..e4609b7e --- /dev/null +++ b/codes/src/pitfalls/multiple-mutable-references.md @@ -0,0 +1,6 @@ +# 多可变引用 + + +https://oribenshir.github.io/afternoon_rusting//blog/mutable-reference + +https://oribenshir.github.io/afternoon_rusting//blog/mutable-reference \ No newline at end of file diff --git a/codes/src/pitfalls/stack-overflow.md b/codes/src/pitfalls/stack-overflow.md new file mode 100644 index 00000000..3347b97d --- /dev/null +++ b/codes/src/pitfalls/stack-overflow.md @@ -0,0 +1,51 @@ +# 线程类型导致的栈溢出 + +在Rust中,我们不太容易遇到栈溢出,因为默认栈还挺大的,而且大的数据往往存在堆上(动态增长),但是一旦遇到该如何处理?先来看段代码: +```rust +#![feature(test)] +extern crate test; + +#[cfg(test)] +mod tests { + use test::Bencher; + + #[bench] + fn it_works(b: &mut Bencher) { + b.iter(|| { let stack = [[[0.0; 2]; 512]; 512]; }); + } +} +``` + +以上代码是一个测试模块,它在堆上生成了一个数组`stack`,初步看起来数组挺大的,先尝试运行下`cargo test`: +> 你很可能会遇到`#![feature(test)]`错误,因为该特性目前只存在`Rust Nightly`版本上,具体解决方法见[Rust语言圣经](https://course.rs/appendix/rust-version.html#在指定目录使用rust-nightly) + +```console +running 1 test + +thread 'tests::it_works' has overflowed its stack +fatal runtime error: stack overflow +``` + +Bang,很不幸,遇到了百年一遇的栈溢出错误,再来试试`cargo bench`,竟然通过了测试,这是什么原因?为何`cargo test`和`cargo bench`拥有完全不同的行为?这就要从Rust的栈原理讲起。 + +首先看看`stack`数组,它的大小是`8 × 2 × 512 × 512 = 4 MiB`,嗯,很大,崩溃也正常(读者说,正常,只是作者你不太正常。。). + +其次,`cargo test`和`cargo bench`,前者运行在一个新创建的线程上,而后者运行在**main线程上**. + +最后,`main`线程由于是老大,所以资源比较多,拥有令其它兄弟艳羡不已的`8MB`栈大小,而其它新线程只有区区`2MB`栈大小(取决于操作系统,`linux`是`2MB`,其它的可能更小),再对比我们的`stack`大小,不崩溃就奇怪了。 + +因此,你现在明白,为何`cargo test`不能运行,而`cargo bench`却可以欢快运行。 + +如果实在想要增大栈的默认大小,以通过该测试,你可以这样运行:`RUST_MIN_STACK=8388608 cargo test`,结果如下: +```console +running 1 test +test tests::it_works ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s +``` +Bingo, 成功了,最后再补充点测试的背景知识: + +> cargo test为何使用新线程?因为它需要并行的运行测试用例,与之相反,cargo bench只需要顺序的执行,因此main线程足矣 + + + diff --git a/codes/src/pitfalls/the-disabled-mutability.md b/codes/src/pitfalls/the-disabled-mutability.md new file mode 100644 index 00000000..73b436bf --- /dev/null +++ b/codes/src/pitfalls/the-disabled-mutability.md @@ -0,0 +1,128 @@ +# 失效的可变性 +众所周知Rust是一门安全性非常强的系统级语言,其中,显式的设置变量可变性,是安全性的重要组成部分。按理来说,变量可变不可变在设置时就已经决定了,但是你遇到过可变变量在某些情况失效,变成不可变吗? + +先来看段正确的代码: +```rust +#[derive(Debug)] +struct A { + f1: u32, + f2: u32, + f3: u32 +} + +#[derive(Debug)] +struct B<'a> { + f1: u32, + a: &'a mut A, +} + +fn main() { + let mut a: A = A{ f1: 0, f2: 1, f3: 2 }; + // b不可变 + let b: B = B{ f1: 3, a: &mut a }; + // 但是b中的字段a可以变 + b.a.f1 += 1; + + println!("b is {:?} ", &b); +} +``` + +在这里,虽然变量`b`被设置为不可变,但是`b`的其中一个字段`a`被设置为可变的结构体,因此我们可以通过`b.a.f1 += 1`来修改`a`的值。 + +也许有人还不知道这种部分可变性的存在,不过没关系,因为马上就不可变了:) + +- 结构体可变时,里面的字段都是可变的,例如`&mut a` +- 结构体不可变时,里面的某个字段可以单独设置为可变,例如`b.a` + +在理解了上面两条简单规则后,来看看下面这段代码: +```rust +#[derive(Debug)] +struct A { + f1: u32, + f2: u32, + f3: u32 +} + +#[derive(Debug)] +struct B<'a> { + f1: u32, + a: &'a mut A, +} + + +impl B<'_> { + // this will not work + pub fn changeme(&self) { + self.a.f1 += 1; + } +} + +fn main() { + let mut a: A = A{ f1: 0, f2: 1, f3: 2 }; + // b is immutable + let b: B = B{ f1: 3, a: &mut a }; + b.changeme(); + + println!("b is {:?} ", &b); +} +``` + +这段代码,仅仅做了一个小改变,不再直接修改`b.a`,而是通过调用`b`上的方法去修改其中的`a`,按理说不会有任何区别。因此我预言:通过方法调用跟直接调用不应该有任何区别,运行验证下: +```console +error[E0594]: cannot assign to `self.a.f1`, which is behind a `&` reference + --> src/main.rs:18:9 + | +17 | pub fn changeme(&self) { + | ----- help: consider changing this to be a mutable reference: `&mut self` +18 | self.a.f1 += 1; + | ^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written +``` + +啪,又被打脸了。我说我是大意了,没有闪,大家信不?反正马先生应该是信的:D + +## 简单分析 +观察第一个例子,我们调用的`b.a`实际上是用`b`的值直接调用的,在这种情况下,由于所有权规则,编译器可以认定,只有一个可变引用指向了`a`,因此这种使用是非常安全的。 + +但是,在第二个例子中,`b`被藏在了`&`后面,根据所有权规则,同时可能存在多个`b`的借用,那么就意味着可能会存在多个可变引用指向`a`,因此编译器就拒绝了这段代码。 + +事实上如果你将第一段代码的调用改成: +```rust +let b: &B = &B{ f1: 3, a: &mut a }; +b.a.f1 += 1; +``` + +一样会报错! + +## 一个练习 +结束之前再来一个练习,稍微有点绕,大家品味品味: +```rust +#[derive(Debug)] +struct A { + f1: u32, + f2: u32, + f3: u32 +} + +#[derive(Debug)] +struct B<'a> { + f1: u32, + a: &'a mut A, +} + +fn main() { + let mut a: A = A{ f1: 0, f2: 1, f3: 2 }; + let b: B = B{ f1: 3, a: &mut a }; + b.a.f1 += 1; + a.f1 = 10; + + println!("b is {:?} ", &b); +} +``` + +小提示:这里`b.a.f1 += 1`和`a.f1 = 10`只能有一个存在,否则就会报错。 + +## 总结 + +根据之前的观察和上面的小提示,可以得出一个结论:**可变性的真正含义是你对目标对象的独占修改权**。在实际项目中,偶尔会遇到比上述代码更复杂的可变性情况,记住这个结论,有助于我们拨云见日,直达本质。 + +学习,就是不断接近和认识事物本质的过程,对于Rust语言的学习亦是如此。 \ No newline at end of file diff --git a/codes/src/pitfalls/use-vec-in-for.md b/codes/src/pitfalls/use-vec-in-for.md new file mode 100644 index 00000000..a2681e60 --- /dev/null +++ b/codes/src/pitfalls/use-vec-in-for.md @@ -0,0 +1,40 @@ +# for循环中使用外部数组 + +一般来说,`for`循环能做到的,`while`也可以,反之亦然,但是有一种情况,还真不行,先来看代码: +```rust +let mut v = vec![1,2,3]; + +for i in 0..v.len() { + v.push(i); + println!("{:?}",v); +} +``` + +我们的目的是创建一个无限增长的数组,往里面插入`0..`(看不懂该表达式的同学请查阅https://course.rs)的数值序列。 + +看起来上面代码可以完成,因为随着数组不停增长,`v.len()`也会不停变大,但是事实上真的如此吗? + +```console +[1, 2, 3, 0] +[1, 2, 3, 0, 1] +[1, 2, 3, 0, 1, 2] +``` + +输出很清晰的表明,只新插入了三个元素:`0..=2`,刚好是`v`的初始长度。 + +这是因为:**在for循环中,`v.len`只会在循环伊始之时进行求值,之后就一直使用该值**。 + +行,问题算是清楚了,那该如何解决呢,我们可以使用`while`循环,该循环与`for`相反,每次都会重新求值: +```rust +let mut v = vec![1,2,3]; + +let mut i = 0; +while i < v.len() { + v.push(i); + i+=1; + println!("{:?}",v); +} +``` + +友情提示,在你运行上述代码时,千万要及时停止,否则会`Boom` - 炸翻控制台。 + diff --git a/codes/src/principles-behind-weird-code/autoref.md b/codes/src/principles-behind-weird-code/autoref.md new file mode 100644 index 00000000..3c990882 --- /dev/null +++ b/codes/src/principles-behind-weird-code/autoref.md @@ -0,0 +1,107 @@ +# autoref specialization +https://www.reddit.com/r/rust/comments/rnn32g/rust_already_has_specialization/ + +## 例子一 +```rust +use std::fmt::Debug; +use std::fmt::Display; +struct Wrap(T); + +trait ViaString { + fn foo(&self); +} +impl ViaString for &&Wrap { + fn foo(&self) { + println!("String: {}", self.0); + } +} + +trait ViaDisplay { + fn foo(&self); +} +impl ViaDisplay for &Wrap { + fn foo(&self) { + println!("Display: {}", self.0); + } +} + +trait ViaDebug { + fn foo(&self); +} +impl ViaDebug for Wrap { + fn foo(&self) { + println!("Debug: {:?}", self.0); + } +} + +fn main() { + (&&&Wrap(String::from("hi"))).foo(); + (&&&Wrap(3)).foo(); + (&&&Wrap(['a', 'b'])).foo(); +} +``` + +输出: +```console +String: hi +Display: 3 +Debug: ['a', 'b'] +``` + + + +## 例子二 +```rust +use std::fmt::*; + +trait Specialized { + fn call(&self); +} + +impl Specialized for &T { + fn call(&self) { + println!("nothing to print"); + } +} + +impl Specialized for &mut &T { + fn call(&self) { + println!("debug print: {:?}", self); + } +} + +impl Specialized for &mut &mut &T { + fn call(&self) { + println!("display print: {}", self); + } +} + +struct DisplayStruct; +impl Display for DisplayStruct { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "DisplayStruct") + } +} + +struct DebugStruct; +impl Debug for DebugStruct { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "DebugStruct") + } +} + +struct NothingStruct; + +fn main() { + (&mut &mut &NothingStruct).call(); + (&mut &mut &DebugStruct).call(); + (&mut &mut &DisplayStruct).call(); +} +``` + +输出: +```console +nothing to print +debug print: DebugStruct +display print: DisplayStruct +``` \ No newline at end of file diff --git a/codes/src/principles-behind-weird-code/index.md b/codes/src/principles-behind-weird-code/index.md new file mode 100644 index 00000000..d6355ff9 --- /dev/null +++ b/codes/src/principles-behind-weird-code/index.md @@ -0,0 +1 @@ +# 奇怪代码背后的原理 diff --git a/codes/src/principles-behind-weird-code/ref-to-rvalue.md b/codes/src/principles-behind-weird-code/ref-to-rvalue.md new file mode 100644 index 00000000..f4147f4c --- /dev/null +++ b/codes/src/principles-behind-weird-code/ref-to-rvalue.md @@ -0,0 +1,35 @@ +## 右值取地址 + +先看一段极其简单的代码: +```rust +fn main() { + println!("{:p}", &34); +} +``` + +这段代码中直接对右值34取了地址,然后打印出该地址,初看没什么,但是用过C++的同学就知道,这么做在C++中是行不通的。 + +## 什么原理 + +其实很简单,编译器把`&34`转变成了以下语句: + +```rust +fn main() { + const THIRTY_FOUR: i32 = 34; + println!("{:p}", &THIRTY_FOUR); +} + +fn main() { + let thirty_four = 34; + println!("{:p}", &thirty_four); +} +``` + +也就是,先赋值给一个变量或者常量,然后再打印出来。 + +实际上下面的代码也可以: +```rust +println!("{:p}", &rand()); +``` + +同样的,编译器会自动增加一行:`let r = rand(); println!("{:p}", &r);`, 但是,该地址只在当前表达式范围内有效,因此假如你有一个函数`foo(&rand())`并且希望这个函数返回函数参数的引用,这样是无法通过编译的。 \ No newline at end of file diff --git a/codes/src/stack-heap/index.md b/codes/src/stack-heap/index.md new file mode 100644 index 00000000..228dc24a --- /dev/null +++ b/codes/src/stack-heap/index.md @@ -0,0 +1 @@ +# 堆和栈 diff --git a/codes/src/stack-heap/recursive-stack-overlfow.md b/codes/src/stack-heap/recursive-stack-overlfow.md new file mode 100644 index 00000000..0770079e --- /dev/null +++ b/codes/src/stack-heap/recursive-stack-overlfow.md @@ -0,0 +1,99 @@ +# 避免递归函数栈溢出 + +当一个递归函数,输入值很大时,可能会造成stack overflow问题,例如对一个图,进行深度优先搜索,那么这种时候该如何解决呢?我们提供了两种方法: + +1. 使用Rust编译器提供的`generator`特性,这个在目前版本还需要`nightly-rust`,该特性可以把递归函数展开为迭代器(iterator)来执行: + +```rust +#![feature(generators, generator_trait)] +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; + +// 原递归函数 +fn triangular(n: u64) -> u64 { + if n == 0 { + 0 + } else { + n + triangular(n - 1) + } +} + +// 新stack-safe函数,通过下面的generator进行展开,不会造成栈溢出 +fn triangular_safe(n: u64) -> u64 { + // `move |_|`是Rust编译器的generator语法 + trampoline(|n| move |_| { + if n == 0 { + 0 + } else { + // 使用`yield`关键字来替代递归函数名 + n + yield (n - 1) + } + })(n) +} + +// 这是一个高阶函数 +// 该函数不仅仅能处理triangular这种类型的递归函数,它可以处理一切fn(A) -> B形式的递归函数,只要满足A不包含任何可变/// 的引用. +// 这里f是一个generator函数,它是在上面的函数中通过yield关键字产生的 +// 我们可以把f想象成一个可以中断的循环函数,而不是调用自身的递归函数,这个循环是由一个f调用流组成 +fn trampoline( + f: impl Fn(Arg) -> Gen +) -> impl Fn(Arg) -> Res +where + Res: Default, + Gen: Generator + Unpin, +{ + move |arg: Arg| { + let mut stack = Vec::new(); + let mut current = f(arg); + let mut res = Res::default(); + + loop { + match Pin::new(&mut current).resume(res) { + GeneratorState::Yielded(arg) => { + stack.push(current); + current = f(arg); + res = Res::default(); + } + GeneratorState::Complete(real_res) => { + match stack.pop() { + None => return real_res, + Some(top) => { + current = top; + res = real_res; + } + } + } + } + } + } +} + +fn main() { + const LARGE: u64 = 1_000_000; + + assert_eq!(triangular_safe(LARGE), LARGE * (LARGE + 1) / 2); + println!("`triangular_safe` has not overflowed its stack."); + + println!("`triangular` will overflow its stack soon..."); + assert_eq!(triangular(LARGE), LARGE * (LARGE + 1) / 2); +} +``` + +上面的实现确实复杂了,但是非常安全,而且也挺高效的,我们有过一个性能测试,对于`Tarjan’s`算法,使用该方式仅仅比递归方式损失了5%的性能 + +2. 使用`proc macro`实现的三方包:[`decurse`](https://github.com/wishawa/decurse) + +简而言之,该包是通过把递归变为异步执行(async/await)的方式来实现的, + +```rust +#[decurse::decurse] // 👈 加上该行既可以避免栈溢出 +fn factorial(x: u32) -> u32 { + if x == 0 { + 1 + } else { + x * factorial(x - 1) + } +} +``` + +该方式的优点是**简单**,且无需**`nightly-rust`**,缺点是性能没有第一种高 \ No newline at end of file diff --git a/codes/src/templates/files/dir.md b/codes/src/templates/files/dir.md new file mode 100644 index 00000000..0d24bbfc --- /dev/null +++ b/codes/src/templates/files/dir.md @@ -0,0 +1 @@ +# 目录(todo) diff --git a/codes/src/templates/files/intro.md b/codes/src/templates/files/intro.md new file mode 100644 index 00000000..e3b630ee --- /dev/null +++ b/codes/src/templates/files/intro.md @@ -0,0 +1 @@ +# 文件操作 diff --git a/codes/src/templates/http/intro.md b/codes/src/templates/http/intro.md new file mode 100644 index 00000000..6f768909 --- /dev/null +++ b/codes/src/templates/http/intro.md @@ -0,0 +1 @@ +# Http请求(todo) diff --git a/codes/src/templates/intro.md b/codes/src/templates/intro.md new file mode 100644 index 00000000..f428fdf9 --- /dev/null +++ b/codes/src/templates/intro.md @@ -0,0 +1 @@ +# 场景模版 todo diff --git a/codes/src/unsafe/get-arryr-index-by-elem.md b/codes/src/unsafe/get-arryr-index-by-elem.md new file mode 100644 index 00000000..85ebaa4c --- /dev/null +++ b/codes/src/unsafe/get-arryr-index-by-elem.md @@ -0,0 +1,31 @@ +# 通过元素获取数组索引 +给出一个数组和其中的元素,获取该元素在数组中的索引位置. + +## unsafe版本 +```rust +fn main() { + let slice = &[1, 2, 3, 4]; + let elem = &slice[2]; + + let elem_ptr = elem as *const i32; + let index = unsafe { + elem_ptr.offset_from(slice.as_ptr()) + }; + + println!("{}",index); +} +``` + +## 正常版本 +实际上,该代码还能通过正常的方式实现,只要把指针转为`usize`: +```rust +fn main() { + let slice = &[1, 2, 3, 4]; + let elem = &slice[2]; + let slice_ptr = slice.as_ptr() as usize; + let elem_ptr = elem as *const i32 as usize; + let index = (elem_ptr - slice_ptr) / std::mem::size_of::(); + + println!("{}",index); +} +``` \ No newline at end of file diff --git a/codes/src/unsafe/index.md b/codes/src/unsafe/index.md new file mode 100644 index 00000000..b119af34 --- /dev/null +++ b/codes/src/unsafe/index.md @@ -0,0 +1 @@ +# Unsafe Rust diff --git a/codes/src/unsafe/multi-dimension-array.md b/codes/src/unsafe/multi-dimension-array.md new file mode 100644 index 00000000..6b5f70df --- /dev/null +++ b/codes/src/unsafe/multi-dimension-array.md @@ -0,0 +1,250 @@ +# 实现多维数组 + +```rust +// #~~~~~~~~~~~~~~~~# +// # Utility types/ # +// # boilerplate # +// #~~~~~~~~~~~~~~~~# + +/// An integer half the width of a `usize` +#[derive(Clone, Copy)] +#[repr(transparent)] +struct Halfsize( + #[cfg(target_pointer_width = "128")] u64, + #[cfg(target_pointer_width = "64")] u32, + #[cfg(target_pointer_width = "32")] u16, + #[cfg(target_pointer_width = "16")] u8, +); + +impl From for usize { + fn from(val: Halfsize) -> usize { + match val.0.try_into() { + Ok(val) => val, + // SAFETY: A Halfsize can always fit into a usize. + Err(_) => unsafe { std::hint::unreachable_unchecked() } + } + } +} + +impl TryFrom for Halfsize { + type Error = std::num::TryFromIntError; + fn try_from(val: usize) -> Result { + val.try_into().map(Halfsize) + } +} + +impl std::ops::Mul for Halfsize { + type Output = usize; + fn mul(self, rhs: Self) -> usize { + match usize::from(self).checked_mul(rhs.into()) { + Some(val) => val, + // SAFETY: + // + // Halfsize::MAX = 2^k - 1 + // usize::MAX = 2^(2k) - 1 + // + // Halfsize::MAX^2 = 2^(2k) - 2*2^k + 1 < usize::MAX + // Thus, the product of any two Halfsizes can fit in a usize. + None => unsafe { std::hint::unreachable_unchecked() }, + } + } +} + +/// Holds two values of the same type, with a guaranteed memory layout. +#[repr(C)] +struct Pair(T, T); + +impl From for Pair { + #[inline(always)] + fn from(val: usize) -> Pair { + // SAFETY: Transmuting usize -> Pair is sound, as Halfsize is + // exactly half the width of a usize, Pair is repr(C), and any + // bit pattern for Halfsize is valid. + unsafe { std::mem::transmute(val) } + } +} +impl From> for usize { + #[inline(always)] + fn from(val: Pair) -> usize { + // SAFETY: Transmuting Pair -> usize is sound, as Halfsize is + // exactly half the width of a usize, Pair is repr(C), and any + // bit pattern for usize is valid. + unsafe { std::mem::transmute(val) } + } +} + +// #~~~~~~~~~~~~~~~~~~~~~~# +// # Some old matrix type # +// #~~~~~~~~~~~~~~~~~~~~~~# + +/// An owned matrix. +pub struct Matrix { + items: Vec, + rows: Halfsize, + cols: Halfsize, +} + +impl Matrix { + pub fn new(rows: usize, cols: usize) -> Self + where + T: Default, + { + let items: Vec = (0..rows * cols).map(|_| Default::default()).collect(); + debug_assert_eq!(items.len(), rows * cols); + + Self { + items, + rows: rows.try_into().expect("`rows` must fit in half a usize"), + cols: cols.try_into().expect("`cols` must fit in half a usize"), + } + } + + pub fn rows(&self) -> usize { + self.rows.into() + } + pub fn cols(&self) -> usize { + self.cols.into() + } +} + +// #~~~~~~~~~~~~~~~~~~~~~~~~~# +// # Where the magic happens # +// #~~~~~~~~~~~~~~~~~~~~~~~~~# + +/// A reference to a matrix. +/// Dimensions are stored in the second field of the fat pointer. +#[repr(transparent)] +pub struct Mat(std::marker::PhantomData, [()]); + +impl Mat { + pub fn rows(&self) -> usize { + // get from the second field of the fat pointer. + let Pair(rows, _) = self.1.len().into(); + rows.into() + } + pub fn cols(&self) -> usize { + // get from the second field of the fat pointer. + let Pair(_, cols) = self.1.len().into(); + cols.into() + } + fn dim(&self) -> Pair { + // get from the second field of the fat pointer. + let Pair(rows, cols) = self.1.len().into(); + Pair(rows.into(), cols.into()) + } +} + +impl std::ops::Deref for Matrix { + type Target = Mat; + fn deref(&self) -> &Mat { + let ptr = self.items[..].as_ptr() as *const (); + let dim = Pair(self.rows, self.cols).into(); + + // SAFETY: Creating this slice is sound, as `from_raw_parts` requires + // `ptr` to point to `dim` fully-initialized and aligned instances + // of (). + // () is a ZST, so it is fully initialized and aligned no matter what. + let slice = unsafe { std::slice::from_raw_parts(ptr, dim) }; + + // SAFETY: `Mat` is repr(transparent), so it's sound to transmute + // from &[()] -> &Mat. + unsafe { std::mem::transmute(slice) } + } +} + +impl std::ops::DerefMut for Matrix { + fn deref_mut(&mut self) -> &mut Mat { + let ptr = self.items[..].as_mut_ptr() as *mut (); + let dim = Pair(self.rows, self.cols).into(); + + // SAFETY: Creating this slice is sound, as `from_raw_parts` requires + // `ptr` to point to `dim` fully-initialized and aligned instances + // of (). + // () is a ZST, so it is fully initialized and aligned no matter what. + let slice = unsafe { std::slice::from_raw_parts_mut(ptr, dim) }; + + // SAFETY: `Mat` is repr(transparent), so it's sound to transmute + // from &mut [()] -> &mut Mat. + unsafe { std::mem::transmute(slice) } + } +} + +impl std::borrow::Borrow> for Matrix { + fn borrow(&self) -> &Mat { + &*self + } +} + +impl std::borrow::ToOwned for Mat +where + T: Clone, +{ + type Owned = Matrix; + fn to_owned(&self) -> Matrix { + // fetch the first field of the fat pointer. + let ptr = self.1.as_ptr() as *const T; + // fetch the second field of the fat pointer. + let Pair(rows, cols) = self.1.len().into(); + + // SAFETY: A Mat can only be safely created from the `Deref` or + // `DerefMut` impls, so we know that `ptr` must point to `rows * cols` + // properly initialized values of T. + let items = unsafe { std::slice::from_raw_parts(ptr, rows * cols) }; + let items = items.to_vec(); + + Matrix { items, rows, cols } + } +} + +impl std::ops::Index<[usize; 2]> for Mat { + type Output = T; + fn index(&self, [i, j]: [usize; 2]) -> &T { + let Pair(rows, cols) = self.dim(); + assert!(i < rows); + assert!(j < cols); + + // SAFETY: A `&Mat` can only be safely created from the `Deref` or + // `DerefMut` impls, so we know that `ptr` must point to `rows * cols` + // properly initialized values of T. + // Since `i` < `rows` and `j` < `cols`, we know that `i * cols + j` + // must be less than `rows * cols`. + unsafe { + let ptr = self.1.as_ptr() as *const T; + &*ptr.add(i * cols + j) + } + } +} +impl std::ops::IndexMut<[usize; 2]> for Mat { + fn index_mut(&mut self, [i, j]: [usize; 2]) -> &mut T { + let Pair(rows, cols) = self.dim(); + assert!(i < rows); + assert!(j < cols); + + // SAFETY: A `&mut Mat` can only be safely created from the `DerefMut` + // impl, so we know that `ptr` must point to `rows * cols` properly + // initialized values of T. + // Since `i` < `rows` and `j` < `cols`, we know that `i * cols + j` + // must be less than `rows * cols`. + unsafe { + let ptr = self.1.as_mut_ptr() as *mut T; + &mut *ptr.add(i * cols + j) + } + } +} + +fn main() { + let mut matrix = Matrix::new(3, 4); + matrix[[0, 1]] = 1; + matrix[[2, 2]] = 4; + assert_eq!(matrix[[0, 1]], 1); + + let mat = &*matrix; + assert_eq!(mat[[0, 1]], 1); + assert_eq!(mat[[2, 2]], 4); + + let matrix2 = mat.to_owned(); + assert_eq!(matrix2[[0, 0]], 0); + assert_eq!(matrix2[[0, 1]], 1); + assert_eq!(matrix2[[2, 2]], 4); +} +``` \ No newline at end of file diff --git a/codes/src/unsafe/self-ref.md b/codes/src/unsafe/self-ref.md new file mode 100644 index 00000000..074cff54 --- /dev/null +++ b/codes/src/unsafe/self-ref.md @@ -0,0 +1,282 @@ +## 自引用unsafe实现 + +```rust +// 使用原生指针和unsafe实现自引用 +pub struct Tree { + count: usize, + root: *mut Node, +} + +#[derive(Debug)] +struct Node { + data: i32, + + // Null指针这里代表"None`; right.is_null() ==> 没有right child + left: *mut Node, + right: *mut Node, + parent: *mut Node, +} + +impl Tree { + pub fn new() -> Self { + Self { + count: 0, + root: std::ptr::null_mut(), + } + } + + // 返回tree的节点数量 + pub fn node_count(&self) -> usize { + assert!(self.count != 0 || self.root.is_null()); + self.count + } + + + // 在tree中新增一项,插入成功则返回true,若给定的数据在tree上已经存在,则返回false + pub fn insert(&mut self, data: i32) -> bool { + if self.root.is_null() { + self.root = Node::new(data); + } else { + if !insert_node(self.root, data) { + return false; + } + } + + self.count += 1; + true + } + + // 找到tree上的指定项,若找到,返回true + pub fn find(&self, data: i32) -> bool { + !find_node(self.root, data).is_null() + } + + // 返回tree的字符串形式,用于Debug + pub fn display(&self) -> String { + display_node(self.root, 0) + } + + // 使用中序遍历来返回tree中的所有数据 + pub fn inorder(&self) -> Vec { + let mut v = vec![]; + if !self.root.is_null() { + let mut node = leftmost_child(self.root); + loop { + if node.is_null() { + break; + } + unsafe { + v.push((*node).data); + } + node = successor_of_node(node); + } + } + v + } + + // 从tree上移除指定项, 若该项存在且被成功移除,则返回true,否则都返回false + pub fn remove(&mut self, data: i32) -> bool { + let node = find_node(self.root, data); + if node.is_null() { + false + } else { + self.remove_node(node); + self.count -= 1; + true + } + } + + // 在tree上找到指定项的继任者 + pub fn successor(&self, data: i32) -> Option { + unsafe { + let node = find_node(self.root, data); + if !node.is_null() { + let nodesucc = successor_of_node(node); + if !nodesucc.is_null() { + return Some((*nodesucc).data); + } + } + None + } + } + + // 从tree上移除指定的节点 + fn remove_node(&mut self, node: *mut Node) { + unsafe { + let lchild = (*node).left; + let rchild = (*node).right; + if lchild.is_null() && rchild.is_null() { + // 节点没有子节点,所以可以安全移除 + self.replace_node(node, std::ptr::null_mut()); + } else if !lchild.is_null() && !rchild.is_null() { + // 节点的左右子节点都在,我们需要找到该节点的继任者,然后将继任者的数据赋给当前节点,然后再递归删除继任者 + let succ = successor_of_node(node); + assert!(!succ.is_null()); + (*node).data = (*succ).data; + self.remove_node(succ); + } else if !lchild.is_null() { + // 节点只有左子节点,所以使用后者替代前者 + self.replace_node(node, lchild); + } else if !rchild.is_null() { + // 节点只有右子节点,所以使用后者替代前者 + self.replace_node(node, rchild); + } else { + panic!("unreachable"); + } + } + } + + // 使用`r`节点来替换目标`node`节点 + fn replace_node(&mut self, node: *mut Node, r: *mut Node) { + unsafe { + let parent = (*node).parent; + if parent.is_null() { + // Removing the root node. + self.root = r; + if !r.is_null() { + (*r).parent = std::ptr::null_mut(); + } + } else { + if !r.is_null() { + (*r).parent = parent; + } + if (*parent).left == node { + (*parent).left = r; + } else if (*parent).right == node { + (*parent).right = r; + } + } + + // 被替换的节点不再被使用,因此可以回收它:通过`Box`拿走它的所有权,然后它会被自动drop + Box::from_raw(node); + } + } +} + +impl Drop for Tree { + fn drop(&mut self) { + // 也许不是性能最高的实现,但是简单,而且有用 + while !self.root.is_null() { + self.remove_node(self.root); + } + } +} + +impl Node { + fn new(data: i32) -> *mut Self { + Box::into_raw(Box::new(Self { + data, + left: std::ptr::null_mut(), + right: std::ptr::null_mut(), + parent: std::ptr::null_mut(), + })) + } + + fn new_with_parent(data: i32, parent: *mut Node) -> *mut Self { + Box::into_raw(Box::new(Self { + data, + left: std::ptr::null_mut(), + right: std::ptr::null_mut(), + parent, + })) + } +} + +// 在节点子树上创建新的节点 +fn insert_node(node: *mut Node, data: i32) -> bool { + unsafe { + if (*node).data == data { + false + } else if data < (*node).data { + if (*node).left.is_null() { + (*node).left = Node::new_with_parent(data, node); + true + } else { + insert_node((*node).left, data) + } + } else { + if (*node).right.is_null() { + (*node).right = Node::new_with_parent(data, node); + true + } else { + insert_node((*node).right, data) + } + } + } +} + +// 在`fromnode`的子树上寻找目标数据,如果没找到则返回`null` +fn find_node(fromnode: *mut Node, data: i32) -> *mut Node { + unsafe { + if fromnode.is_null() || (*fromnode).data == data { + fromnode + } else if data < (*fromnode).data { + find_node((*fromnode).left, data) + } else { + find_node((*fromnode).right, data) + } + } +} + + +// 返回`node`子树的字符串形式,同时指定缩进字符数 +fn display_node(node: *const Node, indent: usize) -> String { + let indent_str = " ".repeat(indent); + if node.is_null() { + indent_str + ".\n" + } else { + unsafe { + let mut s = format!("{}{}\n", indent_str, (*node).data); + s.push_str(&display_node((*node).left, indent + 2)); + s.push_str(&display_node((*node).right, indent + 2)); + s + } + } +} + +// 找到`node`最左边的子节点,如果没有,就返回`node`自身, `node`不能为null +fn leftmost_child(node: *mut Node) -> *mut Node { + unsafe { + if (*node).left.is_null() { + node + } else { + leftmost_child((*node).left) + } + } +} + + +// 在tree上找到`node`的继任者 +fn successor_of_node(node: *mut Node) -> *mut Node { + unsafe { + if !(*node).right.is_null() { + // 若node有一个右子节点,则继任者是该右子节点的最左子节点,若该右子节点没有子节点,则继任者就是右子节点 + leftmost_child((*node).right) + } else { + // 没有右子节点,则找到一个父节点,当前node是该父节点的左子节点, 若在root之前都没找到,说明node没有继任者 + parent_with_left(node) + } + } +} + +// 在`node`的祖先中找到它的父节点,且`node`必须是该父节点的左子节点 +fn parent_with_left(node: *mut Node) -> *mut Node { + unsafe { + // 若`node`有父节点,且该父节点拥有左子节点,同时`node`就是这个左子节点,那么该父节点就是我们的目标 + let parent = (*node).parent; + if !parent.is_null() { + if std::ptr::eq((*parent).left, node) { + return parent; + } + return parent_with_left(parent); + } + + // `node`没有父节点 + std::ptr::null_mut() + } +} + +fn main() { + +} +``` \ No newline at end of file diff --git a/course-book/contents/.DS_Store b/course-book/contents/.DS_Store deleted file mode 100644 index 3b395cba..00000000 Binary files a/course-book/contents/.DS_Store and /dev/null differ