diff --git a/.github/workflows/c.yml b/.github/workflows/c.yml index 8259cd2b1..963c42e17 100644 --- a/.github/workflows/c.yml +++ b/.github/workflows/c.yml @@ -6,10 +6,18 @@ name: C on: push: branches: ["main"] - paths: ["codes/c/**/*.c", "codes/c/**/*.h"] + paths: + - "codes/c/**/*.c" + - "codes/c/**/*.h" + - "en/codes/c/**/*.c" + - "en/codes/c/**/*.h" pull_request: branches: ["main"] - paths: ["codes/c/**/*.c", "codes/c/**/*.h"] + paths: + - "codes/c/**/*.c" + - "codes/c/**/*.h" + - "en/codes/c/**/*.c" + - "en/codes/c/**/*.h" workflow_dispatch: jobs: @@ -30,6 +38,7 @@ jobs: os: [ubuntu-latest, windows-latest] build_type: [Release] c_compiler: [gcc, clang, cl] + code-dir: ["codes/c", "en/codes/c"] include: - os: windows-latest c_compiler: cl @@ -55,18 +64,18 @@ jobs: # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: > - cmake -B ${{ github.workspace }}/codes/c/build + cmake -B ${{ github.workspace }}/${{ matrix.code-dir }}/build -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - -S ${{ github.workspace }}/codes/c + -S ${{ github.workspace }}/${{ matrix.code-dir }} - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build ${{ github.workspace }}/codes/c/build --config ${{ matrix.build_type }} + run: cmake --build ${{ github.workspace }}/${{ matrix.code-dir }}/build --config ${{ matrix.build_type }} - name: Test - working-directory: ${{ github.workspace }}/codes/c/build + working-directory: ${{ github.workspace }}/${{ matrix.code-dir }}/build # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest --build-config ${{ matrix.build_type }} diff --git a/.github/workflows/cpp.yml b/.github/workflows/cpp.yml index ebf3a3e97..314c1a09b 100644 --- a/.github/workflows/cpp.yml +++ b/.github/workflows/cpp.yml @@ -6,10 +6,18 @@ name: C++ on: push: branches: ["main"] - paths: ["codes/cpp/**/*.cpp", "codes/cpp/**/*.hpp"] + paths: + - "codes/cpp/**/*.cpp" + - "codes/cpp/**/*.hpp" + - "en/codes/cpp/**/*.cpp" + - "en/codes/cpp/**/*.hpp" pull_request: branches: ["main"] - paths: ["codes/cpp/**/*.cpp", "codes/cpp/**/*.hpp"] + paths: + - "codes/cpp/**/*.cpp" + - "codes/cpp/**/*.hpp" + - "en/codes/cpp/**/*.cpp" + - "en/codes/cpp/**/*.hpp" workflow_dispatch: jobs: @@ -30,6 +38,7 @@ jobs: os: [ubuntu-latest, windows-latest] build_type: [Release] c_compiler: [gcc, clang, cl] + code-dir: ["codes/cpp", "en/codes/cpp"] include: - os: windows-latest c_compiler: cl @@ -55,18 +64,18 @@ jobs: # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: > - cmake -B ${{ github.workspace }}/codes/cpp/build + cmake -B ${{ github.workspace }}/${{ matrix.code-dir }}/build -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - -S ${{ github.workspace }}/codes/cpp + -S ${{ github.workspace }}/${{ matrix.code-dir }} - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build ${{ github.workspace }}/codes/cpp/build --config ${{ matrix.build_type }} + run: cmake --build ${{ github.workspace }}/${{ matrix.code-dir }}/build --config ${{ matrix.build_type }} - name: Test - working-directory: ${{ github.workspace }}/codes/cpp/build + working-directory: ${{ github.workspace }}/${{ matrix.code-dir }}/build # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest --build-config ${{ matrix.build_type }} diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index a6e7bab2b..b651f0c09 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -5,10 +5,14 @@ name: Dart on: push: branches: ["main"] - paths: ["codes/dart/**/*.dart"] + paths: + - "codes/dart/**/*.dart" + - "en/codes/dart/**/*.dart" pull_request: branches: ["main"] - paths: ["codes/dart/**/*.dart"] + paths: + - "codes/dart/**/*.dart" + - "en/codes/dart/**/*.dart" workflow_dispatch: permissions: @@ -22,6 +26,7 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] dart-sdk: [stable] + code-dir: ["codes/dart", "en/codes/dart"] steps: - uses: actions/checkout@v4 - name: Set up Dart ${{ matrix.dart-sdk }} @@ -29,8 +34,8 @@ jobs: with: sdk: ${{ matrix.dart-sdk}} - name: Run format - run: dart format codes/dart + run: dart format ${{ matrix.code-dir }} - name: Run analyze - run: dart analyze codes/dart + run: dart analyze ${{ matrix.code-dir }} - name: Run build - run: dart codes/dart/build.dart + run: dart ${{ matrix.code-dir }}/build.dart diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 7001048c1..45f33a9ca 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -5,11 +5,15 @@ name: .NET on: push: - branches: [ "main" ] - paths: ["codes/csharp/**/*.cs"] + branches: ["main"] + paths: + - "codes/csharp/**/*.cs" + - "en/codes/csharp/**/*.cs" pull_request: - branches: [ "main" ] - paths: ["codes/csharp/**/*.cs"] + branches: ["main"] + paths: + - "codes/csharp/**/*.cs" + - "en/codes/csharp/**/*.cs" workflow_dispatch: jobs: @@ -18,22 +22,23 @@ jobs: runs-on: ${{ matrix.os }} defaults: run: - working-directory: codes/csharp/ + working-directory: ${{ matrix.code-dir }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] dotnet-version: ["8.0.x"] + code-dir: ["codes/csharp", "en/codes/csharp"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Setup .NET ${{ matrix.dotnet-version }} - uses: actions/setup-dotnet@v3 - with: - dotnet-version: ${{ matrix.dotnet-version }} - - name: Restore dependencies - run: dotnet restore hello-algo.csproj - - name: Build - run: dotnet build --no-restore hello-algo.csproj - - name: Test with dotnet - run: dotnet test hello-algo.csproj + - name: Setup .NET ${{ matrix.dotnet-version }} + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - name: Restore dependencies + run: dotnet restore hello-algo.csproj + - name: Build + run: dotnet build --no-restore hello-algo.csproj + - name: Test with dotnet + run: dotnet test hello-algo.csproj diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 6b615969d..61b142337 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -2,11 +2,15 @@ name: Go on: push: - branches: [ "main" ] - paths: ["codes/go/**/*.go"] + branches: ["main"] + paths: + - "codes/go/**/*.go" + - "en/codes/go/**/*.go" pull_request: - branches: [ "main" ] - paths: ["codes/go/**/*.go"] + branches: ["main"] + paths: + - "codes/go/**/*.go" + - "en/codes/go/**/*.go" workflow_dispatch: jobs: @@ -15,22 +19,23 @@ jobs: runs-on: ${{ matrix.os }} defaults: run: - working-directory: codes/go/ + working-directory: ${{ matrix.code-dir }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] go-version: ["1.19.x"] + code-dir: ["codes/go", "en/codes/go"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Setup Go ${{ matrix.go-version }} - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - - name: Check out code into the Go module directory - run: go get -v -t -d ./... - - name: Build - run: go build -v ./... - - name: Test with Go - run: go test -v ./... + - name: Setup Go ${{ matrix.go-version }} + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.go-version }} + - name: Check out code into the Go module directory + run: go get -v -t -d ./... + - name: Build + run: go build -v ./... + - name: Test with Go + run: go test -v ./... diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index 233b4292b..7e2bbfc5d 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -5,11 +5,15 @@ name: Java on: push: - branches: [ "main" ] - paths: ["codes/java/**/*.java"] + branches: ["main"] + paths: + - "codes/java/**/*.java" + - "en/codes/java/**/*.java" pull_request: - branches: [ "main" ] - paths: ["codes/java/**/*.java"] + branches: ["main"] + paths: + - "codes/java/**/*.java" + - "en/codes/java/**/*.java" workflow_dispatch: jobs: @@ -17,13 +21,14 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - java: [ '11', '17' ] + java: ["11", "17"] + code-dir: ["codes/java", "en/codes/java"] name: Java ${{ matrix.Java }} sample steps: - uses: actions/checkout@v4 - name: Setup java uses: actions/setup-java@v3 with: - distribution: 'temurin' + distribution: "temurin" java-version: ${{ matrix.java }} - - run: javac -d codes/java/build codes/java/**/*.java + - run: javac -d ${{ matrix.code-dir }}/build ${{ matrix.code-dir }}/**/*.java diff --git a/.github/workflows/javascript.yml b/.github/workflows/javascript.yml index 4d435deb4..0768269f6 100644 --- a/.github/workflows/javascript.yml +++ b/.github/workflows/javascript.yml @@ -2,11 +2,15 @@ name: JavaScript on: push: - branches: ['main'] - paths: ['codes/javascript/**/*.js'] + branches: ["main"] + paths: + - "codes/javascript/**/*.js" + - "en/codes/javascript/**/*.js" pull_request: - branches: ['main'] - paths: ['codes/javascript/**/*.js'] + branches: ["main"] + paths: + - "codes/javascript/**/*.js" + - "en/codes/javascript/**/*.js" workflow_dispatch: jobs: @@ -15,6 +19,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] + code-dir: ["codes/javascript", "en/codes/javascript"] steps: - uses: actions/checkout@v5 - uses: actions/setup-node@v6 @@ -24,4 +29,4 @@ jobs: with: deno-version: v2.x - name: Run JavaScript Code - run: deno run -A codes/javascript/test_all.js + run: deno run -A ${{ matrix.code-dir }}/test_all.js diff --git a/.github/workflows/kotlin.yml b/.github/workflows/kotlin.yml index aedeae6f8..fd24a88c4 100644 --- a/.github/workflows/kotlin.yml +++ b/.github/workflows/kotlin.yml @@ -2,11 +2,15 @@ name: Kotlin on: push: - branches: [ "main" ] - paths: ["codes/kotlin/**/*.kt"] + branches: ["main"] + paths: + - "codes/kotlin/**/*.kt" + - "en/codes/kotlin/**/*.kt" pull_request: - branches: [ "main" ] - paths: ["codes/kotlin/**/*.kt"] + branches: ["main"] + paths: + - "codes/kotlin/**/*.kt" + - "en/codes/kotlin/**/*.kt" workflow_dispatch: jobs: @@ -14,11 +18,12 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-latest, macos-latest ] + os: [ubuntu-latest, macos-latest] + code-dir: ["codes/kotlin", "en/codes/kotlin"] name: Kotlin on ${{ matrix.os }} steps: - uses: actions/checkout@v4.1.2 - name: Build JAR - run: kotlinc codes/kotlin/**/*.kt -include-runtime -d codes/kotlin/build/test.jar + run: kotlinc ${{ matrix.code-dir }}/**/*.kt -include-runtime -d ${{ matrix.code-dir }}/build/test.jar diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index e178e2e73..dbfa7bba9 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -6,10 +6,14 @@ name: Python on: push: branches: ["main"] - paths: ["codes/python/**/*.py"] + paths: + - "codes/python/**/*.py" + - "en/codes/python/**/*.py" pull_request: branches: ["main"] - paths: ["codes/python/**/*.py"] + paths: + - "codes/python/**/*.py" + - "en/codes/python/**/*.py" workflow_dispatch: permissions: @@ -22,7 +26,8 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.10", "3.11"] + python-version: ["3.10"] + code-dir: ["codes/python", "en/codes/python"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} @@ -35,7 +40,7 @@ jobs: pip install black - name: Lint with black run: | - black codes/python + black ${{ matrix.code-dir }} - name: Test python code run: | - python codes/python/test_all.py + cd ${{ matrix.code-dir }} && python test_all.py diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 2083cc086..963a6f7fd 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -7,11 +7,15 @@ name: Ruby on: push: - branches: [ "main" ] - paths: ["codes/ruby/**/*.rb"] + branches: ["main"] + paths: + - "codes/ruby/**/*.rb" + - "en/codes/ruby/**/*.rb" pull_request: - branches: [ "main" ] - paths: ["codes/ruby/**/*.rb"] + branches: ["main"] + paths: + - "codes/ruby/**/*.rb" + - "en/codes/ruby/**/*.rb" workflow_dispatch: permissions: @@ -19,19 +23,19 @@ permissions: jobs: test: - name: Ruby ${{ matrix.ruby-version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - ruby-version: ['3.3'] + ruby-version: ["3.3"] + code-dir: ["codes/ruby", "en/codes/ruby"] steps: - - uses: actions/checkout@v4 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby-version }} - - name: Run tests - run: ruby codes/ruby/test_all.rb + - uses: actions/checkout@v4 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + - name: Run tests + run: ruby ${{ matrix.code-dir }}/test_all.rb diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fada28651..a40d97a9b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -3,10 +3,18 @@ name: Rust on: push: branches: ["main"] - paths: ["codes/rust/**/*.rs", "codes/rust/Cargo.toml"] + paths: + - "codes/rust/**/*.rs" + - "codes/rust/Cargo.toml" + - "en/codes/rust/**/*.rs" + - "en/codes/rust/Cargo.toml" pull_request: branches: ["main"] - paths: ["codes/rust/**/*.rs", "codes/rust/Cargo.toml"] + paths: + - "codes/rust/**/*.rs" + - "codes/rust/Cargo.toml" + - "en/codes/rust/**/*.rs" + - "en/codes/rust/Cargo.toml" jobs: build: @@ -15,13 +23,14 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] + code-dir: ["codes/rust", "en/codes/rust"] steps: - uses: brndnmtthws/rust-action-rustup@v1 with: - toolchain: nightly - + toolchain: nightly + - uses: actions/checkout@v4 - + - name: Build - run: cargo build --manifest-path=codes/rust/Cargo.toml && cargo build --manifest-path=codes/rust/Cargo.toml --release + run: cargo build --manifest-path=${{ matrix.code-dir }}/Cargo.toml && cargo build --manifest-path=${{ matrix.code-dir }}/Cargo.toml --release diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index f8bfb6bee..0241fd8ae 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -6,10 +6,14 @@ name: Swift on: push: branches: ["main"] - paths: ["codes/swift/**/*.swift"] + paths: + - "codes/swift/**/*.swift" + - "en/codes/swift/**/*.swift" pull_request: branches: ["main"] - paths: ["codes/swift/**/*.swift"] + paths: + - "codes/swift/**/*.swift" + - "en/codes/swift/**/*.swift" workflow_dispatch: jobs: @@ -19,7 +23,8 @@ jobs: strategy: matrix: os: ["ubuntu-22.04", "macos-14"] + code-dir: ["codes/swift", "en/codes/swift"] steps: - uses: actions/checkout@v4 - name: Build - run: swift build --package-path codes/swift + run: swift build --package-path ${{ matrix.code-dir }} diff --git a/.github/workflows/typescript.yml b/.github/workflows/typescript.yml index 8acd7053f..b1fb2e4f1 100644 --- a/.github/workflows/typescript.yml +++ b/.github/workflows/typescript.yml @@ -2,11 +2,15 @@ name: TypeScript on: push: - branches: ['main'] - paths: ['codes/typescript/**/*.ts'] + branches: ["main"] + paths: + - "codes/typescript/**/*.ts" + - "en/codes/typescript/**/*.ts" pull_request: - branches: ['main'] - paths: ['codes/typescript/**/*.ts'] + branches: ["main"] + paths: + - "codes/typescript/**/*.ts" + - "en/codes/typescript/**/*.ts" workflow_dispatch: jobs: @@ -15,12 +19,13 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] + code-dir: ["codes/typescript", "en/codes/typescript"] steps: - uses: actions/checkout@v5 - uses: actions/setup-node@v6 with: node-version: 24.x - name: Install dependencies - run: cd codes/typescript && npm install + run: cd ${{ matrix.code-dir }} && npm install - name: Check TypeScript code - run: cd codes/typescript && npm run check + run: cd ${{ matrix.code-dir }} && npm run check diff --git a/codes/python/test_all.py b/codes/python/test_all.py index a90c773a4..7c585de87 100644 --- a/codes/python/test_all.py +++ b/codes/python/test_all.py @@ -7,7 +7,7 @@ env["PYTHONIOENCODING"] = "utf-8" if __name__ == "__main__": # find source code files - src_paths = sorted(glob.glob("codes/python/chapter_*/*.py")) + src_paths = sorted(glob.glob("chapter_*/*.py")) errors = [] # run python code diff --git a/docs/chapter_divide_and_conquer/summary.md b/docs/chapter_divide_and_conquer/summary.md index c27ecefd5..0eeb5b30c 100644 --- a/docs/chapter_divide_and_conquer/summary.md +++ b/docs/chapter_divide_and_conquer/summary.md @@ -1,5 +1,7 @@ # 小结 +### 重点回顾 + - 分治是一种常见的算法设计策略,包括分(划分)和治(合并)两个阶段,通常基于递归实现。 - 判断是否是分治算法问题的依据包括:问题能否分解、子问题是否独立、子问题能否合并。 - 归并排序是分治策略的典型应用,其递归地将数组划分为等长的两个子数组,直到只剩一个元素时开始逐层合并,从而完成排序。 diff --git a/docs/chapter_dynamic_programming/summary.md b/docs/chapter_dynamic_programming/summary.md index c46f54dfc..34eaf3259 100644 --- a/docs/chapter_dynamic_programming/summary.md +++ b/docs/chapter_dynamic_programming/summary.md @@ -1,5 +1,7 @@ # 小结 +### 重点回顾 + - 动态规划对问题进行分解,并通过存储子问题的解来规避重复计算,提高计算效率。 - 不考虑时间的前提下,所有动态规划问题都可以用回溯(暴力搜索)进行求解,但递归树中存在大量的重叠子问题,效率极低。通过引入记忆化列表,可以存储所有计算过的子问题的解,从而保证重叠子问题只被计算一次。 - 记忆化搜索是一种从顶至底的递归式解法,而与之对应的动态规划是一种从底至顶的递推式解法,其如同“填写表格”一样。由于当前状态仅依赖某些局部状态,因此我们可以消除 $dp$ 表的一个维度,从而降低空间复杂度。 diff --git a/docs/chapter_greedy/summary.md b/docs/chapter_greedy/summary.md index caefed243..34c2e3bc0 100644 --- a/docs/chapter_greedy/summary.md +++ b/docs/chapter_greedy/summary.md @@ -1,5 +1,7 @@ # 小结 +### 重点回顾 + - 贪心算法通常用于解决最优化问题,其原理是在每个决策阶段都做出局部最优的决策,以期获得全局最优解。 - 贪心算法会迭代地做出一个又一个的贪心选择,每轮都将问题转化成一个规模更小的子问题,直到问题被解决。 - 贪心算法不仅实现简单,还具有很高的解题效率。相比于动态规划,贪心算法的时间复杂度通常更低。 diff --git a/docs/chapter_introduction/summary.md b/docs/chapter_introduction/summary.md index ba538bcaa..1bc4cddf7 100644 --- a/docs/chapter_introduction/summary.md +++ b/docs/chapter_introduction/summary.md @@ -1,5 +1,7 @@ # 小结 +### 重点回顾 + - 算法在日常生活中无处不在,并不是遥不可及的高深知识。实际上,我们已经在不知不觉中学会了许多算法,用以解决生活中的大小问题。 - 查字典的原理与二分查找算法相一致。二分查找算法体现了分而治之的重要算法思想。 - 整理扑克的过程与插入排序算法非常类似。插入排序算法适合排序小型数据集。 diff --git a/docs/chapter_preface/summary.md b/docs/chapter_preface/summary.md index 0ee5a588d..019ea7041 100644 --- a/docs/chapter_preface/summary.md +++ b/docs/chapter_preface/summary.md @@ -1,5 +1,7 @@ # 小结 +### 重点回顾 + - 本书的主要受众是算法初学者。如果你已有一定基础,本书能帮助你系统回顾算法知识,书中源代码也可作为“刷题工具库”使用。 - 书中内容主要包括复杂度分析、数据结构和算法三部分,涵盖了该领域的大部分主题。 - 对于算法新手,在初学阶段阅读一本入门书至关重要,可以少走许多弯路。 diff --git a/docs/chapter_searching/summary.md b/docs/chapter_searching/summary.md index 525250c4a..a488fb014 100644 --- a/docs/chapter_searching/summary.md +++ b/docs/chapter_searching/summary.md @@ -1,5 +1,7 @@ # 小结 +### 重点回顾 + - 二分查找依赖数据的有序性,通过循环逐步缩减一半搜索区间来进行查找。它要求输入数据有序,且仅适用于数组或基于数组实现的数据结构。 - 暴力搜索通过遍历数据结构来定位数据。线性搜索适用于数组和链表,广度优先搜索和深度优先搜索适用于图和树。此类算法通用性好,无须对数据进行预处理,但时间复杂度 $O(n)$ 较高。 - 哈希查找、树查找和二分查找属于高效搜索方法,可在特定数据结构中快速定位目标元素。此类算法效率高,时间复杂度可达 $O(\log n)$ 甚至 $O(1)$ ,但通常需要借助额外数据结构。 diff --git a/en/codes/c/.gitignore b/en/codes/c/.gitignore new file mode 100644 index 000000000..698ee4e21 --- /dev/null +++ b/en/codes/c/.gitignore @@ -0,0 +1,9 @@ +# Ignore all +* +# Unignore all with extensions +!*.* +# Unignore all dirs +!*/ +*.dSYM/ + +build/ diff --git a/en/codes/c/CMakeLists.txt b/en/codes/c/CMakeLists.txt new file mode 100644 index 000000000..bb5f8f6a9 --- /dev/null +++ b/en/codes/c/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.10) +project(hello_algo C) + +set(CMAKE_C_STANDARD 11) + +include_directories(./include) + +add_subdirectory(chapter_computational_complexity) +add_subdirectory(chapter_array_and_linkedlist) +add_subdirectory(chapter_stack_and_queue) +add_subdirectory(chapter_hashing) +add_subdirectory(chapter_tree) +add_subdirectory(chapter_heap) +add_subdirectory(chapter_graph) +add_subdirectory(chapter_searching) +add_subdirectory(chapter_sorting) +add_subdirectory(chapter_divide_and_conquer) +add_subdirectory(chapter_backtracking) +add_subdirectory(chapter_dynamic_programming) +add_subdirectory(chapter_greedy) diff --git a/en/codes/c/chapter_array_and_linkedlist/CMakeLists.txt b/en/codes/c/chapter_array_and_linkedlist/CMakeLists.txt new file mode 100644 index 000000000..29677a0be --- /dev/null +++ b/en/codes/c/chapter_array_and_linkedlist/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(array array.c) +add_executable(linked_list linked_list.c) +add_executable(my_list my_list.c) \ No newline at end of file diff --git a/en/codes/c/chapter_array_and_linkedlist/array.c b/en/codes/c/chapter_array_and_linkedlist/array.c new file mode 100644 index 000000000..a79501aa9 --- /dev/null +++ b/en/codes/c/chapter_array_and_linkedlist/array.c @@ -0,0 +1,114 @@ +/** + * File: array.c + * Created Time: 2022-12-20 + * Author: MolDuM (moldum@163.com) + */ + +#include "../utils/common.h" + +/* Random access to element */ +int randomAccess(int *nums, int size) { + // Randomly select a number from interval [0, size) + int randomIndex = rand() % size; + // Retrieve and return the random element + int randomNum = nums[randomIndex]; + return randomNum; +} + +/* Extend array length */ +int *extend(int *nums, int size, int enlarge) { + // Initialize an array with extended length + int *res = (int *)malloc(sizeof(int) * (size + enlarge)); + // Copy all elements from the original array to the new array + for (int i = 0; i < size; i++) { + res[i] = nums[i]; + } + // Initialize expanded space + for (int i = size; i < size + enlarge; i++) { + res[i] = 0; + } + // Return the extended new array + return res; +} + +/* Insert element num at index index in the array */ +void insert(int *nums, int size, int num, int index) { + // Move all elements at and after index index backward by one position + for (int i = size - 1; i > index; i--) { + nums[i] = nums[i - 1]; + } + // Assign num to the element at index index + nums[index] = num; +} + +/* Remove the element at index index */ +// Note: stdio.h occupies the remove keyword +void removeItem(int *nums, int size, int index) { + // Move all elements after index index forward by one position + for (int i = index; i < size - 1; i++) { + nums[i] = nums[i + 1]; + } +} + +/* Traverse array */ +void traverse(int *nums, int size) { + int count = 0; + // Traverse array by index + for (int i = 0; i < size; i++) { + count += nums[i]; + } +} + +/* Find the specified element in the array */ +int find(int *nums, int size, int target) { + for (int i = 0; i < size; i++) { + if (nums[i] == target) + return i; + } + return -1; +} + +/* Driver Code */ +int main() { + /* Initialize array */ + int size = 5; + int arr[5]; + printf("Array arr = "); + printArray(arr, size); + + int nums[] = {1, 3, 2, 5, 4}; + printf("Array nums = "); + printArray(nums, size); + + /* Insert element */ + int randomNum = randomAccess(nums, size); + printf("Get random element %d from nums", randomNum); + + /* Traverse array */ + int enlarge = 3; + int *res = extend(nums, size, enlarge); + size += enlarge; + printf("Extend array length to 8, resulting in nums = "); + printArray(res, size); + + /* Insert element */ + insert(res, size, 6, 3); + printf("Insert number 6 at index 3, resulting in nums = "); + printArray(res, size); + + /* Remove element */ + removeItem(res, size, 2); + printf("Remove element at index 2, resulting in nums = "); + printArray(res, size); + + /* Traverse array */ + traverse(res, size); + + /* Find element */ + int index = find(res, size, 3); + printf("Find element 3 in res, index = %d\n", index); + + /* Free memory */ + free(res); + return 0; +} diff --git a/en/codes/c/chapter_array_and_linkedlist/linked_list.c b/en/codes/c/chapter_array_and_linkedlist/linked_list.c new file mode 100644 index 000000000..e9238f724 --- /dev/null +++ b/en/codes/c/chapter_array_and_linkedlist/linked_list.c @@ -0,0 +1,89 @@ +/** + * File: linked_list.c + * Created Time: 2023-01-12 + * Author: Zero (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Insert node P after node n0 in the linked list */ +void insert(ListNode *n0, ListNode *P) { + ListNode *n1 = n0->next; + P->next = n1; + n0->next = P; +} + +/* Remove the first node after node n0 in the linked list */ +// Note: stdio.h occupies the remove keyword +void removeItem(ListNode *n0) { + if (!n0->next) + return; + // n0 -> P -> n1 + ListNode *P = n0->next; + ListNode *n1 = P->next; + n0->next = n1; + // Free memory + free(P); +} + +/* Access the node at index index in the linked list */ +ListNode *access(ListNode *head, int index) { + for (int i = 0; i < index; i++) { + if (head == NULL) + return NULL; + head = head->next; + } + return head; +} + +/* Find the first node with value target in the linked list */ +int find(ListNode *head, int target) { + int index = 0; + while (head) { + if (head->val == target) + return index; + head = head->next; + index++; + } + return -1; +} + +/* Driver Code */ +int main() { + /* Initialize linked list */ + // Initialize each node + ListNode *n0 = newListNode(1); + ListNode *n1 = newListNode(3); + ListNode *n2 = newListNode(2); + ListNode *n3 = newListNode(5); + ListNode *n4 = newListNode(4); + // Build references between nodes + n0->next = n1; + n1->next = n2; + n2->next = n3; + n3->next = n4; + printf("Initialized linked list is\r\n"); + printLinkedList(n0); + + /* Insert node */ + insert(n0, newListNode(0)); + printf("Linked list after node insertion is\r\n"); + printLinkedList(n0); + + /* Remove node */ + removeItem(n0); + printf("Linked list after node deletion is\r\n"); + printLinkedList(n0); + + /* Access node */ + ListNode *node = access(n0, 3); + printf("Value of node at index 3 in linked list = %d\r\n", node->val); + + /* Search node */ + int index = find(n0, 2); + printf("Index of node with value 2 in linked list = %d\r\n", index); + + // Free memory + freeMemoryLinkedList(n0); + return 0; +} diff --git a/en/codes/c/chapter_array_and_linkedlist/my_list.c b/en/codes/c/chapter_array_and_linkedlist/my_list.c new file mode 100644 index 000000000..52d22b857 --- /dev/null +++ b/en/codes/c/chapter_array_and_linkedlist/my_list.c @@ -0,0 +1,163 @@ +/** + * File: my_list.c + * Created Time: 2023-01-12 + * Author: Zero (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* List class */ +typedef struct { + int *arr; // Array (stores list elements) + int capacity; // List capacity + int size; // List size + int extendRatio; // List expansion multiplier +} MyList; + +void extendCapacity(MyList *nums); + +/* Constructor */ +MyList *newMyList() { + MyList *nums = malloc(sizeof(MyList)); + nums->capacity = 10; + nums->arr = malloc(sizeof(int) * nums->capacity); + nums->size = 0; + nums->extendRatio = 2; + return nums; +} + +/* Destructor */ +void delMyList(MyList *nums) { + free(nums->arr); + free(nums); +} + +/* Get list length */ +int size(MyList *nums) { + return nums->size; +} + +/* Get list capacity */ +int capacity(MyList *nums) { + return nums->capacity; +} + +/* Update element */ +int get(MyList *nums, int index) { + assert(index >= 0 && index < nums->size); + return nums->arr[index]; +} + +/* Add elements at the end */ +void set(MyList *nums, int index, int num) { + assert(index >= 0 && index < nums->size); + nums->arr[index] = num; +} + +/* Direct traversal of list elements */ +void add(MyList *nums, int num) { + if (size(nums) == capacity(nums)) { + extendCapacity(nums); // Expand capacity + } + nums->arr[size(nums)] = num; + nums->size++; +} + +/* Sort list */ +void insert(MyList *nums, int index, int num) { + assert(index >= 0 && index < size(nums)); + // When the number of elements exceeds capacity, trigger the extension mechanism + if (size(nums) == capacity(nums)) { + extendCapacity(nums); // Expand capacity + } + for (int i = size(nums); i > index; --i) { + nums->arr[i] = nums->arr[i - 1]; + } + nums->arr[index] = num; + nums->size++; +} + +/* Remove element */ +// Note: stdio.h occupies the remove keyword +int removeItem(MyList *nums, int index) { + assert(index >= 0 && index < size(nums)); + int num = nums->arr[index]; + for (int i = index; i < size(nums) - 1; i++) { + nums->arr[i] = nums->arr[i + 1]; + } + nums->size--; + return num; +} + +/* Driver Code */ +void extendCapacity(MyList *nums) { + // Allocate space first + int newCapacity = capacity(nums) * nums->extendRatio; + int *extend = (int *)malloc(sizeof(int) * newCapacity); + int *temp = nums->arr; + + // Copy old data to new data + for (int i = 0; i < size(nums); i++) + extend[i] = nums->arr[i]; + + // Free old data + free(temp); + + // Update new data + nums->arr = extend; + nums->capacity = newCapacity; +} + +/* Convert list to Array for printing */ +int *toArray(MyList *nums) { + return nums->arr; +} + +/* Driver Code */ +int main() { + /* Initialize list */ + MyList *nums = newMyList(); + /* Direct traversal of list elements */ + add(nums, 1); + add(nums, 3); + add(nums, 2); + add(nums, 5); + add(nums, 4); + printf("List nums = "); + printArray(toArray(nums), size(nums)); + printf("Capacity = %d, Length = %d\n", capacity(nums), size(nums)); + + /* Sort list */ + insert(nums, 3, 6); + printf("Insert number 6 at index 3, resulting in nums = "); + printArray(toArray(nums), size(nums)); + + /* Remove element */ + removeItem(nums, 3); + printf("Remove element at index 3, resulting in nums = "); + printArray(toArray(nums), size(nums)); + + /* Update element */ + int num = get(nums, 1); + printf("Access element at index 1, get num = %d\n", num); + + /* Add elements at the end */ + set(nums, 1, 0); + printf("Update element at index 1 to 0, resulting in nums = "); + printArray(toArray(nums), size(nums)); + + /* Test capacity expansion mechanism */ + for (int i = 0; i < 10; i++) { + // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + add(nums, i); + } + + printf("List nums after expansion = "); + printArray(toArray(nums), size(nums)); + printf("Capacity = %d, Length = %d\n", capacity(nums), size(nums)); + + /* Free allocated memory */ + delMyList(nums); + + return 0; +} diff --git a/en/codes/c/chapter_backtracking/CMakeLists.txt b/en/codes/c/chapter_backtracking/CMakeLists.txt new file mode 100644 index 000000000..70161b6dd --- /dev/null +++ b/en/codes/c/chapter_backtracking/CMakeLists.txt @@ -0,0 +1,10 @@ +add_executable(permutations_i permutations_i.c) +add_executable(permutations_ii permutations_ii.c) +add_executable(preorder_traversal_i_compact preorder_traversal_i_compact.c) +add_executable(preorder_traversal_ii_compact preorder_traversal_ii_compact.c) +add_executable(preorder_traversal_iii_compact preorder_traversal_iii_compact.c) +add_executable(preorder_traversal_iii_template preorder_traversal_iii_template.c) +add_executable(subset_sum_i_naive subset_sum_i_naive.c) +add_executable(subset_sum_i subset_sum_i.c) +add_executable(subset_sum_ii subset_sum_ii.c) +add_executable(n_queens n_queens.c) \ No newline at end of file diff --git a/en/codes/c/chapter_backtracking/n_queens.c b/en/codes/c/chapter_backtracking/n_queens.c new file mode 100644 index 000000000..4acd49ab3 --- /dev/null +++ b/en/codes/c/chapter_backtracking/n_queens.c @@ -0,0 +1,95 @@ +/** + * File : n_queens.c + * Created Time: 2023-09-25 + * Author : lucas (superrat6@gmail.com) + */ + +#include "../utils/common.h" + +#define MAX_SIZE 100 + +/* Backtracking algorithm: N queens */ +void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int *resSize, bool cols[MAX_SIZE], + bool diags1[2 * MAX_SIZE - 1], bool diags2[2 * MAX_SIZE - 1]) { + // When all rows are placed, record the solution + if (row == n) { + res[*resSize] = (char **)malloc(sizeof(char *) * n); + for (int i = 0; i < n; ++i) { + res[*resSize][i] = (char *)malloc(sizeof(char) * (n + 1)); + strcpy(res[*resSize][i], state[i]); + } + (*resSize)++; + return; + } + // Traverse all columns + for (int col = 0; col < n; col++) { + // Calculate the main diagonal and anti-diagonal corresponding to this cell + int diag1 = row - col + n - 1; + int diag2 = row + col; + // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { + // Attempt: place the queen in this cell + state[row][col] = 'Q'; + cols[col] = diags1[diag1] = diags2[diag2] = true; + // Place the next row + backtrack(row + 1, n, state, res, resSize, cols, diags1, diags2); + // Backtrack: restore this cell to an empty cell + state[row][col] = '#'; + cols[col] = diags1[diag1] = diags2[diag2] = false; + } + } +} + +/* Solve N queens */ +char ***nQueens(int n, int *returnSize) { + char state[MAX_SIZE][MAX_SIZE]; + // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + state[i][j] = '#'; + } + state[i][n] = '\0'; + } + bool cols[MAX_SIZE] = {false}; // Record whether there is a queen in the column + bool diags1[2 * MAX_SIZE - 1] = {false}; // Record whether there is a queen on the main diagonal + bool diags2[2 * MAX_SIZE - 1] = {false}; // Record whether there is a queen on the anti-diagonal + + char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE); + *returnSize = 0; + backtrack(0, n, state, res, returnSize, cols, diags1, diags2); + return res; +} + +/* Driver Code */ +int main() { + int n = 4; + int returnSize; + char ***res = nQueens(n, &returnSize); + + printf("Input board size is %d\n", n); + printf("Total queen placement solutions: %d\n", returnSize); + for (int i = 0; i < returnSize; ++i) { + for (int j = 0; j < n; ++j) { + printf("["); + for (int k = 0; res[i][j][k] != '\0'; ++k) { + printf("%c", res[i][j][k]); + if (res[i][j][k + 1] != '\0') { + printf(", "); + } + } + printf("]\n"); + } + printf("---------------------\n"); + } + + // Free memory + for (int i = 0; i < returnSize; ++i) { + for (int j = 0; j < n; ++j) { + free(res[i][j]); + } + free(res[i]); + } + free(res); + + return 0; +} diff --git a/en/codes/c/chapter_backtracking/permutations_i.c b/en/codes/c/chapter_backtracking/permutations_i.c new file mode 100644 index 000000000..48a6d8451 --- /dev/null +++ b/en/codes/c/chapter_backtracking/permutations_i.c @@ -0,0 +1,79 @@ +/** + * File: permutations_i.c + * Created Time: 2023-06-04 + * Author: Gonglja (glj0@outlook.com), krahets (krahets@163.com) + */ + +#include "../utils/common.h" + +// Assume at most 1000 permutations +#define MAX_SIZE 1000 + +/* Backtracking algorithm: Permutations I */ +void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) { + // When the state length equals the number of elements, record the solution + if (stateSize == choicesSize) { + res[*resSize] = (int *)malloc(choicesSize * sizeof(int)); + for (int i = 0; i < choicesSize; i++) { + res[*resSize][i] = state[i]; + } + (*resSize)++; + return; + } + // Traverse all choices + for (int i = 0; i < choicesSize; i++) { + int choice = choices[i]; + // Pruning: do not allow repeated selection of elements + if (!selected[i]) { + // Attempt: make choice, update state + selected[i] = true; + state[stateSize] = choice; + // Proceed to the next round of selection + backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + } + } +} + +/* Permutations I */ +int **permutationsI(int *nums, int numsSize, int *returnSize) { + int *state = (int *)malloc(numsSize * sizeof(int)); + bool *selected = (bool *)malloc(numsSize * sizeof(bool)); + for (int i = 0; i < numsSize; i++) { + selected[i] = false; + } + int **res = (int **)malloc(MAX_SIZE * sizeof(int *)); + *returnSize = 0; + + backtrack(state, 0, nums, numsSize, selected, res, returnSize); + + free(state); + free(selected); + + return res; +} + +/* Driver Code */ +int main() { + int nums[] = {1, 2, 3}; + int numsSize = sizeof(nums) / sizeof(nums[0]); + int returnSize; + + int **res = permutationsI(nums, numsSize, &returnSize); + + printf("Input array nums = "); + printArray(nums, numsSize); + printf("\nAll permutations res = \n"); + for (int i = 0; i < returnSize; i++) { + printArray(res[i], numsSize); + } + + // Free memory + for (int i = 0; i < returnSize; i++) { + free(res[i]); + } + free(res); + + return 0; +} diff --git a/en/codes/c/chapter_backtracking/permutations_ii.c b/en/codes/c/chapter_backtracking/permutations_ii.c new file mode 100644 index 000000000..34073f779 --- /dev/null +++ b/en/codes/c/chapter_backtracking/permutations_ii.c @@ -0,0 +1,81 @@ +/** + * File: permutations_ii.c + * Created Time: 2023-10-17 + * Author: krahets (krahets@163.com) + */ + +#include "../utils/common.h" + +// Assume at most 1000 permutations, max element value 1000 +#define MAX_SIZE 1000 + +/* Backtracking algorithm: Permutations II */ +void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) { + // When the state length equals the number of elements, record the solution + if (stateSize == choicesSize) { + res[*resSize] = (int *)malloc(choicesSize * sizeof(int)); + for (int i = 0; i < choicesSize; i++) { + res[*resSize][i] = state[i]; + } + (*resSize)++; + return; + } + // Traverse all choices + bool duplicated[MAX_SIZE] = {false}; + for (int i = 0; i < choicesSize; i++) { + int choice = choices[i]; + // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if (!selected[i] && !duplicated[choice]) { + // Attempt: make choice, update state + duplicated[choice] = true; // Record the selected element value + selected[i] = true; + state[stateSize] = choice; + // Proceed to the next round of selection + backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + } + } +} + +/* Permutations II */ +int **permutationsII(int *nums, int numsSize, int *returnSize) { + int *state = (int *)malloc(numsSize * sizeof(int)); + bool *selected = (bool *)malloc(numsSize * sizeof(bool)); + for (int i = 0; i < numsSize; i++) { + selected[i] = false; + } + int **res = (int **)malloc(MAX_SIZE * sizeof(int *)); + *returnSize = 0; + + backtrack(state, 0, nums, numsSize, selected, res, returnSize); + + free(state); + free(selected); + + return res; +} + +/* Driver Code */ +int main() { + int nums[] = {1, 1, 2}; + int numsSize = sizeof(nums) / sizeof(nums[0]); + int returnSize; + + int **res = permutationsII(nums, numsSize, &returnSize); + + printf("Input array nums = "); + printArray(nums, numsSize); + printf("\nAll permutations res = \n"); + for (int i = 0; i < returnSize; i++) { + printArray(res[i], numsSize); + } + + // Free memory + for (int i = 0; i < returnSize; i++) { + free(res[i]); + } + free(res); + + return 0; +} diff --git a/en/codes/c/chapter_backtracking/preorder_traversal_i_compact.c b/en/codes/c/chapter_backtracking/preorder_traversal_i_compact.c new file mode 100644 index 000000000..aa969fabd --- /dev/null +++ b/en/codes/c/chapter_backtracking/preorder_traversal_i_compact.c @@ -0,0 +1,49 @@ +/** + * File: preorder_traversal_i_compact.c + * Created Time: 2023-05-10 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +// Assume result length not exceeding 100 +#define MAX_SIZE 100 + +TreeNode *res[MAX_SIZE]; +int resSize = 0; + +/* Preorder traversal: Example 1 */ +void preOrder(TreeNode *root) { + if (root == NULL) { + return; + } + if (root->val == 7) { + // Record solution + res[resSize++] = root; + } + preOrder(root->left); + preOrder(root->right); +} + +/* Driver Code */ +int main() { + int arr[] = {1, 7, 3, 4, 5, 6, 7}; + TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0])); + printf("\nInitialize binary tree\n"); + printTree(root); + + // Preorder traversal + preOrder(root); + + printf("\nOutput all nodes with value 7\n"); + int *vals = malloc(resSize * sizeof(int)); + for (int i = 0; i < resSize; i++) { + vals[i] = res[i]->val; + } + printArray(vals, resSize); + + // Free memory + freeMemoryTree(root); + free(vals); + return 0; +} diff --git a/en/codes/c/chapter_backtracking/preorder_traversal_ii_compact.c b/en/codes/c/chapter_backtracking/preorder_traversal_ii_compact.c new file mode 100644 index 000000000..f85b6f45a --- /dev/null +++ b/en/codes/c/chapter_backtracking/preorder_traversal_ii_compact.c @@ -0,0 +1,61 @@ +/** + * File: preorder_traversal_ii_compact.c + * Created Time: 2023-05-28 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +// Assume path and result length not exceeding 100 +#define MAX_SIZE 100 +#define MAX_RES_SIZE 100 + +TreeNode *path[MAX_SIZE]; +TreeNode *res[MAX_RES_SIZE][MAX_SIZE]; +int pathSize = 0, resSize = 0; + +/* Preorder traversal: Example 2 */ +void preOrder(TreeNode *root) { + if (root == NULL) { + return; + } + // Attempt + path[pathSize++] = root; + if (root->val == 7) { + // Record solution + for (int i = 0; i < pathSize; ++i) { + res[resSize][i] = path[i]; + } + resSize++; + } + preOrder(root->left); + preOrder(root->right); + // Backtrack + pathSize--; +} + +/* Driver Code */ +int main() { + int arr[] = {1, 7, 3, 4, 5, 6, 7}; + TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0])); + printf("\nInitialize binary tree\n"); + printTree(root); + + // Preorder traversal + preOrder(root); + + printf("\nOutput all paths from root to node 7\n"); + for (int i = 0; i < resSize; ++i) { + int *vals = malloc(MAX_SIZE * sizeof(int)); + int size = 0; + for (int j = 0; res[i][j] != NULL; ++j) { + vals[size++] = res[i][j]->val; + } + printArray(vals, size); + free(vals); + } + + // Free memory + freeMemoryTree(root); + return 0; +} diff --git a/en/codes/c/chapter_backtracking/preorder_traversal_iii_compact.c b/en/codes/c/chapter_backtracking/preorder_traversal_iii_compact.c new file mode 100644 index 000000000..76e82f1ad --- /dev/null +++ b/en/codes/c/chapter_backtracking/preorder_traversal_iii_compact.c @@ -0,0 +1,62 @@ +/** + * File: preorder_traversal_iii_compact.c + * Created Time: 2023-06-04 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +// Assume path and result length not exceeding 100 +#define MAX_SIZE 100 +#define MAX_RES_SIZE 100 + +TreeNode *path[MAX_SIZE]; +TreeNode *res[MAX_RES_SIZE][MAX_SIZE]; +int pathSize = 0, resSize = 0; + +/* Preorder traversal: Example 3 */ +void preOrder(TreeNode *root) { + // Pruning + if (root == NULL || root->val == 3) { + return; + } + // Attempt + path[pathSize++] = root; + if (root->val == 7) { + // Record solution + for (int i = 0; i < pathSize; i++) { + res[resSize][i] = path[i]; + } + resSize++; + } + preOrder(root->left); + preOrder(root->right); + // Backtrack + pathSize--; +} + +/* Driver Code */ +int main() { + int arr[] = {1, 7, 3, 4, 5, 6, 7}; + TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0])); + printf("\nInitialize binary tree\n"); + printTree(root); + + // Preorder traversal + preOrder(root); + + printf("\nOutput all paths from root to node 7, excluding nodes with value 3\n"); + for (int i = 0; i < resSize; ++i) { + int *vals = malloc(MAX_SIZE * sizeof(int)); + int size = 0; + for (int j = 0; res[i][j] != NULL; ++j) { + vals[size++] = res[i][j]->val; + } + printArray(vals, size); + free(vals); + } + + // Free memory + freeMemoryTree(root); + return 0; +} diff --git a/en/codes/c/chapter_backtracking/preorder_traversal_iii_template.c b/en/codes/c/chapter_backtracking/preorder_traversal_iii_template.c new file mode 100644 index 000000000..40dfaed85 --- /dev/null +++ b/en/codes/c/chapter_backtracking/preorder_traversal_iii_template.c @@ -0,0 +1,93 @@ +/** + * File: preorder_traversal_iii_template.c + * Created Time: 2023-06-04 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +// Assume path and result length not exceeding 100 +#define MAX_SIZE 100 +#define MAX_RES_SIZE 100 + +TreeNode *path[MAX_SIZE]; +TreeNode *res[MAX_RES_SIZE][MAX_SIZE]; +int pathSize = 0, resSize = 0; + +/* Check if the current state is a solution */ +bool isSolution(void) { + return pathSize > 0 && path[pathSize - 1]->val == 7; +} + +/* Record solution */ +void recordSolution(void) { + for (int i = 0; i < pathSize; i++) { + res[resSize][i] = path[i]; + } + resSize++; +} + +/* Check if the choice is valid under the current state */ +bool isValid(TreeNode *choice) { + return choice != NULL && choice->val != 3; +} + +/* Update state */ +void makeChoice(TreeNode *choice) { + path[pathSize++] = choice; +} + +/* Restore state */ +void undoChoice(void) { + pathSize--; +} + +/* Backtracking algorithm: Example 3 */ +void backtrack(TreeNode *choices[2]) { + // Check if it is a solution + if (isSolution()) { + // Record solution + recordSolution(); + } + // Traverse all choices + for (int i = 0; i < 2; i++) { + TreeNode *choice = choices[i]; + // Pruning: check if the choice is valid + if (isValid(choice)) { + // Attempt: make choice, update state + makeChoice(choice); + // Proceed to the next round of selection + TreeNode *nextChoices[2] = {choice->left, choice->right}; + backtrack(nextChoices); + // Backtrack: undo choice, restore to previous state + undoChoice(); + } + } +} + +/* Driver Code */ +int main() { + int arr[] = {1, 7, 3, 4, 5, 6, 7}; + TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0])); + printf("\nInitialize binary tree\n"); + printTree(root); + + // Backtracking algorithm + TreeNode *choices[2] = {root, NULL}; + backtrack(choices); + + printf("\nOutput all paths from root to node 7, excluding nodes with value 3\n"); + for (int i = 0; i < resSize; ++i) { + int *vals = malloc(MAX_SIZE * sizeof(int)); + int size = 0; + for (int j = 0; res[i][j] != NULL; ++j) { + vals[size++] = res[i][j]->val; + } + printArray(vals, size); + free(vals); + } + + // Free memory + freeMemoryTree(root); + return 0; +} diff --git a/en/codes/c/chapter_backtracking/subset_sum_i.c b/en/codes/c/chapter_backtracking/subset_sum_i.c new file mode 100644 index 000000000..1e34efb5c --- /dev/null +++ b/en/codes/c/chapter_backtracking/subset_sum_i.c @@ -0,0 +1,78 @@ +/** + * File: subset_sum_i.c + * Created Time: 2023-07-29 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +#define MAX_SIZE 100 +#define MAX_RES_SIZE 100 + +// State (subset) +int state[MAX_SIZE]; +int stateSize = 0; + +// Result list (subset list) +int res[MAX_RES_SIZE][MAX_SIZE]; +int resColSizes[MAX_RES_SIZE]; +int resSize = 0; + +/* Backtracking algorithm: Subset sum I */ +void backtrack(int target, int *choices, int choicesSize, int start) { + // When the subset sum equals target, record the solution + if (target == 0) { + for (int i = 0; i < stateSize; ++i) { + res[resSize][i] = state[i]; + } + resColSizes[resSize++] = stateSize; + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + for (int i = start; i < choicesSize; i++) { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if (target - choices[i] < 0) { + break; + } + // Attempt: make choice, update target, start + state[stateSize] = choices[i]; + stateSize++; + // Proceed to the next round of selection + backtrack(target - choices[i], choices, choicesSize, i); + // Backtrack: undo choice, restore to previous state + stateSize--; + } +} + +/* Comparison function */ +int cmp(const void *a, const void *b) { + return (*(int *)a - *(int *)b); +} + +/* Solve subset sum I */ +void subsetSumI(int *nums, int numsSize, int target) { + qsort(nums, numsSize, sizeof(int), cmp); // Sort nums + int start = 0; // Start point for traversal + backtrack(target, nums, numsSize, start); +} + +/* Driver Code */ +int main() { + int nums[] = {3, 4, 5}; + int numsSize = sizeof(nums) / sizeof(nums[0]); + int target = 9; + + subsetSumI(nums, numsSize, target); + + printf("Input array nums = "); + printArray(nums, numsSize); + printf("target = %d\n", target); + printf("All subsets with sum equal to %d res = \n", target); + for (int i = 0; i < resSize; ++i) { + printArray(res[i], resColSizes[i]); + } + + return 0; +} diff --git a/en/codes/c/chapter_backtracking/subset_sum_i_naive.c b/en/codes/c/chapter_backtracking/subset_sum_i_naive.c new file mode 100644 index 000000000..7621bf7d6 --- /dev/null +++ b/en/codes/c/chapter_backtracking/subset_sum_i_naive.c @@ -0,0 +1,69 @@ +/** + * File: subset_sum_i_naive.c + * Created Time: 2023-07-28 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +#define MAX_SIZE 100 +#define MAX_RES_SIZE 100 + +// State (subset) +int state[MAX_SIZE]; +int stateSize = 0; + +// Result list (subset list) +int res[MAX_RES_SIZE][MAX_SIZE]; +int resColSizes[MAX_RES_SIZE]; +int resSize = 0; + +/* Backtracking algorithm: Subset sum I */ +void backtrack(int target, int total, int *choices, int choicesSize) { + // When the subset sum equals target, record the solution + if (total == target) { + for (int i = 0; i < stateSize; i++) { + res[resSize][i] = state[i]; + } + resColSizes[resSize++] = stateSize; + return; + } + // Traverse all choices + for (int i = 0; i < choicesSize; i++) { + // Pruning: if the subset sum exceeds target, skip this choice + if (total + choices[i] > target) { + continue; + } + // Attempt: make choice, update element sum total + state[stateSize++] = choices[i]; + // Proceed to the next round of selection + backtrack(target, total + choices[i], choices, choicesSize); + // Backtrack: undo choice, restore to previous state + stateSize--; + } +} + +/* Solve subset sum I (including duplicate subsets) */ +void subsetSumINaive(int *nums, int numsSize, int target) { + resSize = 0; // Initialize solution count to 0 + backtrack(target, 0, nums, numsSize); +} + +/* Driver Code */ +int main() { + int nums[] = {3, 4, 5}; + int numsSize = sizeof(nums) / sizeof(nums[0]); + int target = 9; + + subsetSumINaive(nums, numsSize, target); + + printf("Input array nums = "); + printArray(nums, numsSize); + printf("target = %d\n", target); + printf("All subsets with sum equal to %d res = \n", target); + for (int i = 0; i < resSize; i++) { + printArray(res[i], resColSizes[i]); + } + + return 0; +} diff --git a/en/codes/c/chapter_backtracking/subset_sum_ii.c b/en/codes/c/chapter_backtracking/subset_sum_ii.c new file mode 100644 index 000000000..5b2399d65 --- /dev/null +++ b/en/codes/c/chapter_backtracking/subset_sum_ii.c @@ -0,0 +1,83 @@ +/** + * File: subset_sum_ii.c + * Created Time: 2023-07-29 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +#define MAX_SIZE 100 +#define MAX_RES_SIZE 100 + +// State (subset) +int state[MAX_SIZE]; +int stateSize = 0; + +// Result list (subset list) +int res[MAX_RES_SIZE][MAX_SIZE]; +int resColSizes[MAX_RES_SIZE]; +int resSize = 0; + +/* Backtracking algorithm: Subset sum II */ +void backtrack(int target, int *choices, int choicesSize, int start) { + // When the subset sum equals target, record the solution + if (target == 0) { + for (int i = 0; i < stateSize; i++) { + res[resSize][i] = state[i]; + } + resColSizes[resSize++] = stateSize; + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + for (int i = start; i < choicesSize; i++) { + // Pruning 1: Skip if subset sum exceeds target + if (target - choices[i] < 0) { + continue; + } + // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + if (i > start && choices[i] == choices[i - 1]) { + continue; + } + // Attempt: make choice, update target, start + state[stateSize] = choices[i]; + stateSize++; + // Proceed to the next round of selection + backtrack(target - choices[i], choices, choicesSize, i + 1); + // Backtrack: undo choice, restore to previous state + stateSize--; + } +} + +/* Comparison function */ +int cmp(const void *a, const void *b) { + return (*(int *)a - *(int *)b); +} + +/* Solve subset sum II */ +void subsetSumII(int *nums, int numsSize, int target) { + // Sort nums + qsort(nums, numsSize, sizeof(int), cmp); + // Start backtracking + backtrack(target, nums, numsSize, 0); +} + +/* Driver Code */ +int main() { + int nums[] = {4, 4, 5}; + int numsSize = sizeof(nums) / sizeof(nums[0]); + int target = 9; + + subsetSumII(nums, numsSize, target); + + printf("Input array nums = "); + printArray(nums, numsSize); + printf("target = %d\n", target); + printf("All subsets with sum equal to %d res = \n", target); + for (int i = 0; i < resSize; ++i) { + printArray(res[i], resColSizes[i]); + } + + return 0; +} diff --git a/en/codes/c/chapter_computational_complexity/CMakeLists.txt b/en/codes/c/chapter_computational_complexity/CMakeLists.txt new file mode 100644 index 000000000..dcfa063c3 --- /dev/null +++ b/en/codes/c/chapter_computational_complexity/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(iteration iteration.c) +add_executable(recursion recursion.c) +add_executable(time_complexity time_complexity.c) +add_executable(worst_best_time_complexity worst_best_time_complexity.c) +add_executable(space_complexity space_complexity.c) diff --git a/en/codes/c/chapter_computational_complexity/iteration.c b/en/codes/c/chapter_computational_complexity/iteration.c new file mode 100644 index 000000000..71f099a3b --- /dev/null +++ b/en/codes/c/chapter_computational_complexity/iteration.c @@ -0,0 +1,81 @@ +/** + * File: iteration.c + * Created Time: 2023-09-09 + * Author: Gonglja (glj0@outlook.com), MwumLi (mwumli@hotmail.com) + */ + +#include "../utils/common.h" + +/* for loop */ +int forLoop(int n) { + int res = 0; + // Sum 1, 2, ..., n-1, n + for (int i = 1; i <= n; i++) { + res += i; + } + return res; +} + +/* while loop */ +int whileLoop(int n) { + int res = 0; + int i = 1; // Initialize condition variable + // Sum 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // Update condition variable + } + return res; +} + +/* while loop (two updates) */ +int whileLoopII(int n) { + int res = 0; + int i = 1; // Initialize condition variable + // Sum 1, 4, 10, ... + while (i <= n) { + res += i; + // Update condition variable + i++; + i *= 2; + } + return res; +} + +/* Nested for loop */ +char *nestedForLoop(int n) { + // n * n is the number of points, "(i, j), " string max length is 6+10*2, plus extra space for null character \0 + int size = n * n * 26 + 1; + char *res = malloc(size * sizeof(char)); + // Loop i = 1, 2, ..., n-1, n + for (int i = 1; i <= n; i++) { + // Loop j = 1, 2, ..., n-1, n + for (int j = 1; j <= n; j++) { + char tmp[26]; + snprintf(tmp, sizeof(tmp), "(%d, %d), ", i, j); + strncat(res, tmp, size - strlen(res) - 1); + } + } + return res; +} + +/* Driver Code */ +int main() { + int n = 5; + int res; + + res = forLoop(n); + printf("\nFor loop sum result res = %d\n", res); + + res = whileLoop(n); + printf("\nWhile loop sum result res = %d\n", res); + + res = whileLoopII(n); + printf("\nWhile loop (two updates) sum result res = %d\n", res); + + char *resStr = nestedForLoop(n); + printf("\nNested for loop traversal result %s\r\n", resStr); + free(resStr); + + return 0; +} diff --git a/en/codes/c/chapter_computational_complexity/recursion.c b/en/codes/c/chapter_computational_complexity/recursion.c new file mode 100644 index 000000000..e11460559 --- /dev/null +++ b/en/codes/c/chapter_computational_complexity/recursion.c @@ -0,0 +1,77 @@ +/** + * File: recursion.c + * Created Time: 2023-09-09 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Recursion */ +int recur(int n) { + // Termination condition + if (n == 1) + return 1; + // Recurse: recursive call + int res = recur(n - 1); + // Return: return result + return n + res; +} + +/* Simulate recursion using iteration */ +int forLoopRecur(int n) { + int stack[1000]; // Use a large array to simulate stack + int top = -1; // Stack top index + int res = 0; + // Recurse: recursive call + for (int i = n; i > 0; i--) { + // Simulate "recurse" with "push" + stack[1 + top++] = i; + } + // Return: return result + while (top >= 0) { + // Simulate "return" with "pop" + res += stack[top--]; + } + // res = 1+2+3+...+n + return res; +} + +/* Tail recursion */ +int tailRecur(int n, int res) { + // Termination condition + if (n == 0) + return res; + // Tail recursive call + return tailRecur(n - 1, res + n); +} + +/* Fibonacci sequence: recursion */ +int fib(int n) { + // Termination condition f(1) = 0, f(2) = 1 + if (n == 1 || n == 2) + return n - 1; + // Recursive call f(n) = f(n-1) + f(n-2) + int res = fib(n - 1) + fib(n - 2); + // Return result f(n) + return res; +} + +/* Driver Code */ +int main() { + int n = 5; + int res; + + res = recur(n); + printf("\nRecursion sum result res = %d\n", res); + + res = forLoopRecur(n); + printf("\nUsing iteration to simulate recursion sum result res = %d\n", res); + + res = tailRecur(n, 0); + printf("\nTail recursion sum result res = %d\n", res); + + res = fib(n); + printf("\nThe %dth Fibonacci number is %d\n", n, res); + + return 0; +} diff --git a/en/codes/c/chapter_computational_complexity/space_complexity.c b/en/codes/c/chapter_computational_complexity/space_complexity.c new file mode 100644 index 000000000..35efeca76 --- /dev/null +++ b/en/codes/c/chapter_computational_complexity/space_complexity.c @@ -0,0 +1,141 @@ +/** + * File: space_complexity.c + * Created Time: 2023-04-15 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Function */ +int func() { + // Perform some operations + return 0; +} + +/* Constant order */ +void constant(int n) { + // Constants, variables, objects occupy O(1) space + const int a = 0; + int b = 0; + int nums[1000]; + ListNode *node = newListNode(0); + free(node); + // Variables in the loop occupy O(1) space + for (int i = 0; i < n; i++) { + int c = 0; + } + // Functions in the loop occupy O(1) space + for (int i = 0; i < n; i++) { + func(); + } +} + +/* Hash table */ +typedef struct { + int key; + int val; + UT_hash_handle hh; // Implemented using uthash.h +} HashTable; + +/* Linear order */ +void linear(int n) { + // Array of length n uses O(n) space + int *nums = malloc(sizeof(int) * n); + free(nums); + + // A list of length n occupies O(n) space + ListNode **nodes = malloc(sizeof(ListNode *) * n); + for (int i = 0; i < n; i++) { + nodes[i] = newListNode(i); + } + // Memory release + for (int i = 0; i < n; i++) { + free(nodes[i]); + } + free(nodes); + + // A hash table of length n occupies O(n) space + HashTable *h = NULL; + for (int i = 0; i < n; i++) { + HashTable *tmp = malloc(sizeof(HashTable)); + tmp->key = i; + tmp->val = i; + HASH_ADD_INT(h, key, tmp); + } + + // Memory release + HashTable *curr, *tmp; + HASH_ITER(hh, h, curr, tmp) { + HASH_DEL(h, curr); + free(curr); + } +} + +/* Linear order (recursive implementation) */ +void linearRecur(int n) { + printf("Recursion n = %d\r\n", n); + if (n == 1) + return; + linearRecur(n - 1); +} + +/* Exponential order */ +void quadratic(int n) { + // 2D list uses O(n^2) space + int **numMatrix = malloc(sizeof(int *) * n); + for (int i = 0; i < n; i++) { + int *tmp = malloc(sizeof(int) * n); + for (int j = 0; j < n; j++) { + tmp[j] = 0; + } + numMatrix[i] = tmp; + } + + // Memory release + for (int i = 0; i < n; i++) { + free(numMatrix[i]); + } + free(numMatrix); +} + +/* Quadratic order (recursive implementation) */ +int quadraticRecur(int n) { + if (n <= 0) + return 0; + int *nums = malloc(sizeof(int) * n); + printf("In recursion n = %d, nums length = %d\r\n", n, n); + int res = quadraticRecur(n - 1); + free(nums); + return res; +} + +/* Driver Code */ +TreeNode *buildTree(int n) { + if (n == 0) + return NULL; + TreeNode *root = newTreeNode(0); + root->left = buildTree(n - 1); + root->right = buildTree(n - 1); + return root; +} + +/* Driver Code */ +int main() { + int n = 5; + // Constant order + constant(n); + // Linear order + linear(n); + linearRecur(n); + // Exponential order + quadratic(n); + quadraticRecur(n); + // Exponential order + TreeNode *root = buildTree(n); + printTree(root); + + // Free memory + freeMemoryTree(root); + + return 0; +} diff --git a/en/codes/c/chapter_computational_complexity/time_complexity.c b/en/codes/c/chapter_computational_complexity/time_complexity.c new file mode 100644 index 000000000..2094cfa70 --- /dev/null +++ b/en/codes/c/chapter_computational_complexity/time_complexity.c @@ -0,0 +1,179 @@ +/** + * File: time_complexity.c + * Created Time: 2023-01-03 + * Author: codingonion (coderonion@gmail.com) + */ + +#include "../utils/common.h" + +/* Constant order */ +int constant(int n) { + int count = 0; + int size = 100000; + int i = 0; + for (int i = 0; i < size; i++) { + count++; + } + return count; +} + +/* Linear order */ +int linear(int n) { + int count = 0; + for (int i = 0; i < n; i++) { + count++; + } + return count; +} + +/* Linear order (traversing array) */ +int arrayTraversal(int *nums, int n) { + int count = 0; + // Number of iterations is proportional to the array length + for (int i = 0; i < n; i++) { + count++; + } + return count; +} + +/* Exponential order */ +int quadratic(int n) { + int count = 0; + // Number of iterations is quadratically related to the data size n + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; +} + +/* Quadratic order (bubble sort) */ +int bubbleSort(int *nums, int n) { + int count = 0; // Counter + // Outer loop: unsorted range is [0, i] + for (int i = n - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // Element swap includes 3 unit operations + } + } + } + return count; +} + +/* Exponential order (loop implementation) */ +int exponential(int n) { + int count = 0; + int bas = 1; + // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + for (int i = 0; i < n; i++) { + for (int j = 0; j < bas; j++) { + count++; + } + bas *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; +} + +/* Exponential order (recursive implementation) */ +int expRecur(int n) { + if (n == 1) + return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; +} + +/* Logarithmic order (loop implementation) */ +int logarithmic(int n) { + int count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; +} + +/* Logarithmic order (recursive implementation) */ +int logRecur(int n) { + if (n <= 1) + return 0; + return logRecur(n / 2) + 1; +} + +/* Linearithmic order */ +int linearLogRecur(int n) { + if (n <= 1) + return 1; + int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (int i = 0; i < n; i++) { + count++; + } + return count; +} + +/* Factorial order (recursive implementation) */ +int factorialRecur(int n) { + if (n == 0) + return 1; + int count = 0; + for (int i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; +} + +/* Driver Code */ +int main(int argc, char *argv[]) { + // You can modify n to run and observe the trend of the number of operations for various complexities + int n = 8; + printf("Input data size n = %d\n", n); + + int count = constant(n); + printf("Constant-time operations count = %d\n", count); + + count = linear(n); + printf("Linear-time operations count = %d\n", count); + // Allocate heap memory (create 1D variable-length array: n elements of type int) + int *nums = (int *)malloc(n * sizeof(int)); + count = arrayTraversal(nums, n); + printf("Linear-time (array traversal) operations count = %d\n", count); + + count = quadratic(n); + printf("Quadratic-time operations count = %d\n", count); + for (int i = 0; i < n; i++) { + nums[i] = n - i; // [n,n-1,...,2,1] + } + count = bubbleSort(nums, n); + printf("Quadratic-time (bubble sort) operations count = %d\n", count); + + count = exponential(n); + printf("Exponential-time (iterative) operations count = %d\n", count); + count = expRecur(n); + printf("Exponential-time (recursive) operations count = %d\n", count); + + count = logarithmic(n); + printf("Logarithmic-time (iterative) operations count = %d\n", count); + count = logRecur(n); + printf("Logarithmic-time (recursive) operations count = %d\n", count); + + count = linearLogRecur(n); + printf("Linearithmic-time (recursive) operations count = %d\n", count); + + count = factorialRecur(n); + printf("Factorial-time (recursive) operations count = %d\n", count); + + // Free heap memory + if (nums != NULL) { + free(nums); + nums = NULL; + } + getchar(); + + return 0; +} diff --git a/en/codes/c/chapter_computational_complexity/worst_best_time_complexity.c b/en/codes/c/chapter_computational_complexity/worst_best_time_complexity.c new file mode 100644 index 000000000..068c59365 --- /dev/null +++ b/en/codes/c/chapter_computational_complexity/worst_best_time_complexity.c @@ -0,0 +1,57 @@ +/** + * File: worst_best_time_complexity.c + * Created Time: 2023-01-03 + * Author: codingonion (coderonion@gmail.com) + */ + +#include "../utils/common.h" + +/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +int *randomNumbers(int n) { + // Allocate heap memory (create 1D variable-length array: n elements of type int) + int *nums = (int *)malloc(n * sizeof(int)); + // Generate array nums = { 1, 2, 3, ..., n } + for (int i = 0; i < n; i++) { + nums[i] = i + 1; + } + // Randomly shuffle array elements + for (int i = n - 1; i > 0; i--) { + int j = rand() % (i + 1); + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + return nums; +} + +/* Find the index of number 1 in array nums */ +int findOne(int *nums, int n) { + for (int i = 0; i < n; i++) { + // When element 1 is at the head of the array, best time complexity O(1) is achieved + // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + if (nums[i] == 1) + return i; + } + return -1; +} + +/* Driver Code */ +int main(int argc, char *argv[]) { + // Initialize random seed + srand((unsigned int)time(NULL)); + for (int i = 0; i < 10; i++) { + int n = 100; + int *nums = randomNumbers(n); + int index = findOne(nums, n); + printf("\nArray [ 1, 2, ..., n ] after shuffling = "); + printArray(nums, n); + printf("Index of number 1 is %d\n", index); + // Free heap memory + if (nums != NULL) { + free(nums); + nums = NULL; + } + } + + return 0; +} diff --git a/en/codes/c/chapter_divide_and_conquer/CMakeLists.txt b/en/codes/c/chapter_divide_and_conquer/CMakeLists.txt new file mode 100644 index 000000000..e03b1c588 --- /dev/null +++ b/en/codes/c/chapter_divide_and_conquer/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(binary_search_recur binary_search_recur.c) +add_executable(build_tree build_tree.c) +add_executable(hanota hanota.c) diff --git a/en/codes/c/chapter_divide_and_conquer/binary_search_recur.c b/en/codes/c/chapter_divide_and_conquer/binary_search_recur.c new file mode 100644 index 000000000..77ad293ce --- /dev/null +++ b/en/codes/c/chapter_divide_and_conquer/binary_search_recur.c @@ -0,0 +1,47 @@ +/** + * File: binary_search_recur.c + * Created Time: 2023-10-01 + * Author: Zuoxun (845242523@qq.com) + */ + +#include "../utils/common.h" + +/* Binary search: problem f(i, j) */ +int dfs(int nums[], int target, int i, int j) { + // If the interval is empty, it means there is no target element, return -1 + if (i > j) { + return -1; + } + // Calculate the midpoint index m + int m = (i + j) / 2; + if (nums[m] < target) { + // Recursion subproblem f(m+1, j) + return dfs(nums, target, m + 1, j); + } else if (nums[m] > target) { + // Recursion subproblem f(i, m-1) + return dfs(nums, target, i, m - 1); + } else { + // Found the target element, return its index + return m; + } +} + +/* Binary search */ +int binarySearch(int nums[], int target, int numsSize) { + int n = numsSize; + // Solve the problem f(0, n-1) + return dfs(nums, target, 0, n - 1); +} + +/* Driver Code */ +int main() { + int target = 6; + int nums[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; + int numsSize = sizeof(nums) / sizeof(nums[0]); + + // Binary search (closed interval on both sides) + int index = binarySearch(nums, target, numsSize); + printf("Index of target element 6 = %d\n", index); + + return 0; +} diff --git a/en/codes/c/chapter_divide_and_conquer/build_tree.c b/en/codes/c/chapter_divide_and_conquer/build_tree.c new file mode 100644 index 000000000..d3dfe6da3 --- /dev/null +++ b/en/codes/c/chapter_divide_and_conquer/build_tree.c @@ -0,0 +1,61 @@ +/** + * File : build_tree.c + * Created Time: 2023-10-16 + * Author : lucas (superrat6@gmail.com) + */ + +#include "../utils/common.h" + +// Assume all elements less than 1000 +#define MAX_SIZE 1000 + +/* Build binary tree: divide and conquer */ +TreeNode *dfs(int *preorder, int *inorderMap, int i, int l, int r, int size) { + // Terminate when the subtree interval is empty + if (r - l < 0) + return NULL; + // Initialize the root node + TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode)); + root->val = preorder[i]; + root->left = NULL; + root->right = NULL; + // Query m to divide the left and right subtrees + int m = inorderMap[preorder[i]]; + // Subproblem: build the left subtree + root->left = dfs(preorder, inorderMap, i + 1, l, m - 1, size); + // Subproblem: build the right subtree + root->right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r, size); + // Return the root node + return root; +} + +/* Build binary tree */ +TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) { + // Initialize hash map, storing the mapping from inorder elements to indices + int *inorderMap = (int *)malloc(sizeof(int) * MAX_SIZE); + for (int i = 0; i < inorderSize; i++) { + inorderMap[inorder[i]] = i; + } + TreeNode *root = dfs(preorder, inorderMap, 0, 0, inorderSize - 1, inorderSize); + free(inorderMap); + return root; +} + +/* Driver Code */ +int main() { + int preorder[] = {3, 9, 2, 1, 7}; + int inorder[] = {9, 3, 1, 2, 7}; + int preorderSize = sizeof(preorder) / sizeof(preorder[0]); + int inorderSize = sizeof(inorder) / sizeof(inorder[0]); + printf("Preorder traversal = "); + printArray(preorder, preorderSize); + printf("Inorder traversal = "); + printArray(inorder, inorderSize); + + TreeNode *root = buildTree(preorder, preorderSize, inorder, inorderSize); + printf("The constructed binary tree is:\n"); + printTree(root); + + freeMemoryTree(root); + return 0; +} diff --git a/en/codes/c/chapter_divide_and_conquer/hanota.c b/en/codes/c/chapter_divide_and_conquer/hanota.c new file mode 100644 index 000000000..156e2ae3a --- /dev/null +++ b/en/codes/c/chapter_divide_and_conquer/hanota.c @@ -0,0 +1,74 @@ +/** + * File: hanota.c + * Created Time: 2023-10-01 + * Author: Zuoxun (845242523@qq.com), lucas(superrat6@gmail.com) + */ + +#include "../utils/common.h" + +// Assume at most 1000 permutations +#define MAX_SIZE 1000 + +/* Move a disk */ +void move(int *src, int *srcSize, int *tar, int *tarSize) { + // Take out a disk from the top of src + int pan = src[*srcSize - 1]; + src[*srcSize - 1] = 0; + (*srcSize)--; + // Place the disk on top of tar + tar[*tarSize] = pan; + (*tarSize)++; +} + +/* Solve the Tower of Hanoi problem f(i) */ +void dfs(int i, int *src, int *srcSize, int *buf, int *bufSize, int *tar, int *tarSize) { + // If there is only one disk left in src, move it directly to tar + if (i == 1) { + move(src, srcSize, tar, tarSize); + return; + } + // Subproblem f(i-1): move the top i-1 disks from src to buf using tar + dfs(i - 1, src, srcSize, tar, tarSize, buf, bufSize); + // Subproblem f(1): move the remaining disk from src to tar + move(src, srcSize, tar, tarSize); + // Subproblem f(i-1): move the top i-1 disks from buf to tar using src + dfs(i - 1, buf, bufSize, src, srcSize, tar, tarSize); +} + +/* Solve the Tower of Hanoi problem */ +void solveHanota(int *A, int *ASize, int *B, int *BSize, int *C, int *CSize) { + // Move the top n disks from A to C using B + dfs(*ASize, A, ASize, B, BSize, C, CSize); +} + +/* Driver Code */ +int main() { + // The tail of the list is the top of the rod + int a[] = {5, 4, 3, 2, 1}; + int b[MAX_SIZE] = {0}; + int c[MAX_SIZE] = {0}; + + int ASize = sizeof(a) / sizeof(a[0]); + int BSize = 0; + int CSize = 0; + + printf("\nInitial state:"); + printf("\nA = "); + printArray(a, ASize); + printf("B = "); + printArray(b, BSize); + printf("C = "); + printArray(c, CSize); + + solveHanota(a, &ASize, b, &BSize, c, &CSize); + + printf("\nAfter disk movement:"); + printf("A = "); + printArray(a, ASize); + printf("B = "); + printArray(b, BSize); + printf("C = "); + printArray(c, CSize); + + return 0; +} diff --git a/en/codes/c/chapter_dynamic_programming/CMakeLists.txt b/en/codes/c/chapter_dynamic_programming/CMakeLists.txt new file mode 100644 index 000000000..dd769ebd5 --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(climbing_stairs_constraint_dp climbing_stairs_constraint_dp.c) +add_executable(min_cost_climbing_stairs_dp min_cost_climbing_stairs_dp.c) +add_executable(min_path_sum min_path_sum.c) +add_executable(knapsack knapsack.c) +add_executable(unbounded_knapsack unbounded_knapsack.c) +add_executable(coin_change coin_change.c) +add_executable(coin_change_ii coin_change_ii.c) +add_executable(edit_distance edit_distance.c) diff --git a/en/codes/c/chapter_dynamic_programming/climbing_stairs_backtrack.c b/en/codes/c/chapter_dynamic_programming/climbing_stairs_backtrack.c new file mode 100644 index 000000000..cc229ed90 --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/climbing_stairs_backtrack.c @@ -0,0 +1,47 @@ +/** + * File: climbing_stairs_backtrack.c + * Created Time: 2023-09-22 + * Author: huawuque404 (huawuque404@163.com) + */ + +#include "../utils/common.h" + +/* Backtracking */ +void backtrack(int *choices, int state, int n, int *res, int len) { + // When climbing to the n-th stair, add 1 to the solution count + if (state == n) + res[0]++; + // Traverse all choices + for (int i = 0; i < len; i++) { + int choice = choices[i]; + // Pruning: not allowed to go beyond the n-th stair + if (state + choice > n) + continue; + // Attempt: make choice, update state + backtrack(choices, state + choice, n, res, len); + // Backtrack + } +} + +/* Climbing stairs: Backtracking */ +int climbingStairsBacktrack(int n) { + int choices[2] = {1, 2}; // Can choose to climb up 1 or 2 stairs + int state = 0; // Start climbing from the 0-th stair + int *res = (int *)malloc(sizeof(int)); + *res = 0; // Use res[0] to record the solution count + int len = sizeof(choices) / sizeof(int); + backtrack(choices, state, n, res, len); + int result = *res; + free(res); + return result; +} + +/* Driver Code */ +int main() { + int n = 9; + + int res = climbingStairsBacktrack(n); + printf("Climbing %d stairs has %d solutions\n", n, res); + + return 0; +} \ No newline at end of file diff --git a/en/codes/c/chapter_dynamic_programming/climbing_stairs_constraint_dp.c b/en/codes/c/chapter_dynamic_programming/climbing_stairs_constraint_dp.c new file mode 100644 index 000000000..e8037064f --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/climbing_stairs_constraint_dp.c @@ -0,0 +1,46 @@ +/** + * File: climbing_stairs_constraint_dp.c + * Created Time: 2023-10-02 + * Author: Zuoxun (845242523@qq.com) + */ + +#include "../utils/common.h" + +/* Climbing stairs with constraint: Dynamic programming */ +int climbingStairsConstraintDP(int n) { + if (n == 1 || n == 2) { + return 1; + } + // Initialize dp table, used to store solutions to subproblems + int **dp = malloc((n + 1) * sizeof(int *)); + for (int i = 0; i <= n; i++) { + dp[i] = calloc(3, sizeof(int)); + } + // Initial state: preset the solution to the smallest subproblem + dp[1][1] = 1; + dp[1][2] = 0; + dp[2][1] = 0; + dp[2][2] = 1; + // State transition: gradually solve larger subproblems from smaller ones + for (int i = 3; i <= n; i++) { + dp[i][1] = dp[i - 1][2]; + dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; + } + int res = dp[n][1] + dp[n][2]; + // Free memory + for (int i = 0; i <= n; i++) { + free(dp[i]); + } + free(dp); + return res; +} + +/* Driver Code */ +int main() { + int n = 9; + + int res = climbingStairsConstraintDP(n); + printf("Climbing %d stairs has %d solutions\n", n, res); + + return 0; +} diff --git a/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c new file mode 100644 index 000000000..9a30935b7 --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c @@ -0,0 +1,32 @@ +/** + * File: climbing_stairs_dfs.c + * Created Time: 2023-09-19 + * Author: huawuque404 (huawuque404@163.com) + */ + +#include "../utils/common.h" + +/* Search */ +int dfs(int i) { + // Known dp[1] and dp[2], return them + if (i == 1 || i == 2) + return i; + // dp[i] = dp[i-1] + dp[i-2] + int count = dfs(i - 1) + dfs(i - 2); + return count; +} + +/* Climbing stairs: Search */ +int climbingStairsDFS(int n) { + return dfs(n); +} + +/* Driver Code */ +int main() { + int n = 9; + + int res = climbingStairsDFS(n); + printf("Climbing %d stairs has %d solutions\n", n, res); + + return 0; +} \ No newline at end of file diff --git a/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs_mem.c b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs_mem.c new file mode 100644 index 000000000..30fe9a241 --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs_mem.c @@ -0,0 +1,44 @@ +/** + * File: climbing_stairs_dfs_mem.c + * Created Time: 2023-09-19 + * Author: huawuque404 (huawuque404@163.com) + */ + +#include "../utils/common.h" + +/* Memoization search */ +int dfs(int i, int *mem) { + // Known dp[1] and dp[2], return them + if (i == 1 || i == 2) + return i; + // If record dp[i] exists, return it directly + if (mem[i] != -1) + return mem[i]; + // dp[i] = dp[i-1] + dp[i-2] + int count = dfs(i - 1, mem) + dfs(i - 2, mem); + // Record dp[i] + mem[i] = count; + return count; +} + +/* Climbing stairs: Memoization search */ +int climbingStairsDFSMem(int n) { + // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + int *mem = (int *)malloc((n + 1) * sizeof(int)); + for (int i = 0; i <= n; i++) { + mem[i] = -1; + } + int result = dfs(n, mem); + free(mem); + return result; +} + +/* Driver Code */ +int main() { + int n = 9; + + int res = climbingStairsDFSMem(n); + printf("Climbing %d stairs has %d solutions\n", n, res); + + return 0; +} \ No newline at end of file diff --git a/en/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c new file mode 100644 index 000000000..cfd2ef790 --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c @@ -0,0 +1,51 @@ +/** + * File: climbing_stairs_dp.c + * Created Time: 2023-09-19 + * Author: huawuque404 (huawuque404@163.com) + */ + +#include "../utils/common.h" + +/* Climbing stairs: Dynamic programming */ +int climbingStairsDP(int n) { + if (n == 1 || n == 2) + return n; + // Initialize dp table, used to store solutions to subproblems + int *dp = (int *)malloc((n + 1) * sizeof(int)); + // Initial state: preset the solution to the smallest subproblem + dp[1] = 1; + dp[2] = 2; + // State transition: gradually solve larger subproblems from smaller ones + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + int result = dp[n]; + free(dp); + return result; +} + +/* Climbing stairs: Space-optimized dynamic programming */ +int climbingStairsDPComp(int n) { + if (n == 1 || n == 2) + return n; + int a = 1, b = 2; + for (int i = 3; i <= n; i++) { + int tmp = b; + b = a + b; + a = tmp; + } + return b; +} + +/* Driver Code */ +int main() { + int n = 9; + + int res = climbingStairsDP(n); + printf("Climbing %d stairs has %d solutions\n", n, res); + + res = climbingStairsDPComp(n); + printf("Climbing %d stairs has %d solutions\n", n, res); + + return 0; +} \ No newline at end of file diff --git a/en/codes/c/chapter_dynamic_programming/coin_change.c b/en/codes/c/chapter_dynamic_programming/coin_change.c new file mode 100644 index 000000000..1c9e562b2 --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/coin_change.c @@ -0,0 +1,92 @@ +/** + * File: coin_change.c + * Created Time: 2023-10-02 + * Author: Zuoxun (845242523@qq.com) + */ + +#include "../utils/common.h" + +/* Find minimum value */ +int myMin(int a, int b) { + return a < b ? a : b; +} + +/* Coin change: Dynamic programming */ +int coinChangeDP(int coins[], int amt, int coinsSize) { + int n = coinsSize; + int MAX = amt + 1; + // Initialize dp table + int **dp = malloc((n + 1) * sizeof(int *)); + for (int i = 0; i <= n; i++) { + dp[i] = calloc(amt + 1, sizeof(int)); + } + // State transition: first row and first column + for (int a = 1; a <= amt; a++) { + dp[0][a] = MAX; + } + // State transition: rest of the rows and columns + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[i][a] = myMin(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); + } + } + } + int res = dp[n][amt] != MAX ? dp[n][amt] : -1; + // Free memory + for (int i = 0; i <= n; i++) { + free(dp[i]); + } + free(dp); + return res; +} + +/* Coin change: Space-optimized dynamic programming */ +int coinChangeDPComp(int coins[], int amt, int coinsSize) { + int n = coinsSize; + int MAX = amt + 1; + // Initialize dp table + int *dp = malloc((amt + 1) * sizeof(int)); + for (int j = 1; j <= amt; j++) { + dp[j] = MAX; + } + dp[0] = 0; + + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[a] = myMin(dp[a], dp[a - coins[i - 1]] + 1); + } + } + } + int res = dp[amt] != MAX ? dp[amt] : -1; + // Free memory + free(dp); + return res; +} + +/* Driver code */ +int main() { + int coins[] = {1, 2, 5}; + int coinsSize = sizeof(coins) / sizeof(coins[0]); + int amt = 4; + + // Dynamic programming + int res = coinChangeDP(coins, amt, coinsSize); + printf("Minimum number of coins needed to make target amount is %d\n", res); + + // Space-optimized dynamic programming + res = coinChangeDPComp(coins, amt, coinsSize); + printf("Minimum number of coins needed to make target amount is %d\n", res); + + return 0; +} diff --git a/en/codes/c/chapter_dynamic_programming/coin_change_ii.c b/en/codes/c/chapter_dynamic_programming/coin_change_ii.c new file mode 100644 index 000000000..ca37fce59 --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/coin_change_ii.c @@ -0,0 +1,81 @@ +/** + * File: coin_change_ii.c + * Created Time: 2023-10-02 + * Author: Zuoxun (845242523@qq.com) + */ + +#include "../utils/common.h" + +/* Coin change II: Dynamic programming */ +int coinChangeIIDP(int coins[], int amt, int coinsSize) { + int n = coinsSize; + // Initialize dp table + int **dp = malloc((n + 1) * sizeof(int *)); + for (int i = 0; i <= n; i++) { + dp[i] = calloc(amt + 1, sizeof(int)); + } + // Initialize first column + for (int i = 0; i <= n; i++) { + dp[i][0] = 1; + } + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; + } + } + } + int res = dp[n][amt]; + // Free memory + for (int i = 0; i <= n; i++) { + free(dp[i]); + } + free(dp); + return res; +} + +/* Coin change II: Space-optimized dynamic programming */ +int coinChangeIIDPComp(int coins[], int amt, int coinsSize) { + int n = coinsSize; + // Initialize dp table + int *dp = calloc(amt + 1, sizeof(int)); + dp[0] = 1; + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[a] = dp[a] + dp[a - coins[i - 1]]; + } + } + } + int res = dp[amt]; + // Free memory + free(dp); + return res; +} + +/* Driver code */ +int main() { + int coins[] = {1, 2, 5}; + int coinsSize = sizeof(coins) / sizeof(coins[0]); + int amt = 5; + + // Dynamic programming + int res = coinChangeIIDP(coins, amt, coinsSize); + printf("Number of coin combinations to make target amount is %d\n", res); + + // Space-optimized dynamic programming + res = coinChangeIIDPComp(coins, amt, coinsSize); + printf("Number of coin combinations to make target amount is %d\n", res); + + return 0; +} diff --git a/en/codes/c/chapter_dynamic_programming/edit_distance.c b/en/codes/c/chapter_dynamic_programming/edit_distance.c new file mode 100644 index 000000000..9188c3d9b --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/edit_distance.c @@ -0,0 +1,159 @@ +/** + * File: edit_distance.c + * Created Time: 2023-10-02 + * Author: Zuoxun (845242523@qq.com) + */ + +#include "../utils/common.h" + +/* Find minimum value */ +int myMin(int a, int b) { + return a < b ? a : b; +} + +/* Edit distance: Brute-force search */ +int editDistanceDFS(char *s, char *t, int i, int j) { + // If both s and t are empty, return 0 + if (i == 0 && j == 0) + return 0; + // If s is empty, return length of t + if (i == 0) + return j; + // If t is empty, return length of s + if (j == 0) + return i; + // If two characters are equal, skip both characters + if (s[i - 1] == t[j - 1]) + return editDistanceDFS(s, t, i - 1, j - 1); + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + int insert = editDistanceDFS(s, t, i, j - 1); + int del = editDistanceDFS(s, t, i - 1, j); + int replace = editDistanceDFS(s, t, i - 1, j - 1); + // Return minimum edit steps + return myMin(myMin(insert, del), replace) + 1; +} + +/* Edit distance: Memoization search */ +int editDistanceDFSMem(char *s, char *t, int memCols, int **mem, int i, int j) { + // If both s and t are empty, return 0 + if (i == 0 && j == 0) + return 0; + // If s is empty, return length of t + if (i == 0) + return j; + // If t is empty, return length of s + if (j == 0) + return i; + // If there's a record, return it directly + if (mem[i][j] != -1) + return mem[i][j]; + // If two characters are equal, skip both characters + if (s[i - 1] == t[j - 1]) + return editDistanceDFSMem(s, t, memCols, mem, i - 1, j - 1); + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + int insert = editDistanceDFSMem(s, t, memCols, mem, i, j - 1); + int del = editDistanceDFSMem(s, t, memCols, mem, i - 1, j); + int replace = editDistanceDFSMem(s, t, memCols, mem, i - 1, j - 1); + // Record and return minimum edit steps + mem[i][j] = myMin(myMin(insert, del), replace) + 1; + return mem[i][j]; +} + +/* Edit distance: Dynamic programming */ +int editDistanceDP(char *s, char *t, int n, int m) { + int **dp = malloc((n + 1) * sizeof(int *)); + for (int i = 0; i <= n; i++) { + dp[i] = calloc(m + 1, sizeof(int)); + } + // State transition: first row and first column + for (int i = 1; i <= n; i++) { + dp[i][0] = i; + } + for (int j = 1; j <= m; j++) { + dp[0][j] = j; + } + // State transition: rest of the rows and columns + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + if (s[i - 1] == t[j - 1]) { + // If two characters are equal, skip both characters + dp[i][j] = dp[i - 1][j - 1]; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[i][j] = myMin(myMin(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; + } + } + } + int res = dp[n][m]; + // Free memory + for (int i = 0; i <= n; i++) { + free(dp[i]); + } + return res; +} + +/* Edit distance: Space-optimized dynamic programming */ +int editDistanceDPComp(char *s, char *t, int n, int m) { + int *dp = calloc(m + 1, sizeof(int)); + // State transition: first row + for (int j = 1; j <= m; j++) { + dp[j] = j; + } + // State transition: rest of the rows + for (int i = 1; i <= n; i++) { + // State transition: first column + int leftup = dp[0]; // Temporarily store dp[i-1, j-1] + dp[0] = i; + // State transition: rest of the columns + for (int j = 1; j <= m; j++) { + int temp = dp[j]; + if (s[i - 1] == t[j - 1]) { + // If two characters are equal, skip both characters + dp[j] = leftup; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[j] = myMin(myMin(dp[j - 1], dp[j]), leftup) + 1; + } + leftup = temp; // Update for next round's dp[i-1, j-1] + } + } + int res = dp[m]; + // Free memory + free(dp); + return res; +} + +/* Driver Code */ +int main() { + char *s = "bag"; + char *t = "pack"; + int n = strlen(s), m = strlen(t); + + // Brute-force search + int res = editDistanceDFS(s, t, n, m); + printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res); + + // Memoization search + int **mem = malloc((n + 1) * sizeof(int *)); + for (int i = 0; i <= n; i++) { + mem[i] = malloc((m + 1) * sizeof(int)); + memset(mem[i], -1, (m + 1) * sizeof(int)); + } + res = editDistanceDFSMem(s, t, m + 1, mem, n, m); + printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res); + // Free memory + for (int i = 0; i <= n; i++) { + free(mem[i]); + } + free(mem); + + // Dynamic programming + res = editDistanceDP(s, t, n, m); + printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res); + + // Space-optimized dynamic programming + res = editDistanceDPComp(s, t, n, m); + printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res); + + return 0; +} diff --git a/en/codes/c/chapter_dynamic_programming/knapsack.c b/en/codes/c/chapter_dynamic_programming/knapsack.c new file mode 100644 index 000000000..2fa42ab25 --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/knapsack.c @@ -0,0 +1,137 @@ +/** + * File: knapsack.c + * Created Time: 2023-10-02 + * Author: Zuoxun (845242523@qq.com) + */ + +#include "../utils/common.h" + +/* Find maximum value */ +int myMax(int a, int b) { + return a > b ? a : b; +} + +/* 0-1 knapsack: Brute-force search */ +int knapsackDFS(int wgt[], int val[], int i, int c) { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i == 0 || c == 0) { + return 0; + } + // If exceeds knapsack capacity, can only choose not to put it in + if (wgt[i - 1] > c) { + return knapsackDFS(wgt, val, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + int no = knapsackDFS(wgt, val, i - 1, c); + int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; + // Return the larger value of the two options + return myMax(no, yes); +} + +/* 0-1 knapsack: Memoization search */ +int knapsackDFSMem(int wgt[], int val[], int memCols, int **mem, int i, int c) { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i == 0 || c == 0) { + return 0; + } + // If there's a record, return it directly + if (mem[i][c] != -1) { + return mem[i][c]; + } + // If exceeds knapsack capacity, can only choose not to put it in + if (wgt[i - 1] > c) { + return knapsackDFSMem(wgt, val, memCols, mem, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + int no = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c); + int yes = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; + // Record and return the larger value of the two options + mem[i][c] = myMax(no, yes); + return mem[i][c]; +} + +/* 0-1 knapsack: Dynamic programming */ +int knapsackDP(int wgt[], int val[], int cap, int wgtSize) { + int n = wgtSize; + // Initialize dp table + int **dp = malloc((n + 1) * sizeof(int *)); + for (int i = 0; i <= n; i++) { + dp[i] = calloc(cap + 1, sizeof(int)); + } + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c]; + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = myMax(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); + } + } + } + int res = dp[n][cap]; + // Free memory + for (int i = 0; i <= n; i++) { + free(dp[i]); + } + return res; +} + +/* 0-1 knapsack: Space-optimized dynamic programming */ +int knapsackDPComp(int wgt[], int val[], int cap, int wgtSize) { + int n = wgtSize; + // Initialize dp table + int *dp = calloc(cap + 1, sizeof(int)); + // State transition + for (int i = 1; i <= n; i++) { + // Traverse in reverse order + for (int c = cap; c >= 1; c--) { + if (wgt[i - 1] <= c) { + // The larger value between not selecting and selecting item i + dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + int res = dp[cap]; + // Free memory + free(dp); + return res; +} + +/* Driver Code */ +int main() { + int wgt[] = {10, 20, 30, 40, 50}; + int val[] = {50, 120, 150, 210, 240}; + int cap = 50; + int n = sizeof(wgt) / sizeof(wgt[0]); + int wgtSize = n; + + // Brute-force search + int res = knapsackDFS(wgt, val, n, cap); + printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + + // Memoization search + int **mem = malloc((n + 1) * sizeof(int *)); + for (int i = 0; i <= n; i++) { + mem[i] = malloc((cap + 1) * sizeof(int)); + memset(mem[i], -1, (cap + 1) * sizeof(int)); + } + res = knapsackDFSMem(wgt, val, cap + 1, mem, n, cap); + printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + // Free memory + for (int i = 0; i <= n; i++) { + free(mem[i]); + } + free(mem); + + // Dynamic programming + res = knapsackDP(wgt, val, cap, wgtSize); + printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + + // Space-optimized dynamic programming + res = knapsackDPComp(wgt, val, cap, wgtSize); + printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + + return 0; +} diff --git a/en/codes/c/chapter_dynamic_programming/min_cost_climbing_stairs_dp.c b/en/codes/c/chapter_dynamic_programming/min_cost_climbing_stairs_dp.c new file mode 100644 index 000000000..02bc2f18b --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/min_cost_climbing_stairs_dp.c @@ -0,0 +1,62 @@ +/** + * File: min_cost_climbing_stairs_dp.c + * Created Time: 2023-10-02 + * Author: Zuoxun (845242523@qq.com) + */ + +#include "../utils/common.h" + +/* Find minimum value */ +int myMin(int a, int b) { + return a < b ? a : b; +} + +/* Minimum cost climbing stairs: Dynamic programming */ +int minCostClimbingStairsDP(int cost[], int costSize) { + int n = costSize - 1; + if (n == 1 || n == 2) + return cost[n]; + // Initialize dp table, used to store solutions to subproblems + int *dp = calloc(n + 1, sizeof(int)); + // Initial state: preset the solution to the smallest subproblem + dp[1] = cost[1]; + dp[2] = cost[2]; + // State transition: gradually solve larger subproblems from smaller ones + for (int i = 3; i <= n; i++) { + dp[i] = myMin(dp[i - 1], dp[i - 2]) + cost[i]; + } + int res = dp[n]; + // Free memory + free(dp); + return res; +} + +/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +int minCostClimbingStairsDPComp(int cost[], int costSize) { + int n = costSize - 1; + if (n == 1 || n == 2) + return cost[n]; + int a = cost[1], b = cost[2]; + for (int i = 3; i <= n; i++) { + int tmp = b; + b = myMin(a, tmp) + cost[i]; + a = tmp; + } + return b; +} + +/* Driver Code */ +int main() { + int cost[] = {0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1}; + int costSize = sizeof(cost) / sizeof(cost[0]); + printf("Input stair cost list is:"); + printArray(cost, costSize); + + int res = minCostClimbingStairsDP(cost, costSize); + printf("Minimum cost to climb stairs is %d\n", res); + + res = minCostClimbingStairsDPComp(cost, costSize); + printf("Minimum cost to climb stairs is %d\n", res); + + return 0; +} diff --git a/en/codes/c/chapter_dynamic_programming/min_path_sum.c b/en/codes/c/chapter_dynamic_programming/min_path_sum.c new file mode 100644 index 000000000..36ac13b2b --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/min_path_sum.c @@ -0,0 +1,134 @@ +/** + * File: min_path_sum.c + * Created Time: 2023-10-02 + * Author: Zuoxun (845242523@qq.com) + */ + +#include "../utils/common.h" + +// Assume max matrix rows and columns is 100 +#define MAX_SIZE 100 + +/* Find minimum value */ +int myMin(int a, int b) { + return a < b ? a : b; +} + +/* Minimum path sum: Brute-force search */ +int minPathSumDFS(int grid[MAX_SIZE][MAX_SIZE], int i, int j) { + // If it's the top-left cell, terminate the search + if (i == 0 && j == 0) { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + return INT_MAX; + } + // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + int up = minPathSumDFS(grid, i - 1, j); + int left = minPathSumDFS(grid, i, j - 1); + // Return the minimum path cost from top-left to (i, j) + return myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX; +} + +/* Minimum path sum: Memoization search */ +int minPathSumDFSMem(int grid[MAX_SIZE][MAX_SIZE], int mem[MAX_SIZE][MAX_SIZE], int i, int j) { + // If it's the top-left cell, terminate the search + if (i == 0 && j == 0) { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + return INT_MAX; + } + // If there's a record, return it directly + if (mem[i][j] != -1) { + return mem[i][j]; + } + // Minimum path cost for left and upper cells + int up = minPathSumDFSMem(grid, mem, i - 1, j); + int left = minPathSumDFSMem(grid, mem, i, j - 1); + // Record and return the minimum path cost from top-left to (i, j) + mem[i][j] = myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX; + return mem[i][j]; +} + +/* Minimum path sum: Dynamic programming */ +int minPathSumDP(int grid[MAX_SIZE][MAX_SIZE], int n, int m) { + // Initialize dp table + int **dp = malloc(n * sizeof(int *)); + for (int i = 0; i < n; i++) { + dp[i] = calloc(m, sizeof(int)); + } + dp[0][0] = grid[0][0]; + // State transition: first row + for (int j = 1; j < m; j++) { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + // State transition: first column + for (int i = 1; i < n; i++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + // State transition: rest of the rows and columns + for (int i = 1; i < n; i++) { + for (int j = 1; j < m; j++) { + dp[i][j] = myMin(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; + } + } + int res = dp[n - 1][m - 1]; + // Free memory + for (int i = 0; i < n; i++) { + free(dp[i]); + } + return res; +} + +/* Minimum path sum: Space-optimized dynamic programming */ +int minPathSumDPComp(int grid[MAX_SIZE][MAX_SIZE], int n, int m) { + // Initialize dp table + int *dp = calloc(m, sizeof(int)); + // State transition: first row + dp[0] = grid[0][0]; + for (int j = 1; j < m; j++) { + dp[j] = dp[j - 1] + grid[0][j]; + } + // State transition: rest of the rows + for (int i = 1; i < n; i++) { + // State transition: first column + dp[0] = dp[0] + grid[i][0]; + // State transition: rest of the columns + for (int j = 1; j < m; j++) { + dp[j] = myMin(dp[j - 1], dp[j]) + grid[i][j]; + } + } + int res = dp[m - 1]; + // Free memory + free(dp); + return res; +} + +/* Driver Code */ +int main() { + int grid[MAX_SIZE][MAX_SIZE] = {{1, 3, 1, 5}, {2, 2, 4, 2}, {5, 3, 2, 1}, {4, 3, 5, 2}}; + int n = 4, m = 4; // Matrix capacity is MAX_SIZE * MAX_SIZE, valid rows and columns are n * m + + // Brute-force search + int res = minPathSumDFS(grid, n - 1, m - 1); + printf("Minimum path sum from top-left to bottom-right is %d\n", res); + + // Memoization search + int mem[MAX_SIZE][MAX_SIZE]; + memset(mem, -1, sizeof(mem)); + res = minPathSumDFSMem(grid, mem, n - 1, m - 1); + printf("Minimum path sum from top-left to bottom-right is %d\n", res); + + // Dynamic programming + res = minPathSumDP(grid, n, m); + printf("Minimum path sum from top-left to bottom-right is %d\n", res); + + // Space-optimized dynamic programming + res = minPathSumDPComp(grid, n, m); + printf("Minimum path sum from top-left to bottom-right is %d\n", res); + + return 0; +} diff --git a/en/codes/c/chapter_dynamic_programming/unbounded_knapsack.c b/en/codes/c/chapter_dynamic_programming/unbounded_knapsack.c new file mode 100644 index 000000000..666317199 --- /dev/null +++ b/en/codes/c/chapter_dynamic_programming/unbounded_knapsack.c @@ -0,0 +1,81 @@ +/** + * File: unbounded_knapsack.c + * Created Time: 2023-10-02 + * Author: Zuoxun (845242523@qq.com) + */ + +#include "../utils/common.h" + +/* Find maximum value */ +int myMax(int a, int b) { + return a > b ? a : b; +} + +/* Unbounded knapsack: Dynamic programming */ +int unboundedKnapsackDP(int wgt[], int val[], int cap, int wgtSize) { + int n = wgtSize; + // Initialize dp table + int **dp = malloc((n + 1) * sizeof(int *)); + for (int i = 0; i <= n; i++) { + dp[i] = calloc(cap + 1, sizeof(int)); + } + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c]; + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = myMax(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); + } + } + } + int res = dp[n][cap]; + // Free memory + for (int i = 0; i <= n; i++) { + free(dp[i]); + } + return res; +} + +/* Unbounded knapsack: Space-optimized dynamic programming */ +int unboundedKnapsackDPComp(int wgt[], int val[], int cap, int wgtSize) { + int n = wgtSize; + // Initialize dp table + int *dp = calloc(cap + 1, sizeof(int)); + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[c] = dp[c]; + } else { + // The larger value between not selecting and selecting item i + dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + int res = dp[cap]; + // Free memory + free(dp); + return res; +} + +/* Driver code */ +int main() { + int wgt[] = {1, 2, 3}; + int val[] = {5, 11, 15}; + int wgtSize = sizeof(wgt) / sizeof(wgt[0]); + int cap = 4; + + // Dynamic programming + int res = unboundedKnapsackDP(wgt, val, cap, wgtSize); + printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + + // Space-optimized dynamic programming + res = unboundedKnapsackDPComp(wgt, val, cap, wgtSize); + printf("Maximum item value not exceeding knapsack capacity is %d\n", res); + + return 0; +} diff --git a/en/codes/c/chapter_graph/CMakeLists.txt b/en/codes/c/chapter_graph/CMakeLists.txt new file mode 100644 index 000000000..28f8470f4 --- /dev/null +++ b/en/codes/c/chapter_graph/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(graph_adjacency_matrix graph_adjacency_matrix.c) +add_executable(graph_adjacency_list_test graph_adjacency_list_test.c) +add_executable(graph_bfs graph_bfs.c) +add_executable(graph_dfs graph_dfs.c) diff --git a/en/codes/c/chapter_graph/graph_adjacency_list.c b/en/codes/c/chapter_graph/graph_adjacency_list.c new file mode 100644 index 000000000..fe6f1f6b6 --- /dev/null +++ b/en/codes/c/chapter_graph/graph_adjacency_list.c @@ -0,0 +1,171 @@ +/** + * File: graph_adjacency_list.c + * Created Time: 2023-07-07 + * Author: NI-SW (947743645@qq.com) + */ + +#include "../utils/common.h" + +// Assume max node count is 100 +#define MAX_SIZE 100 + +/* Node structure */ +typedef struct AdjListNode { + Vertex *vertex; // Vertex + struct AdjListNode *next; // Successor node +} AdjListNode; + +/* Undirected graph class based on adjacency list */ +typedef struct { + AdjListNode *heads[MAX_SIZE]; // Node array + int size; // Node count +} GraphAdjList; + +/* Constructor */ +GraphAdjList *newGraphAdjList() { + GraphAdjList *graph = (GraphAdjList *)malloc(sizeof(GraphAdjList)); + if (!graph) { + return NULL; + } + graph->size = 0; + for (int i = 0; i < MAX_SIZE; i++) { + graph->heads[i] = NULL; + } + return graph; +} + +/* Destructor */ +void delGraphAdjList(GraphAdjList *graph) { + for (int i = 0; i < graph->size; i++) { + AdjListNode *cur = graph->heads[i]; + while (cur != NULL) { + AdjListNode *next = cur->next; + if (cur != graph->heads[i]) { + free(cur); + } + cur = next; + } + free(graph->heads[i]->vertex); + free(graph->heads[i]); + } + free(graph); +} + +/* Find node corresponding to vertex */ +AdjListNode *findNode(GraphAdjList *graph, Vertex *vet) { + for (int i = 0; i < graph->size; i++) { + if (graph->heads[i]->vertex == vet) { + return graph->heads[i]; + } + } + return NULL; +} + +/* Add edge helper function */ +void addEdgeHelper(AdjListNode *head, Vertex *vet) { + AdjListNode *node = (AdjListNode *)malloc(sizeof(AdjListNode)); + node->vertex = vet; + // Head insertion + node->next = head->next; + head->next = node; +} + +/* Add edge */ +void addEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) { + AdjListNode *head1 = findNode(graph, vet1); + AdjListNode *head2 = findNode(graph, vet2); + assert(head1 != NULL && head2 != NULL && head1 != head2); + // Add edge vet1 - vet2 + addEdgeHelper(head1, vet2); + addEdgeHelper(head2, vet1); +} + +/* Remove edge helper function */ +void removeEdgeHelper(AdjListNode *head, Vertex *vet) { + AdjListNode *pre = head; + AdjListNode *cur = head->next; + // Search for node corresponding to vet in list + while (cur != NULL && cur->vertex != vet) { + pre = cur; + cur = cur->next; + } + if (cur == NULL) + return; + // Remove node corresponding to vet from list + pre->next = cur->next; + // Free memory + free(cur); +} + +/* Remove edge */ +void removeEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) { + AdjListNode *head1 = findNode(graph, vet1); + AdjListNode *head2 = findNode(graph, vet2); + assert(head1 != NULL && head2 != NULL); + // Remove edge vet1 - vet2 + removeEdgeHelper(head1, head2->vertex); + removeEdgeHelper(head2, head1->vertex); +} + +/* Add vertex */ +void addVertex(GraphAdjList *graph, Vertex *vet) { + assert(graph != NULL && graph->size < MAX_SIZE); + AdjListNode *head = (AdjListNode *)malloc(sizeof(AdjListNode)); + head->vertex = vet; + head->next = NULL; + // Add a new linked list in the adjacency list + graph->heads[graph->size++] = head; +} + +/* Remove vertex */ +void removeVertex(GraphAdjList *graph, Vertex *vet) { + AdjListNode *node = findNode(graph, vet); + assert(node != NULL); + // Remove the linked list corresponding to vertex vet in the adjacency list + AdjListNode *cur = node, *pre = NULL; + while (cur) { + pre = cur; + cur = cur->next; + free(pre); + } + // Traverse the linked lists of other vertices and remove all edges containing vet + for (int i = 0; i < graph->size; i++) { + cur = graph->heads[i]; + pre = NULL; + while (cur) { + pre = cur; + cur = cur->next; + if (cur && cur->vertex == vet) { + pre->next = cur->next; + free(cur); + break; + } + } + } + // Move vertices after this vertex forward to fill gap + int i; + for (i = 0; i < graph->size; i++) { + if (graph->heads[i] == node) + break; + } + for (int j = i; j < graph->size - 1; j++) { + graph->heads[j] = graph->heads[j + 1]; + } + graph->size--; + free(vet); +} + +/* Print adjacency list */ +void printGraph(const GraphAdjList *graph) { + printf("Adjacency list =\n"); + for (int i = 0; i < graph->size; ++i) { + AdjListNode *node = graph->heads[i]; + printf("%d: [", node->vertex->val); + node = node->next; + while (node) { + printf("%d, ", node->vertex->val); + node = node->next; + } + printf("]\n"); + } +} diff --git a/en/codes/c/chapter_graph/graph_adjacency_list_test.c b/en/codes/c/chapter_graph/graph_adjacency_list_test.c new file mode 100644 index 000000000..837d08f44 --- /dev/null +++ b/en/codes/c/chapter_graph/graph_adjacency_list_test.c @@ -0,0 +1,55 @@ +/** + * File: graph_adjacency_list_test.c + * Created Time: 2023-07-11 + * Author: NI-SW (947743645@qq.com) + */ + +#include "graph_adjacency_list.c" + +/* Driver Code */ +int main() { + int vals[] = {1, 3, 2, 5, 4}; + int size = sizeof(vals) / sizeof(vals[0]); + Vertex **v = valsToVets(vals, size); + Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[3]}, {v[2], v[4]}, {v[3], v[4]}}; + int egdeSize = sizeof(edges) / sizeof(edges[0]); + GraphAdjList *graph = newGraphAdjList(); + // Add all vertices and edges + for (int i = 0; i < size; i++) { + addVertex(graph, v[i]); + } + for (int i = 0; i < egdeSize; i++) { + addEdge(graph, edges[i][0], edges[i][1]); + } + printf("\nAfter initialization, graph is\n"); + printGraph(graph); + + /* Add edge */ + // Vertices 1, 3 are v[0], v[1] + addEdge(graph, v[0], v[2]); + printf("\nAfter adding edge 1-2, graph is\n"); + printGraph(graph); + + /* Remove edge */ + // Vertex 3 is v[1] + removeEdge(graph, v[0], v[1]); + printf("\nAfter deleting edge 1-3, graph is\n"); + printGraph(graph); + + /* Add vertex */ + Vertex *v5 = newVertex(6); + addVertex(graph, v5); + printf("\nAfter adding vertex 6, graph is\n"); + printGraph(graph); + + /* Remove vertex */ + // Vertex 3 is v[1] + removeVertex(graph, v[1]); + printf("\nAfter deleting vertex 3, graph is:\n"); + printGraph(graph); + + // Free memory + delGraphAdjList(graph); + free(v); + return 0; +} diff --git a/en/codes/c/chapter_graph/graph_adjacency_matrix.c b/en/codes/c/chapter_graph/graph_adjacency_matrix.c new file mode 100644 index 000000000..a4341a22d --- /dev/null +++ b/en/codes/c/chapter_graph/graph_adjacency_matrix.c @@ -0,0 +1,150 @@ +/** + * File: graph_adjacency_matrix.c + * Created Time: 2023-07-06 + * Author: NI-SW (947743645@qq.com) + */ + +#include "../utils/common.h" + +// Assume max vertex count is 100 +#define MAX_SIZE 100 + +/* Undirected graph structure based on adjacency matrix */ +typedef struct { + int vertices[MAX_SIZE]; + int adjMat[MAX_SIZE][MAX_SIZE]; + int size; +} GraphAdjMat; + +/* Constructor */ +GraphAdjMat *newGraphAdjMat() { + GraphAdjMat *graph = (GraphAdjMat *)malloc(sizeof(GraphAdjMat)); + graph->size = 0; + for (int i = 0; i < MAX_SIZE; i++) { + for (int j = 0; j < MAX_SIZE; j++) { + graph->adjMat[i][j] = 0; + } + } + return graph; +} + +/* Destructor */ +void delGraphAdjMat(GraphAdjMat *graph) { + free(graph); +} + +/* Add vertex */ +void addVertex(GraphAdjMat *graph, int val) { + if (graph->size == MAX_SIZE) { + fprintf(stderr, "Graph vertex count has reached maximum\n"); + return; + } + // Add nth vertex and zero nth row and column + int n = graph->size; + graph->vertices[n] = val; + for (int i = 0; i <= n; i++) { + graph->adjMat[n][i] = graph->adjMat[i][n] = 0; + } + graph->size++; +} + +/* Remove vertex */ +void removeVertex(GraphAdjMat *graph, int index) { + if (index < 0 || index >= graph->size) { + fprintf(stderr, "Vertex index out of bounds\n"); + return; + } + // Remove the vertex at index from the vertex list + for (int i = index; i < graph->size - 1; i++) { + graph->vertices[i] = graph->vertices[i + 1]; + } + // Remove the row at index from the adjacency matrix + for (int i = index; i < graph->size - 1; i++) { + for (int j = 0; j < graph->size; j++) { + graph->adjMat[i][j] = graph->adjMat[i + 1][j]; + } + } + // Remove the column at index from the adjacency matrix + for (int i = 0; i < graph->size; i++) { + for (int j = index; j < graph->size - 1; j++) { + graph->adjMat[i][j] = graph->adjMat[i][j + 1]; + } + } + graph->size--; +} + +/* Add edge */ +// Parameters i, j correspond to the vertices element indices +void addEdge(GraphAdjMat *graph, int i, int j) { + if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) { + fprintf(stderr, "Edge index out of bounds or equal\n"); + return; + } + graph->adjMat[i][j] = 1; + graph->adjMat[j][i] = 1; +} + +/* Remove edge */ +// Parameters i, j correspond to the vertices element indices +void removeEdge(GraphAdjMat *graph, int i, int j) { + if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) { + fprintf(stderr, "Edge index out of bounds or equal\n"); + return; + } + graph->adjMat[i][j] = 0; + graph->adjMat[j][i] = 0; +} + +/* Print adjacency matrix */ +void printGraphAdjMat(GraphAdjMat *graph) { + printf("Vertex list = "); + printArray(graph->vertices, graph->size); + printf("Adjacency matrix =\n"); + for (int i = 0; i < graph->size; i++) { + printArray(graph->adjMat[i], graph->size); + } +} + +/* Driver Code */ +int main() { + // Add edge + GraphAdjMat *graph = newGraphAdjMat(); + int vertices[] = {1, 3, 2, 5, 4}; + for (int i = 0; i < 5; i++) { + addVertex(graph, vertices[i]); + } + int edges[][2] = {{0, 1}, {0, 3}, {1, 2}, {2, 3}, {2, 4}, {3, 4}}; + for (int i = 0; i < 6; i++) { + addEdge(graph, edges[i][0], edges[i][1]); + } + printf("\nAfter initialization, graph is\n"); + printGraphAdjMat(graph); + + /* Add edge */ + // Add vertex + addEdge(graph, 0, 2); + printf("\nAfter adding edge 1-2, graph is\n"); + printGraphAdjMat(graph); + + /* Remove edge */ + // Vertices 1, 3 have indices 0, 1 respectively + removeEdge(graph, 0, 1); + printf("\nAfter deleting edge 1-3, graph is\n"); + printGraphAdjMat(graph); + + /* Add vertex */ + addVertex(graph, 6); + printf("\nAfter adding vertex 6, graph is\n"); + printGraphAdjMat(graph); + + /* Remove vertex */ + // Vertex 3 has index 1 + removeVertex(graph, 1); + printf("\nAfter deleting vertex 3, graph is\n"); + printGraphAdjMat(graph); + + // Free memory + delGraphAdjMat(graph); + + return 0; +} diff --git a/en/codes/c/chapter_graph/graph_bfs.c b/en/codes/c/chapter_graph/graph_bfs.c new file mode 100644 index 000000000..ec3c9f828 --- /dev/null +++ b/en/codes/c/chapter_graph/graph_bfs.c @@ -0,0 +1,116 @@ +/** + * File: graph_bfs.c + * Created Time: 2023-07-11 + * Author: NI-SW (947743645@qq.com) + */ + +#include "graph_adjacency_list.c" + +// Assume max node count is 100 +#define MAX_SIZE 100 + +/* Node queue structure */ +typedef struct { + Vertex *vertices[MAX_SIZE]; + int front, rear, size; +} Queue; + +/* Constructor */ +Queue *newQueue() { + Queue *q = (Queue *)malloc(sizeof(Queue)); + q->front = q->rear = q->size = 0; + return q; +} + +/* Check if the queue is empty */ +int isEmpty(Queue *q) { + return q->size == 0; +} + +/* Enqueue operation */ +void enqueue(Queue *q, Vertex *vet) { + q->vertices[q->rear] = vet; + q->rear = (q->rear + 1) % MAX_SIZE; + q->size++; +} + +/* Dequeue operation */ +Vertex *dequeue(Queue *q) { + Vertex *vet = q->vertices[q->front]; + q->front = (q->front + 1) % MAX_SIZE; + q->size--; + return vet; +} + +/* Check if vertex has been visited */ +int isVisited(Vertex **visited, int size, Vertex *vet) { + // Traverse to find node using O(n) time + for (int i = 0; i < size; i++) { + if (visited[i] == vet) + return 1; + } + return 0; +} + +/* Breadth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +void graphBFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize, Vertex **visited, int *visitedSize) { + // Queue used to implement BFS + Queue *queue = newQueue(); + enqueue(queue, startVet); + visited[(*visitedSize)++] = startVet; + // Starting from vertex vet, loop until all vertices are visited + while (!isEmpty(queue)) { + Vertex *vet = dequeue(queue); // Dequeue the front vertex + res[(*resSize)++] = vet; // Record visited vertex + // Traverse all adjacent vertices of this vertex + AdjListNode *node = findNode(graph, vet); + while (node != NULL) { + // Skip vertices that have been visited + if (!isVisited(visited, *visitedSize, node->vertex)) { + enqueue(queue, node->vertex); // Only enqueue unvisited vertices + visited[(*visitedSize)++] = node->vertex; // Mark this vertex as visited + } + node = node->next; + } + } + // Free memory + free(queue); +} + +/* Driver Code */ +int main() { + // Add edge + int vals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + int size = sizeof(vals) / sizeof(vals[0]); + Vertex **v = valsToVets(vals, size); + Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[1], v[4]}, {v[2], v[5]}, {v[3], v[4]}, + {v[3], v[6]}, {v[4], v[5]}, {v[4], v[7]}, {v[5], v[8]}, {v[6], v[7]}, {v[7], v[8]}}; + int egdeSize = sizeof(edges) / sizeof(edges[0]); + GraphAdjList *graph = newGraphAdjList(); + // Add all vertices and edges + for (int i = 0; i < size; i++) { + addVertex(graph, v[i]); + } + for (int i = 0; i < egdeSize; i++) { + addEdge(graph, edges[i][0], edges[i][1]); + } + printf("\nAfter initialization, graph is\n"); + printGraph(graph); + + // Breadth-first traversal + // Vertex traversal sequence + Vertex *res[MAX_SIZE]; + int resSize = 0; + // Used to record visited vertices + Vertex *visited[MAX_SIZE]; + int visitedSize = 0; + graphBFS(graph, v[0], res, &resSize, visited, &visitedSize); + printf("\nBreadth-first traversal (BFS) vertex sequence is\n"); + printArray(vetsToVals(res, resSize), resSize); + + // Free memory + delGraphAdjList(graph); + free(v); + return 0; +} diff --git a/en/codes/c/chapter_graph/graph_dfs.c b/en/codes/c/chapter_graph/graph_dfs.c new file mode 100644 index 000000000..a88bd36b9 --- /dev/null +++ b/en/codes/c/chapter_graph/graph_dfs.c @@ -0,0 +1,75 @@ +/** + * File: graph_dfs.c + * Created Time: 2023-07-13 + * Author: NI-SW (947743645@qq.com) + */ + +#include "graph_adjacency_list.c" + +// Assume max node count is 100 +#define MAX_SIZE 100 + +/* Check if vertex has been visited */ +int isVisited(Vertex **res, int size, Vertex *vet) { + // Traverse to find node using O(n) time + for (int i = 0; i < size; i++) { + if (res[i] == vet) { + return 1; + } + } + return 0; +} + +/* Depth-first traversal helper function */ +void dfs(GraphAdjList *graph, Vertex **res, int *resSize, Vertex *vet) { + // Record visited vertex + res[(*resSize)++] = vet; + // Traverse all adjacent vertices of this vertex + AdjListNode *node = findNode(graph, vet); + while (node != NULL) { + // Skip vertices that have been visited + if (!isVisited(res, *resSize, node->vertex)) { + // Recursively visit adjacent vertices + dfs(graph, res, resSize, node->vertex); + } + node = node->next; + } +} + +/* Depth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +void graphDFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize) { + dfs(graph, res, resSize, startVet); +} + +/* Driver Code */ +int main() { + // Add edge + int vals[] = {0, 1, 2, 3, 4, 5, 6}; + int size = sizeof(vals) / sizeof(vals[0]); + Vertex **v = valsToVets(vals, size); + Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[5]}, {v[4], v[5]}, {v[5], v[6]}}; + int egdeSize = sizeof(edges) / sizeof(edges[0]); + GraphAdjList *graph = newGraphAdjList(); + // Add all vertices and edges + for (int i = 0; i < size; i++) { + addVertex(graph, v[i]); + } + for (int i = 0; i < egdeSize; i++) { + addEdge(graph, edges[i][0], edges[i][1]); + } + printf("\nAfter initialization, graph is\n"); + printGraph(graph); + + // Depth-first traversal + Vertex *res[MAX_SIZE]; + int resSize = 0; + graphDFS(graph, v[0], res, &resSize); + printf("\nDepth-first traversal (DFS) vertex sequence is\n"); + printArray(vetsToVals(res, resSize), resSize); + + // Free memory + delGraphAdjList(graph); + free(v); + return 0; +} diff --git a/en/codes/c/chapter_greedy/CMakeLists.txt b/en/codes/c/chapter_greedy/CMakeLists.txt new file mode 100644 index 000000000..b8e6ca425 --- /dev/null +++ b/en/codes/c/chapter_greedy/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(coin_change_greedy coin_change_greedy.c) +add_executable(fractional_knapsack fractional_knapsack.c) +add_executable(max_capacity max_capacity.c) +add_executable(max_product_cutting max_product_cutting.c) + +if (NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC") + target_link_libraries(max_product_cutting m) +endif() diff --git a/en/codes/c/chapter_greedy/coin_change_greedy.c b/en/codes/c/chapter_greedy/coin_change_greedy.c new file mode 100644 index 000000000..b8f8908ae --- /dev/null +++ b/en/codes/c/chapter_greedy/coin_change_greedy.c @@ -0,0 +1,60 @@ +/** + * File: coin_change_greedy.c + * Created Time: 2023-09-07 + * Author: lwbaptx (lwbaptx@gmail.com) + */ + +#include "../utils/common.h" + +/* Coin change: Greedy algorithm */ +int coinChangeGreedy(int *coins, int size, int amt) { + // Assume coins list is sorted + int i = size - 1; + int count = 0; + // Loop to make greedy choices until no remaining amount + while (amt > 0) { + // Find the coin that is less than and closest to the remaining amount + while (i > 0 && coins[i] > amt) { + i--; + } + // Choose coins[i] + amt -= coins[i]; + count++; + } + // If no feasible solution is found, return -1 + return amt == 0 ? count : -1; +} + +/* Driver Code */ +int main() { + // Greedy algorithm: Can guarantee finding the global optimal solution + int coins1[6] = {1, 5, 10, 20, 50, 100}; + int amt = 186; + int res = coinChangeGreedy(coins1, 6, amt); + printf("\ncoins = "); + printArray(coins1, 6); + printf("amt = %d\n", amt); + printf("Minimum number of coins needed to make %d is %d\n", amt, res); + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + int coins2[3] = {1, 20, 50}; + amt = 60; + res = coinChangeGreedy(coins2, 3, amt); + printf("\ncoins = "); + printArray(coins2, 3); + printf("amt = %d\n", amt); + printf("Minimum number of coins needed to make %d is %d\n", amt, res); + printf("Actually minimum needed is 3, i.e., 20 + 20 + 20\n"); + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + int coins3[3] = {1, 49, 50}; + amt = 98; + res = coinChangeGreedy(coins3, 3, amt); + printf("\ncoins = "); + printArray(coins3, 3); + printf("amt = %d\n", amt); + printf("Minimum number of coins needed to make %d is %d\n", amt, res); + printf("Actually minimum needed is 2, i.e., 49 + 49\n"); + + return 0; +} diff --git a/en/codes/c/chapter_greedy/fractional_knapsack.c b/en/codes/c/chapter_greedy/fractional_knapsack.c new file mode 100644 index 000000000..22878a163 --- /dev/null +++ b/en/codes/c/chapter_greedy/fractional_knapsack.c @@ -0,0 +1,60 @@ +/** + * File: fractional_knapsack.c + * Created Time: 2023-09-14 + * Author: xianii (xianyi.xia@outlook.com) + */ + +#include "../utils/common.h" + +/* Item */ +typedef struct { + int w; // Item weight + int v; // Item value +} Item; + +/* Sort by value density */ +int sortByValueDensity(const void *a, const void *b) { + Item *t1 = (Item *)a; + Item *t2 = (Item *)b; + return (float)(t1->v) / t1->w < (float)(t2->v) / t2->w; +} + +/* Fractional knapsack: Greedy algorithm */ +float fractionalKnapsack(int wgt[], int val[], int itemCount, int cap) { + // Create item list with two attributes: weight, value + Item *items = malloc(sizeof(Item) * itemCount); + for (int i = 0; i < itemCount; i++) { + items[i] = (Item){.w = wgt[i], .v = val[i]}; + } + // Sort by unit value item.v / item.w from high to low + qsort(items, (size_t)itemCount, sizeof(Item), sortByValueDensity); + // Loop for greedy selection + float res = 0.0; + for (int i = 0; i < itemCount; i++) { + if (items[i].w <= cap) { + // If remaining capacity is sufficient, put the entire current item into the knapsack + res += items[i].v; + cap -= items[i].w; + } else { + // If remaining capacity is insufficient, put part of the current item into the knapsack + res += (float)cap / items[i].w * items[i].v; + cap = 0; + break; + } + } + free(items); + return res; +} + +/* Driver Code */ +int main(void) { + int wgt[] = {10, 20, 30, 40, 50}; + int val[] = {50, 120, 150, 210, 240}; + int capacity = 50; + + // Greedy algorithm + float res = fractionalKnapsack(wgt, val, sizeof(wgt) / sizeof(int), capacity); + printf("Maximum item value not exceeding knapsack capacity is %0.2f\n", res); + + return 0; +} diff --git a/en/codes/c/chapter_greedy/max_capacity.c b/en/codes/c/chapter_greedy/max_capacity.c new file mode 100644 index 000000000..a11df42ef --- /dev/null +++ b/en/codes/c/chapter_greedy/max_capacity.c @@ -0,0 +1,49 @@ +/** + * File: max_capacity.c + * Created Time: 2023-09-15 + * Author: xianii (xianyi.xia@outlook.com) + */ + +#include "../utils/common.h" + +/* Find minimum value */ +int myMin(int a, int b) { + return a < b ? a : b; +} +/* Find maximum value */ +int myMax(int a, int b) { + return a > b ? a : b; +} + +/* Max capacity: Greedy algorithm */ +int maxCapacity(int ht[], int htLength) { + // Initialize i, j to be at both ends of the array + int i = 0; + int j = htLength - 1; + // Initial max capacity is 0 + int res = 0; + // Loop for greedy selection until the two boards meet + while (i < j) { + // Update max capacity + int capacity = myMin(ht[i], ht[j]) * (j - i); + res = myMax(res, capacity); + // Move the shorter board inward + if (ht[i] < ht[j]) { + i++; + } else { + j--; + } + } + return res; +} + +/* Driver Code */ +int main(void) { + int ht[] = {3, 8, 5, 2, 7, 7, 3, 4}; + + // Greedy algorithm + int res = maxCapacity(ht, sizeof(ht) / sizeof(int)); + printf("Maximum capacity is %d\n", res); + + return 0; +} diff --git a/en/codes/c/chapter_greedy/max_product_cutting.c b/en/codes/c/chapter_greedy/max_product_cutting.c new file mode 100644 index 000000000..7f34abd47 --- /dev/null +++ b/en/codes/c/chapter_greedy/max_product_cutting.c @@ -0,0 +1,38 @@ +/** + * File: max_product_cutting.c + * Created Time: 2023-09-15 + * Author: xianii (xianyi.xia@outlook.com) + */ + +#include "../utils/common.h" + +/* Max product cutting: Greedy algorithm */ +int maxProductCutting(int n) { + // When n <= 3, must cut out a 1 + if (n <= 3) { + return 1 * (n - 1); + } + // Greedily cut out 3, a is the number of 3s, b is the remainder + int a = n / 3; + int b = n % 3; + if (b == 1) { + // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + return pow(3, a - 1) * 2 * 2; + } + if (b == 2) { + // When the remainder is 2, do nothing + return pow(3, a) * 2; + } + // When the remainder is 0, do nothing + return pow(3, a); +} + +/* Driver Code */ +int main(void) { + int n = 58; + // Greedy algorithm + int res = maxProductCutting(n); + printf("Maximum cutting product is %d\n", res); + + return 0; +} diff --git a/en/codes/c/chapter_hashing/CMakeLists.txt b/en/codes/c/chapter_hashing/CMakeLists.txt new file mode 100644 index 000000000..9ac951ae4 --- /dev/null +++ b/en/codes/c/chapter_hashing/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(array_hash_map array_hash_map.c) +add_executable(hash_map_chaining hash_map_chaining.c) +add_executable(hash_map_open_addressing hash_map_open_addressing.c) +add_executable(simple_hash simple_hash.c) diff --git a/en/codes/c/chapter_hashing/array_hash_map.c b/en/codes/c/chapter_hashing/array_hash_map.c new file mode 100644 index 000000000..94e4abff2 --- /dev/null +++ b/en/codes/c/chapter_hashing/array_hash_map.c @@ -0,0 +1,215 @@ +/** + * File: array_hash_map.c + * Created Time: 2023-03-18 + * Author: Guanngxu (446678850@qq.com) + */ + +#include "../utils/common.h" + +/* Default hash table size */ +#define MAX_SIZE 100 + +/* Key-value pair int->string */ +typedef struct { + int key; + char *val; +} Pair; + +/* Collection of key-value pairs */ +typedef struct { + void *set; + int len; +} MapSet; + +/* Hash table based on array implementation */ +typedef struct { + Pair *buckets[MAX_SIZE]; +} ArrayHashMap; + +/* Constructor */ +ArrayHashMap *newArrayHashMap() { + ArrayHashMap *hmap = malloc(sizeof(ArrayHashMap)); + for (int i=0; i < MAX_SIZE; i++) { + hmap->buckets[i] = NULL; + } + return hmap; +} + +/* Destructor */ +void delArrayHashMap(ArrayHashMap *hmap) { + for (int i = 0; i < MAX_SIZE; i++) { + if (hmap->buckets[i] != NULL) { + free(hmap->buckets[i]->val); + free(hmap->buckets[i]); + } + } + free(hmap); +} + +/* Hash function */ +int hashFunc(int key) { + int index = key % MAX_SIZE; + return index; +} + +/* Query operation */ +const char *get(const ArrayHashMap *hmap, const int key) { + int index = hashFunc(key); + const Pair *Pair = hmap->buckets[index]; + if (Pair == NULL) + return NULL; + return Pair->val; +} + +/* Add operation */ +void put(ArrayHashMap *hmap, const int key, const char *val) { + Pair *Pair = malloc(sizeof(Pair)); + Pair->key = key; + Pair->val = malloc(strlen(val) + 1); + strcpy(Pair->val, val); + + int index = hashFunc(key); + hmap->buckets[index] = Pair; +} + +/* Remove operation */ +void removeItem(ArrayHashMap *hmap, const int key) { + int index = hashFunc(key); + free(hmap->buckets[index]->val); + free(hmap->buckets[index]); + hmap->buckets[index] = NULL; +} + +/* Get all key-value pairs */ +void pairSet(ArrayHashMap *hmap, MapSet *set) { + Pair *entries; + int i = 0, index = 0; + int total = 0; + /* Count valid key-value pairs */ + for (i = 0; i < MAX_SIZE; i++) { + if (hmap->buckets[i] != NULL) { + total++; + } + } + entries = malloc(sizeof(Pair) * total); + for (i = 0; i < MAX_SIZE; i++) { + if (hmap->buckets[i] != NULL) { + entries[index].key = hmap->buckets[i]->key; + entries[index].val = malloc(strlen(hmap->buckets[i]->val) + 1); + strcpy(entries[index].val, hmap->buckets[i]->val); + index++; + } + } + set->set = entries; + set->len = total; +} + +/* Get all keys */ +void keySet(ArrayHashMap *hmap, MapSet *set) { + int *keys; + int i = 0, index = 0; + int total = 0; + /* Count valid key-value pairs */ + for (i = 0; i < MAX_SIZE; i++) { + if (hmap->buckets[i] != NULL) { + total++; + } + } + keys = malloc(total * sizeof(int)); + for (i = 0; i < MAX_SIZE; i++) { + if (hmap->buckets[i] != NULL) { + keys[index] = hmap->buckets[i]->key; + index++; + } + } + set->set = keys; + set->len = total; +} + +/* Get all values */ +void valueSet(ArrayHashMap *hmap, MapSet *set) { + char **vals; + int i = 0, index = 0; + int total = 0; + /* Count valid key-value pairs */ + for (i = 0; i < MAX_SIZE; i++) { + if (hmap->buckets[i] != NULL) { + total++; + } + } + vals = malloc(total * sizeof(char *)); + for (i = 0; i < MAX_SIZE; i++) { + if (hmap->buckets[i] != NULL) { + vals[index] = hmap->buckets[i]->val; + index++; + } + } + set->set = vals; + set->len = total; +} + +/* Print hash table */ +void print(ArrayHashMap *hmap) { + int i; + MapSet set; + pairSet(hmap, &set); + Pair *entries = (Pair *)set.set; + for (i = 0; i < set.len; i++) { + printf("%d -> %s\n", entries[i].key, entries[i].val); + } + free(set.set); +} + +/* Driver Code */ +int main() { + /* Initialize hash table */ + ArrayHashMap *hmap = newArrayHashMap(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + put(hmap, 12836, "Xiao Ha"); + put(hmap, 15937, "Xiao Luo"); + put(hmap, 16750, "Xiao Suan"); + put(hmap, 13276, "Xiao Fa"); + put(hmap, 10583, "Xiao Ya"); + printf("\nAfter addition, hash table is\nKey -> Value\n"); + print(hmap); + + /* Query operation */ + // Input key into hash table to get value + const char *name = get(hmap, 15937); + printf("\nInput student ID 15937, found name %s\n", name); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + removeItem(hmap, 10583); + printf("\nAfter deleting 10583, hash table is\nKey -> Value\n"); + print(hmap); + + /* Traverse hash table */ + int i; + + printf("\nTraverse key-value pairs Key->Value\n"); + print(hmap); + + MapSet set; + + keySet(hmap, &set); + int *keys = (int *)set.set; + printf("\nTraverse keys only Key\n"); + for (i = 0; i < set.len; i++) { + printf("%d\n", keys[i]); + } + free(set.set); + + valueSet(hmap, &set); + char **vals = (char **)set.set; + printf("\nTraverse values only Value\n"); + for (i = 0; i < set.len; i++) { + printf("%s\n", vals[i]); + } + free(set.set); + + delArrayHashMap(hmap); + return 0; +} diff --git a/en/codes/c/chapter_hashing/hash_map_chaining.c b/en/codes/c/chapter_hashing/hash_map_chaining.c new file mode 100644 index 000000000..c2fd415d5 --- /dev/null +++ b/en/codes/c/chapter_hashing/hash_map_chaining.c @@ -0,0 +1,213 @@ +/** + * File: hash_map_chaining.c + * Created Time: 2023-10-13 + * Author: SenMing (1206575349@qq.com), krahets (krahets@163.com) + */ + +#include +#include +#include + +// Assume max val length is 100 +#define MAX_SIZE 100 + +/* Key-value pair */ +typedef struct { + int key; + char val[MAX_SIZE]; +} Pair; + +/* Linked list node */ +typedef struct Node { + Pair *pair; + struct Node *next; +} Node; + +/* Hash table with separate chaining */ +typedef struct { + int size; // Number of key-value pairs + int capacity; // Hash table capacity + double loadThres; // Load factor threshold for triggering expansion + int extendRatio; // Expansion multiplier + Node **buckets; // Bucket array +} HashMapChaining; + +/* Constructor */ +HashMapChaining *newHashMapChaining() { + HashMapChaining *hashMap = (HashMapChaining *)malloc(sizeof(HashMapChaining)); + hashMap->size = 0; + hashMap->capacity = 4; + hashMap->loadThres = 2.0 / 3.0; + hashMap->extendRatio = 2; + hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *)); + for (int i = 0; i < hashMap->capacity; i++) { + hashMap->buckets[i] = NULL; + } + return hashMap; +} + +/* Destructor */ +void delHashMapChaining(HashMapChaining *hashMap) { + for (int i = 0; i < hashMap->capacity; i++) { + Node *cur = hashMap->buckets[i]; + while (cur) { + Node *tmp = cur; + cur = cur->next; + free(tmp->pair); + free(tmp); + } + } + free(hashMap->buckets); + free(hashMap); +} + +/* Hash function */ +int hashFunc(HashMapChaining *hashMap, int key) { + return key % hashMap->capacity; +} + +/* Load factor */ +double loadFactor(HashMapChaining *hashMap) { + return (double)hashMap->size / (double)hashMap->capacity; +} + +/* Query operation */ +char *get(HashMapChaining *hashMap, int key) { + int index = hashFunc(hashMap, key); + // Traverse bucket, if key is found, return corresponding val + Node *cur = hashMap->buckets[index]; + while (cur) { + if (cur->pair->key == key) { + return cur->pair->val; + } + cur = cur->next; + } + return ""; // Return empty string if key not found +} + +/* Add operation */ +void put(HashMapChaining *hashMap, int key, const char *val); + +/* Expand hash table */ +void extend(HashMapChaining *hashMap) { + // Temporarily store the original hash table + int oldCapacity = hashMap->capacity; + Node **oldBuckets = hashMap->buckets; + // Initialize expanded new hash table + hashMap->capacity *= hashMap->extendRatio; + hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *)); + for (int i = 0; i < hashMap->capacity; i++) { + hashMap->buckets[i] = NULL; + } + hashMap->size = 0; + // Move key-value pairs from original hash table to new hash table + for (int i = 0; i < oldCapacity; i++) { + Node *cur = oldBuckets[i]; + while (cur) { + put(hashMap, cur->pair->key, cur->pair->val); + Node *temp = cur; + cur = cur->next; + // Free memory + free(temp->pair); + free(temp); + } + } + + free(oldBuckets); +} + +/* Add operation */ +void put(HashMapChaining *hashMap, int key, const char *val) { + // When load factor exceeds threshold, perform expansion + if (loadFactor(hashMap) > hashMap->loadThres) { + extend(hashMap); + } + int index = hashFunc(hashMap, key); + // Traverse bucket, if specified key is encountered, update corresponding val and return + Node *cur = hashMap->buckets[index]; + while (cur) { + if (cur->pair->key == key) { + strcpy(cur->pair->val, val); // If specified key is found, update corresponding val and return + return; + } + cur = cur->next; + } + // If key not found, add key-value pair to list head + Pair *newPair = (Pair *)malloc(sizeof(Pair)); + newPair->key = key; + strcpy(newPair->val, val); + Node *newNode = (Node *)malloc(sizeof(Node)); + newNode->pair = newPair; + newNode->next = hashMap->buckets[index]; + hashMap->buckets[index] = newNode; + hashMap->size++; +} + +/* Remove operation */ +void removeItem(HashMapChaining *hashMap, int key) { + int index = hashFunc(hashMap, key); + Node *cur = hashMap->buckets[index]; + Node *pre = NULL; + while (cur) { + if (cur->pair->key == key) { + // Remove key-value pair from it + if (pre) { + pre->next = cur->next; + } else { + hashMap->buckets[index] = cur->next; + } + // Free memory + free(cur->pair); + free(cur); + hashMap->size--; + return; + } + pre = cur; + cur = cur->next; + } +} + +/* Print hash table */ +void print(HashMapChaining *hashMap) { + for (int i = 0; i < hashMap->capacity; i++) { + Node *cur = hashMap->buckets[i]; + printf("["); + while (cur) { + printf("%d -> %s, ", cur->pair->key, cur->pair->val); + cur = cur->next; + } + printf("]\n"); + } +} + +/* Driver Code */ +int main() { + /* Initialize hash table */ + HashMapChaining *hashMap = newHashMapChaining(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + put(hashMap, 12836, "Xiao Ha"); + put(hashMap, 15937, "Xiao Luo"); + put(hashMap, 16750, "Xiao Suan"); + put(hashMap, 13276, "Xiao Fa"); + put(hashMap, 10583, "Xiao Ya"); + printf("\nAfter addition, hash table is\nKey -> Value\n"); + print(hashMap); + + /* Query operation */ + // Input key into hash table to get value + char *name = get(hashMap, 13276); + printf("\nInput student ID 13276, found name %s\n", name); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + removeItem(hashMap, 12836); + printf("\nAfter deleting student ID 12836, hash table is\nKey -> Value\n"); + print(hashMap); + + /* Free hash table space */ + delHashMapChaining(hashMap); + + return 0; +} diff --git a/en/codes/c/chapter_hashing/hash_map_open_addressing.c b/en/codes/c/chapter_hashing/hash_map_open_addressing.c new file mode 100644 index 000000000..d6ad81f1f --- /dev/null +++ b/en/codes/c/chapter_hashing/hash_map_open_addressing.c @@ -0,0 +1,211 @@ +/** + * File: hash_map_open_addressing.c + * Created Time: 2023-10-6 + * Author: lclc6 (w1929522410@163.com) + */ + +#include "../utils/common.h" + +/* Hash table with open addressing */ +typedef struct { + int key; + char *val; +} Pair; + +/* Hash table with open addressing */ +typedef struct { + int size; // Number of key-value pairs + int capacity; // Hash table capacity + double loadThres; // Load factor threshold for triggering expansion + int extendRatio; // Expansion multiplier + Pair **buckets; // Bucket array + Pair *TOMBSTONE; // Removal marker +} HashMapOpenAddressing; + +// Function declaration +void extend(HashMapOpenAddressing *hashMap); + +/* Constructor */ +HashMapOpenAddressing *newHashMapOpenAddressing() { + HashMapOpenAddressing *hashMap = (HashMapOpenAddressing *)malloc(sizeof(HashMapOpenAddressing)); + hashMap->size = 0; + hashMap->capacity = 4; + hashMap->loadThres = 2.0 / 3.0; + hashMap->extendRatio = 2; + hashMap->buckets = (Pair **)calloc(hashMap->capacity, sizeof(Pair *)); + hashMap->TOMBSTONE = (Pair *)malloc(sizeof(Pair)); + hashMap->TOMBSTONE->key = -1; + hashMap->TOMBSTONE->val = "-1"; + + return hashMap; +} + +/* Destructor */ +void delHashMapOpenAddressing(HashMapOpenAddressing *hashMap) { + for (int i = 0; i < hashMap->capacity; i++) { + Pair *pair = hashMap->buckets[i]; + if (pair != NULL && pair != hashMap->TOMBSTONE) { + free(pair->val); + free(pair); + } + } + free(hashMap->buckets); + free(hashMap->TOMBSTONE); + free(hashMap); +} + +/* Hash function */ +int hashFunc(HashMapOpenAddressing *hashMap, int key) { + return key % hashMap->capacity; +} + +/* Load factor */ +double loadFactor(HashMapOpenAddressing *hashMap) { + return (double)hashMap->size / (double)hashMap->capacity; +} + +/* Search for bucket index corresponding to key */ +int findBucket(HashMapOpenAddressing *hashMap, int key) { + int index = hashFunc(hashMap, key); + int firstTombstone = -1; + // Linear probing, break when encountering an empty bucket + while (hashMap->buckets[index] != NULL) { + // If key is encountered, return the corresponding bucket index + if (hashMap->buckets[index]->key == key) { + // If a removal marker was encountered before, move the key-value pair to that index + if (firstTombstone != -1) { + hashMap->buckets[firstTombstone] = hashMap->buckets[index]; + hashMap->buckets[index] = hashMap->TOMBSTONE; + return firstTombstone; // Return the moved bucket index + } + return index; // Return bucket index + } + // Record the first removal marker encountered + if (firstTombstone == -1 && hashMap->buckets[index] == hashMap->TOMBSTONE) { + firstTombstone = index; + } + // Calculate bucket index, wrap around to the head if past the tail + index = (index + 1) % hashMap->capacity; + } + // If key does not exist, return the index for insertion + return firstTombstone == -1 ? index : firstTombstone; +} + +/* Query operation */ +char *get(HashMapOpenAddressing *hashMap, int key) { + // Search for bucket index corresponding to key + int index = findBucket(hashMap, key); + // If key-value pair is found, return corresponding val + if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) { + return hashMap->buckets[index]->val; + } + // Return empty string if key-value pair does not exist + return ""; +} + +/* Add operation */ +void put(HashMapOpenAddressing *hashMap, int key, char *val) { + // When load factor exceeds threshold, perform expansion + if (loadFactor(hashMap) > hashMap->loadThres) { + extend(hashMap); + } + // Search for bucket index corresponding to key + int index = findBucket(hashMap, key); + // If key-value pair is found, overwrite val and return + if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) { + free(hashMap->buckets[index]->val); + hashMap->buckets[index]->val = (char *)malloc(sizeof(strlen(val) + 1)); + strcpy(hashMap->buckets[index]->val, val); + hashMap->buckets[index]->val[strlen(val)] = '\0'; + return; + } + // If key-value pair does not exist, add the key-value pair + Pair *pair = (Pair *)malloc(sizeof(Pair)); + pair->key = key; + pair->val = (char *)malloc(sizeof(strlen(val) + 1)); + strcpy(pair->val, val); + pair->val[strlen(val)] = '\0'; + + hashMap->buckets[index] = pair; + hashMap->size++; +} + +/* Remove operation */ +void removeItem(HashMapOpenAddressing *hashMap, int key) { + // Search for bucket index corresponding to key + int index = findBucket(hashMap, key); + // If key-value pair is found, overwrite it with removal marker + if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) { + Pair *pair = hashMap->buckets[index]; + free(pair->val); + free(pair); + hashMap->buckets[index] = hashMap->TOMBSTONE; + hashMap->size--; + } +} + +/* Expand hash table */ +void extend(HashMapOpenAddressing *hashMap) { + // Temporarily store the original hash table + Pair **bucketsTmp = hashMap->buckets; + int oldCapacity = hashMap->capacity; + // Initialize expanded new hash table + hashMap->capacity *= hashMap->extendRatio; + hashMap->buckets = (Pair **)calloc(hashMap->capacity, sizeof(Pair *)); + hashMap->size = 0; + // Move key-value pairs from original hash table to new hash table + for (int i = 0; i < oldCapacity; i++) { + Pair *pair = bucketsTmp[i]; + if (pair != NULL && pair != hashMap->TOMBSTONE) { + put(hashMap, pair->key, pair->val); + free(pair->val); + free(pair); + } + } + free(bucketsTmp); +} + +/* Print hash table */ +void print(HashMapOpenAddressing *hashMap) { + for (int i = 0; i < hashMap->capacity; i++) { + Pair *pair = hashMap->buckets[i]; + if (pair == NULL) { + printf("NULL\n"); + } else if (pair == hashMap->TOMBSTONE) { + printf("TOMBSTONE\n"); + } else { + printf("%d -> %s\n", pair->key, pair->val); + } + } +} + +/* Driver Code */ +int main() { + // Initialize hash table + HashMapOpenAddressing *hashmap = newHashMapOpenAddressing(); + + // Add operation + // Add key-value pair (key, val) to the hash table + put(hashmap, 12836, "Xiao Ha"); + put(hashmap, 15937, "Xiao Luo"); + put(hashmap, 16750, "Xiao Suan"); + put(hashmap, 13276, "Xiao Fa"); + put(hashmap, 10583, "Xiao Ya"); + printf("\nAfter addition, hash table is\nKey -> Value\n"); + print(hashmap); + + // Query operation + // Input key into hash table to get value val + char *name = get(hashmap, 13276); + printf("\nInput student ID 13276, found name %s\n", name); + + // Remove operation + // Remove key-value pair (key, val) from hash table + removeItem(hashmap, 16750); + printf("\nAfter deleting 16750, hash table is\nKey -> Value\n"); + print(hashmap); + + // Destroy hash table + delHashMapOpenAddressing(hashmap); + return 0; +} diff --git a/en/codes/c/chapter_hashing/simple_hash.c b/en/codes/c/chapter_hashing/simple_hash.c new file mode 100644 index 000000000..41c1df04c --- /dev/null +++ b/en/codes/c/chapter_hashing/simple_hash.c @@ -0,0 +1,68 @@ +/** + * File: simple_hash.c + * Created Time: 2023-09-09 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Additive hash */ +int addHash(char *key) { + long long hash = 0; + const int MODULUS = 1000000007; + for (int i = 0; i < strlen(key); i++) { + hash = (hash + (unsigned char)key[i]) % MODULUS; + } + return (int)hash; +} + +/* Multiplicative hash */ +int mulHash(char *key) { + long long hash = 0; + const int MODULUS = 1000000007; + for (int i = 0; i < strlen(key); i++) { + hash = (31 * hash + (unsigned char)key[i]) % MODULUS; + } + return (int)hash; +} + +/* XOR hash */ +int xorHash(char *key) { + int hash = 0; + const int MODULUS = 1000000007; + + for (int i = 0; i < strlen(key); i++) { + hash ^= (unsigned char)key[i]; + } + return hash & MODULUS; +} + +/* Rotational hash */ +int rotHash(char *key) { + long long hash = 0; + const int MODULUS = 1000000007; + for (int i = 0; i < strlen(key); i++) { + hash = ((hash << 4) ^ (hash >> 28) ^ (unsigned char)key[i]) % MODULUS; + } + + return (int)hash; +} + +/* Driver Code */ +int main() { + char *key = "Hello Algo"; + + int hash = addHash(key); + printf("Additive hash value is %d\n", hash); + + hash = mulHash(key); + printf("Multiplicative hash value is %d\n", hash); + + hash = xorHash(key); + printf("XOR hash value is %d\n", hash); + + hash = rotHash(key); + printf("Rotational hash value is %d\n", hash); + + return 0; +} diff --git a/en/codes/c/chapter_heap/CMakeLists.txt b/en/codes/c/chapter_heap/CMakeLists.txt new file mode 100644 index 000000000..357ec702c --- /dev/null +++ b/en/codes/c/chapter_heap/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(my_heap_test my_heap_test.c) +add_executable(top_k top_k.c) diff --git a/en/codes/c/chapter_heap/my_heap.c b/en/codes/c/chapter_heap/my_heap.c new file mode 100644 index 000000000..929772ee7 --- /dev/null +++ b/en/codes/c/chapter_heap/my_heap.c @@ -0,0 +1,152 @@ +/** + * File: my_heap.c + * Created Time: 2023-01-15 + * Author: Reanon (793584285@qq.com) + */ + +#include "../utils/common.h" + +#define MAX_SIZE 5000 + +/* Max heap */ +typedef struct { + // size represents the actual number of elements + int size; + // Use pre-allocated array to avoid expansion + int data[MAX_SIZE]; +} MaxHeap; + +// Function declaration +void siftDown(MaxHeap *maxHeap, int i); +void siftUp(MaxHeap *maxHeap, int i); +int parent(MaxHeap *maxHeap, int i); + +/* Constructor, build heap from slice */ +MaxHeap *newMaxHeap(int nums[], int size) { + // Push all elements to heap + MaxHeap *maxHeap = (MaxHeap *)malloc(sizeof(MaxHeap)); + maxHeap->size = size; + memcpy(maxHeap->data, nums, size * sizeof(int)); + for (int i = parent(maxHeap, size - 1); i >= 0; i--) { + // Heapify all nodes except leaf nodes + siftDown(maxHeap, i); + } + return maxHeap; +} + +/* Destructor */ +void delMaxHeap(MaxHeap *maxHeap) { + // Free memory + free(maxHeap); +} + +/* Get index of left child node */ +int left(MaxHeap *maxHeap, int i) { + return 2 * i + 1; +} + +/* Get index of right child node */ +int right(MaxHeap *maxHeap, int i) { + return 2 * i + 2; +} + +/* Get index of parent node */ +int parent(MaxHeap *maxHeap, int i) { + return (i - 1) / 2; // Round down +} + +/* Swap elements */ +void swap(MaxHeap *maxHeap, int i, int j) { + int temp = maxHeap->data[i]; + maxHeap->data[i] = maxHeap->data[j]; + maxHeap->data[j] = temp; +} + +/* Get heap size */ +int size(MaxHeap *maxHeap) { + return maxHeap->size; +} + +/* Check if heap is empty */ +int isEmpty(MaxHeap *maxHeap) { + return maxHeap->size == 0; +} + +/* Access top element */ +int peek(MaxHeap *maxHeap) { + return maxHeap->data[0]; +} + +/* Element enters heap */ +void push(MaxHeap *maxHeap, int val) { + // By default, should not add this many nodes + if (maxHeap->size == MAX_SIZE) { + printf("heap is full!"); + return; + } + // Add node + maxHeap->data[maxHeap->size] = val; + maxHeap->size++; + + // Heapify from bottom to top + siftUp(maxHeap, maxHeap->size - 1); +} + +/* Element exits heap */ +int pop(MaxHeap *maxHeap) { + // Handle empty case + if (isEmpty(maxHeap)) { + printf("heap is empty!"); + return INT_MAX; + } + // Delete node + swap(maxHeap, 0, size(maxHeap) - 1); + // Remove node + int val = maxHeap->data[maxHeap->size - 1]; + maxHeap->size--; + // Return top element + siftDown(maxHeap, 0); + + // Return heap top element + return val; +} + +/* Starting from node i, heapify from top to bottom */ +void siftDown(MaxHeap *maxHeap, int i) { + while (true) { + // Find node with maximum value among nodes i, l, r, denoted as max + int l = left(maxHeap, i); + int r = right(maxHeap, i); + int max = i; + if (l < size(maxHeap) && maxHeap->data[l] > maxHeap->data[max]) { + max = l; + } + if (r < size(maxHeap) && maxHeap->data[r] > maxHeap->data[max]) { + max = r; + } + // Swap two nodes + if (max == i) { + break; + } + // Swap two nodes + swap(maxHeap, i, max); + // Loop downwards heapification + i = max; + } +} + +/* Starting from node i, heapify from bottom to top */ +void siftUp(MaxHeap *maxHeap, int i) { + while (true) { + // Get parent node of node i + int p = parent(maxHeap, i); + // When "crossing root node" or "node needs no repair", end heapify + if (p < 0 || maxHeap->data[i] <= maxHeap->data[p]) { + break; + } + // Swap two nodes + swap(maxHeap, i, p); + // Loop upward heapify + i = p; + } +} diff --git a/en/codes/c/chapter_heap/my_heap_test.c b/en/codes/c/chapter_heap/my_heap_test.c new file mode 100644 index 000000000..9e2fa8a7f --- /dev/null +++ b/en/codes/c/chapter_heap/my_heap_test.c @@ -0,0 +1,41 @@ +/** + * File: my_heap_test.c + * Created Time: 2023-01-15 + * Author: Reanon (793584285@qq.com) + */ + +#include "my_heap.c" + +/* Driver Code */ +int main() { + /* Initialize heap */ + // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + int nums[] = {9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2}; + MaxHeap *maxHeap = newMaxHeap(nums, sizeof(nums) / sizeof(int)); + printf("After input array and building heap\n"); + printHeap(maxHeap->data, maxHeap->size); + + /* Check if heap is empty */ + printf("\nHeap top element is %d\n", peek(maxHeap)); + + /* Element enters heap */ + push(maxHeap, 7); + printf("\nAfter element 7 pushes to heap\n"); + printHeap(maxHeap->data, maxHeap->size); + + /* Time complexity is O(n), not O(nlogn) */ + int top = pop(maxHeap); + printf("\nAfter heap top element %d exits heap\n", top); + printHeap(maxHeap->data, maxHeap->size); + + /* Get heap size */ + printf("\nHeap element count is %d\n", size(maxHeap)); + + /* Check if heap is empty */ + printf("\nIs heap empty %d\n", isEmpty(maxHeap)); + + // Free memory + delMaxHeap(maxHeap); + + return 0; +} diff --git a/en/codes/c/chapter_heap/top_k.c b/en/codes/c/chapter_heap/top_k.c new file mode 100644 index 000000000..7a82c6adb --- /dev/null +++ b/en/codes/c/chapter_heap/top_k.c @@ -0,0 +1,73 @@ +/** + * File: top_k.c + * Created Time: 2023-10-26 + * Author: krahets (krahets163.com) + */ + +#include "my_heap.c" + +/* Element enters heap */ +void pushMinHeap(MaxHeap *maxHeap, int val) { + // Negate element + push(maxHeap, -val); +} + +/* Element exits heap */ +int popMinHeap(MaxHeap *maxHeap) { + // Negate element + return -pop(maxHeap); +} + +/* Access top element */ +int peekMinHeap(MaxHeap *maxHeap) { + // Negate element + return -peek(maxHeap); +} + +/* Extract elements from heap */ +int *getMinHeap(MaxHeap *maxHeap) { + // Negate all heap elements and store in res array + int *res = (int *)malloc(maxHeap->size * sizeof(int)); + for (int i = 0; i < maxHeap->size; i++) { + res[i] = -maxHeap->data[i]; + } + return res; +} + +// Function to find k largest elements in array using heap +int *topKHeap(int *nums, int sizeNums, int k) { + // Python's heapq module implements min heap by default + // Note: We negate all heap elements to simulate min heap using max heap + int *empty = (int *)malloc(0); + MaxHeap *maxHeap = newMaxHeap(empty, 0); + // Enter the first k elements of array into heap + for (int i = 0; i < k; i++) { + pushMinHeap(maxHeap, nums[i]); + } + // Starting from the (k+1)th element, maintain heap length as k + for (int i = k; i < sizeNums; i++) { + // If current element is greater than top element, top element exits heap, current element enters heap + if (nums[i] > peekMinHeap(maxHeap)) { + popMinHeap(maxHeap); + pushMinHeap(maxHeap, nums[i]); + } + } + int *res = getMinHeap(maxHeap); + // Free memory + delMaxHeap(maxHeap); + return res; +} + +/* Driver Code */ +int main() { + int nums[] = {1, 7, 6, 3, 2}; + int k = 3; + int sizeNums = sizeof(nums) / sizeof(nums[0]); + + int *res = topKHeap(nums, sizeNums, k); + printf("The largest %d elements are: ", k); + printArray(res, k); + + free(res); + return 0; +} diff --git a/en/codes/c/chapter_searching/CMakeLists.txt b/en/codes/c/chapter_searching/CMakeLists.txt new file mode 100644 index 000000000..7b2a152d5 --- /dev/null +++ b/en/codes/c/chapter_searching/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(binary_search binary_search.c) +add_executable(two_sum two_sum.c) +add_executable(binary_search_edge binary_search_edge.c) +add_executable(binary_search_insertion binary_search_insertion.c) diff --git a/en/codes/c/chapter_searching/binary_search.c b/en/codes/c/chapter_searching/binary_search.c new file mode 100644 index 000000000..84247319a --- /dev/null +++ b/en/codes/c/chapter_searching/binary_search.c @@ -0,0 +1,59 @@ +/** + * File: binary_search.c + * Created Time: 2023-03-18 + * Author: Guanngxu (446678850@qq.com) + */ + +#include "../utils/common.h" + +/* Binary search (closed interval on both sides) */ +int binarySearch(int *nums, int len, int target) { + // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + int i = 0, j = len - 1; + // Loop, exit when the search interval is empty (empty when i > j) + while (i <= j) { + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) // This means target is in the interval [m+1, j] + i = m + 1; + else if (nums[m] > target) // This means target is in the interval [i, m-1] + j = m - 1; + else // Found the target element, return its index + return m; + } + // Target element not found, return -1 + return -1; +} + +/* Binary search (left-closed right-open interval) */ +int binarySearchLCRO(int *nums, int len, int target) { + // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + int i = 0, j = len; + // Loop, exit when the search interval is empty (empty when i = j) + while (i < j) { + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) // This means target is in the interval [m+1, j) + i = m + 1; + else if (nums[m] > target) // This means target is in the interval [i, m) + j = m; + else // Found the target element, return its index + return m; + } + // Target element not found, return -1 + return -1; +} + +/* Driver Code */ +int main() { + int target = 6; + int nums[10] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; + + /* Binary search (closed interval on both sides) */ + int index = binarySearch(nums, 10, target); + printf("Index of target element 6 = %d\n", index); + + /* Binary search (left-closed right-open interval) */ + index = binarySearchLCRO(nums, 10, target); + printf("Index of target element 6 = %d\n", index); + + return 0; +} diff --git a/en/codes/c/chapter_searching/binary_search_edge.c b/en/codes/c/chapter_searching/binary_search_edge.c new file mode 100644 index 000000000..d9bdc6858 --- /dev/null +++ b/en/codes/c/chapter_searching/binary_search_edge.c @@ -0,0 +1,67 @@ +/** + * File: binary_search_edge.c + * Created Time: 2023-09-09 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Binary search for insertion point (with duplicate elements) */ +int binarySearchInsertion(int *nums, int numSize, int target) { + int i = 0, j = numSize - 1; // Initialize closed interval [0, n-1] + while (i <= j) { + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) { + i = m + 1; // target is in the interval [m+1, j] + } else { + j = m - 1; // The first element less than target is in the interval [i, m-1] + } + } + // Return insertion point i + return i; +} + +/* Binary search for the leftmost target */ +int binarySearchLeftEdge(int *nums, int numSize, int target) { + // Equivalent to finding the insertion point of target + int i = binarySearchInsertion(nums, numSize, target); + // Target not found, return -1 + if (i == numSize || nums[i] != target) { + return -1; + } + // Found target, return index i + return i; +} + +/* Binary search for the rightmost target */ +int binarySearchRightEdge(int *nums, int numSize, int target) { + // Convert to finding the leftmost target + 1 + int i = binarySearchInsertion(nums, numSize, target + 1); + // j points to the rightmost target, i points to the first element greater than target + int j = i - 1; + // Target not found, return -1 + if (j == -1 || nums[j] != target) { + return -1; + } + // Found target, return index j + return j; +} + +/* Driver Code */ +int main() { + // Array with duplicate elements + int nums[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15}; + printf("\nArray nums = "); + printArray(nums, sizeof(nums) / sizeof(nums[0])); + + // Binary search left and right boundaries + int targets[] = {6, 7}; + for (int i = 0; i < sizeof(targets) / sizeof(targets[0]); i++) { + int index = binarySearchLeftEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]); + printf("Leftmost element %d index is %d\n", targets[i], index); + index = binarySearchRightEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]); + printf("Rightmost element %d index is %d\n", targets[i], index); + } + + return 0; +} \ No newline at end of file diff --git a/en/codes/c/chapter_searching/binary_search_insertion.c b/en/codes/c/chapter_searching/binary_search_insertion.c new file mode 100644 index 000000000..ec76a1571 --- /dev/null +++ b/en/codes/c/chapter_searching/binary_search_insertion.c @@ -0,0 +1,68 @@ +/** + * File: binary_search_insertion.c + * Created Time: 2023-09-09 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Binary search for insertion point (no duplicate elements) */ +int binarySearchInsertionSimple(int *nums, int numSize, int target) { + int i = 0, j = numSize - 1; // Initialize closed interval [0, n-1] + while (i <= j) { + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) { + i = m + 1; // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // target is in the interval [i, m-1] + } else { + return m; // Found target, return insertion point m + } + } + // Target not found, return insertion point i + return i; +} + +/* Binary search for insertion point (with duplicate elements) */ +int binarySearchInsertion(int *nums, int numSize, int target) { + int i = 0, j = numSize - 1; // Initialize closed interval [0, n-1] + while (i <= j) { + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) { + i = m + 1; // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // target is in the interval [i, m-1] + } else { + j = m - 1; // The first element less than target is in the interval [i, m-1] + } + } + // Return insertion point i + return i; +} + +/* Driver Code */ +int main() { + // Array without duplicate elements + int nums1[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; + printf("\nArray nums = "); + printArray(nums1, sizeof(nums1) / sizeof(nums1[0])); + // Binary search for insertion point + int targets1[] = {6, 9}; + for (int i = 0; i < sizeof(targets1) / sizeof(targets1[0]); i++) { + int index = binarySearchInsertionSimple(nums1, sizeof(nums1) / sizeof(nums1[0]), targets1[i]); + printf("Insertion point index for element %d is %d\n", targets1[i], index); + } + + // Array with duplicate elements + int nums2[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15}; + printf("\nArray nums = "); + printArray(nums2, sizeof(nums2) / sizeof(nums2[0])); + // Binary search for insertion point + int targets2[] = {2, 6, 20}; + for (int i = 0; i < sizeof(targets2) / sizeof(int); i++) { + int index = binarySearchInsertion(nums2, sizeof(nums2) / sizeof(nums2[0]), targets2[i]); + printf("Insertion point index for element %d is %d\n", targets2[i], index); + } + + return 0; +} diff --git a/en/codes/c/chapter_searching/two_sum.c b/en/codes/c/chapter_searching/two_sum.c new file mode 100644 index 000000000..34c106ebf --- /dev/null +++ b/en/codes/c/chapter_searching/two_sum.c @@ -0,0 +1,86 @@ +/** + * File: two_sum.c + * Created Time: 2023-01-19 + * Author: Reanon (793584285@qq.com) + */ + +#include "../utils/common.h" + +/* Method 1: Brute force enumeration */ +int *twoSumBruteForce(int *nums, int numsSize, int target, int *returnSize) { + for (int i = 0; i < numsSize; ++i) { + for (int j = i + 1; j < numsSize; ++j) { + if (nums[i] + nums[j] == target) { + int *res = malloc(sizeof(int) * 2); + res[0] = i, res[1] = j; + *returnSize = 2; + return res; + } + } + } + *returnSize = 0; + return NULL; +} + +/* Hash table */ +typedef struct { + int key; + int val; + UT_hash_handle hh; // Implemented using uthash.h +} HashTable; + +/* Hash table lookup */ +HashTable *find(HashTable *h, int key) { + HashTable *tmp; + HASH_FIND_INT(h, &key, tmp); + return tmp; +} + +/* Hash table element insertion */ +void insert(HashTable **h, int key, int val) { + HashTable *t = find(*h, key); + if (t == NULL) { + HashTable *tmp = malloc(sizeof(HashTable)); + tmp->key = key, tmp->val = val; + HASH_ADD_INT(*h, key, tmp); + } else { + t->val = val; + } +} + +/* Method 2: Auxiliary hash table */ +int *twoSumHashTable(int *nums, int numsSize, int target, int *returnSize) { + HashTable *hashtable = NULL; + for (int i = 0; i < numsSize; i++) { + HashTable *t = find(hashtable, target - nums[i]); + if (t != NULL) { + int *res = malloc(sizeof(int) * 2); + res[0] = t->val, res[1] = i; + *returnSize = 2; + return res; + } + insert(&hashtable, nums[i], i); + } + *returnSize = 0; + return NULL; +} + +/* Driver Code */ +int main() { + // ======= Test Case ======= + int nums[] = {2, 7, 11, 15}; + int target = 13; + // ====== Driver Code ====== + int returnSize; + int *res = twoSumBruteForce(nums, sizeof(nums) / sizeof(int), target, &returnSize); + // Method 1 + printf("Method 1 res = "); + printArray(res, returnSize); + + // Method 2 + res = twoSumHashTable(nums, sizeof(nums) / sizeof(int), target, &returnSize); + printf("Method 2 res = "); + printArray(res, returnSize); + + return 0; +} \ No newline at end of file diff --git a/en/codes/c/chapter_sorting/CMakeLists.txt b/en/codes/c/chapter_sorting/CMakeLists.txt new file mode 100644 index 000000000..88756b4c9 --- /dev/null +++ b/en/codes/c/chapter_sorting/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(bubble_sort bubble_sort.c) +add_executable(insertion_sort insertion_sort.c) +add_executable(quick_sort quick_sort.c) +add_executable(counting_sort counting_sort.c) +add_executable(radix_sort radix_sort.c) +add_executable(merge_sort merge_sort.c) +add_executable(heap_sort heap_sort.c) +add_executable(bucket_sort bucket_sort.c) +add_executable(selection_sort selection_sort.c) diff --git a/en/codes/c/chapter_sorting/bubble_sort.c b/en/codes/c/chapter_sorting/bubble_sort.c new file mode 100644 index 000000000..198e3f33d --- /dev/null +++ b/en/codes/c/chapter_sorting/bubble_sort.c @@ -0,0 +1,61 @@ +/** + * File: bubble_sort.c + * Created Time: 2022-12-26 + * Author: Listening (https://github.com/L-Super) + */ + +#include "../utils/common.h" + +/* Bubble sort */ +void bubbleSort(int nums[], int size) { + // Outer loop: unsorted range is [0, i] + for (int i = size - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + int temp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = temp; + } + } + } +} + +/* Bubble sort (flag optimization) */ +void bubbleSortWithFlag(int nums[], int size) { + // Outer loop: unsorted range is [0, i] + for (int i = size - 1; i > 0; i--) { + bool flag = false; + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + int temp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = temp; + flag = true; + } + } + if (!flag) + break; + } +} + +/* Driver Code */ +int main() { + int nums[6] = {4, 1, 3, 1, 5, 2}; + printf("After bubble sort: "); + bubbleSort(nums, 6); + for (int i = 0; i < 6; i++) { + printf("%d ", nums[i]); + } + + int nums1[6] = {4, 1, 3, 1, 5, 2}; + printf("\nAfter optimized bubble sort: "); + bubbleSortWithFlag(nums1, 6); + for (int i = 0; i < 6; i++) { + printf("%d ", nums1[i]); + } + printf("\n"); + + return 0; +} diff --git a/en/codes/c/chapter_sorting/bucket_sort.c b/en/codes/c/chapter_sorting/bucket_sort.c new file mode 100644 index 000000000..9d2b76d7d --- /dev/null +++ b/en/codes/c/chapter_sorting/bucket_sort.c @@ -0,0 +1,57 @@ +/** + * File: bucket_sort.c + * Created Time: 2023-05-30 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +#define SIZE 10 + +/* Comparison function for qsort */ +int compare(const void *a, const void *b) { + float fa = *(const float *)a; + float fb = *(const float *)b; + return (fa > fb) - (fa < fb); +} + +/* Bucket sort */ +void bucketSort(float nums[], int n) { + int k = n / 2; // Initialize k = n/2 buckets + int *sizes = malloc(k * sizeof(int)); // Record each bucket's size + float **buckets = malloc(k * sizeof(float *)); // Array of dynamic arrays (buckets) + // Pre-allocate sufficient space for each bucket + for (int i = 0; i < k; ++i) { + buckets[i] = (float *)malloc(n * sizeof(float)); + sizes[i] = 0; + } + // 1. Distribute array elements into various buckets + for (int i = 0; i < n; ++i) { + int idx = (int)(nums[i] * k); + buckets[idx][sizes[idx]++] = nums[i]; + } + // 2. Sort each bucket + for (int i = 0; i < k; ++i) { + qsort(buckets[i], sizes[i], sizeof(float), compare); + } + // 3. Merge sorted buckets + int idx = 0; + for (int i = 0; i < k; ++i) { + for (int j = 0; j < sizes[i]; ++j) { + nums[idx++] = buckets[i][j]; + } + // Free memory + free(buckets[i]); + } +} + +/* Driver Code */ +int main() { + // Assume input data is floating point, interval [0, 1) + float nums[SIZE] = {0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f}; + bucketSort(nums, SIZE); + printf("After bucket sort completes, nums = "); + printArrayFloat(nums, SIZE); + + return 0; +} diff --git a/en/codes/c/chapter_sorting/counting_sort.c b/en/codes/c/chapter_sorting/counting_sort.c new file mode 100644 index 000000000..9a101b2b8 --- /dev/null +++ b/en/codes/c/chapter_sorting/counting_sort.c @@ -0,0 +1,87 @@ +/** + * File: counting_sort.c + * Created Time: 2023-03-20 + * Author: Reanon (793584285@qq.com), Guanngxu (446678850@qq.com) + */ + +#include "../utils/common.h" + +/* Counting sort */ +// Simple implementation, cannot be used for sorting objects +void countingSortNaive(int nums[], int size) { + // 1. Count the maximum element m in the array + int m = 0; + for (int i = 0; i < size; i++) { + if (nums[i] > m) { + m = nums[i]; + } + } + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + int *counter = calloc(m + 1, sizeof(int)); + for (int i = 0; i < size; i++) { + counter[nums[i]]++; + } + // 3. Traverse counter, filling each element back into the original array nums + int i = 0; + for (int num = 0; num < m + 1; num++) { + for (int j = 0; j < counter[num]; j++, i++) { + nums[i] = num; + } + } + // 4. Free memory + free(counter); +} + +/* Counting sort */ +// Complete implementation, can sort objects and is a stable sort +void countingSort(int nums[], int size) { + // 1. Count the maximum element m in the array + int m = 0; + for (int i = 0; i < size; i++) { + if (nums[i] > m) { + m = nums[i]; + } + } + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + int *counter = calloc(m, sizeof(int)); + for (int i = 0; i < size; i++) { + counter[nums[i]]++; + } + // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + // counter[num]-1 is the last index where num appears in res + for (int i = 0; i < m; i++) { + counter[i + 1] += counter[i]; + } + // 4. Traverse nums in reverse order, placing each element into the result array res + // Initialize the array res to record results + int *res = malloc(sizeof(int) * size); + for (int i = size - 1; i >= 0; i--) { + int num = nums[i]; + res[counter[num] - 1] = num; // Place num at the corresponding index + counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num + } + // Use result array res to overwrite the original array nums + memcpy(nums, res, size * sizeof(int)); + // 5. Free memory + free(res); + free(counter); +} + +/* Driver Code */ +int main() { + int nums[] = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4}; + int size = sizeof(nums) / sizeof(int); + countingSortNaive(nums, size); + printf("After counting sort (cannot sort objects) completes, nums = "); + printArray(nums, size); + + int nums1[] = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4}; + int size1 = sizeof(nums1) / sizeof(int); + countingSort(nums1, size1); + printf("After counting sort completes, nums1 = "); + printArray(nums1, size1); + + return 0; +} diff --git a/en/codes/c/chapter_sorting/heap_sort.c b/en/codes/c/chapter_sorting/heap_sort.c new file mode 100644 index 000000000..41c4316e5 --- /dev/null +++ b/en/codes/c/chapter_sorting/heap_sort.c @@ -0,0 +1,60 @@ +/** + * File: heap_sort.c + * Created Time: 2023-05-30 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Heap length is n, start heapifying node i, from top to bottom */ +void siftDown(int nums[], int n, int i) { + while (1) { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + int l = 2 * i + 1; + int r = 2 * i + 2; + int ma = i; + if (l < n && nums[l] > nums[ma]) + ma = l; + if (r < n && nums[r] > nums[ma]) + ma = r; + // Swap two nodes + if (ma == i) { + break; + } + // Swap two nodes + int temp = nums[i]; + nums[i] = nums[ma]; + nums[ma] = temp; + // Loop downwards heapification + i = ma; + } +} + +/* Heap sort */ +void heapSort(int nums[], int n) { + // Build heap operation: heapify all nodes except leaves + for (int i = n / 2 - 1; i >= 0; --i) { + siftDown(nums, n, i); + } + // Extract the largest element from the heap and repeat for n-1 rounds + for (int i = n - 1; i > 0; --i) { + // Delete node + int tmp = nums[0]; + nums[0] = nums[i]; + nums[i] = tmp; + // Start heapifying the root node, from top to bottom + siftDown(nums, i, 0); + } +} + +/* Driver Code */ +int main() { + int nums[] = {4, 1, 3, 1, 5, 2}; + int n = sizeof(nums) / sizeof(nums[0]); + + heapSort(nums, n); + printf("After heap sort completes, nums = "); + printArray(nums, n); + + return 0; +} \ No newline at end of file diff --git a/en/codes/c/chapter_sorting/insertion_sort.c b/en/codes/c/chapter_sorting/insertion_sort.c new file mode 100644 index 000000000..d0953d1ab --- /dev/null +++ b/en/codes/c/chapter_sorting/insertion_sort.c @@ -0,0 +1,36 @@ +/** + * File: insertion_sort.c + * Created Time: 2022-12-29 + * Author: Listening (https://github.com/L-Super) + */ + +#include "../utils/common.h" + +/* Insertion sort */ +void insertionSort(int nums[], int size) { + // Outer loop: sorted interval is [0, i-1] + for (int i = 1; i < size; i++) { + int base = nums[i], j = i - 1; + // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + while (j >= 0 && nums[j] > base) { + // Move nums[j] to the right by one position + nums[j + 1] = nums[j]; + j--; + } + // Assign base to the correct position + nums[j + 1] = base; + } +} + +/* Driver Code */ +int main() { + int nums[] = {4, 1, 3, 1, 5, 2}; + insertionSort(nums, 6); + printf("After insertion sort completes, nums = "); + for (int i = 0; i < 6; i++) { + printf("%d ", nums[i]); + } + printf("\n"); + + return 0; +} diff --git a/en/codes/c/chapter_sorting/merge_sort.c b/en/codes/c/chapter_sorting/merge_sort.c new file mode 100644 index 000000000..3e9ae4624 --- /dev/null +++ b/en/codes/c/chapter_sorting/merge_sort.c @@ -0,0 +1,63 @@ +/** + * File: merge_sort.c + * Created Time: 2022-03-21 + * Author: Guanngxu (446678850@qq.com) + */ + +#include "../utils/common.h" + +/* Merge left subarray and right subarray */ +void merge(int *nums, int left, int mid, int right) { + // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + // Create a temporary array tmp to store the merged results + int tmpSize = right - left + 1; + int *tmp = (int *)malloc(tmpSize * sizeof(int)); + // Initialize the start indices of the left and right subarrays + int i = left, j = mid + 1, k = 0; + // While both subarrays still have elements, compare and copy the smaller element into the temporary array + while (i <= mid && j <= right) { + if (nums[i] <= nums[j]) { + tmp[k++] = nums[i++]; + } else { + tmp[k++] = nums[j++]; + } + } + // Copy the remaining elements of the left and right subarrays into the temporary array + while (i <= mid) { + tmp[k++] = nums[i++]; + } + while (j <= right) { + tmp[k++] = nums[j++]; + } + // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + for (k = 0; k < tmpSize; ++k) { + nums[left + k] = tmp[k]; + } + // Free memory + free(tmp); +} + +/* Merge sort */ +void mergeSort(int *nums, int left, int right) { + // Termination condition + if (left >= right) + return; // Terminate recursion when subarray length is 1 + // Divide and conquer stage + int mid = left + (right - left) / 2; // Calculate midpoint + mergeSort(nums, left, mid); // Recursively process the left subarray + mergeSort(nums, mid + 1, right); // Recursively process the right subarray + // Merge stage + merge(nums, left, mid, right); +} + +/* Driver Code */ +int main() { + /* Merge sort */ + int nums[] = {7, 3, 2, 6, 0, 1, 5, 4}; + int size = sizeof(nums) / sizeof(int); + mergeSort(nums, 0, size - 1); + printf("After merge sort completes, nums = "); + printArray(nums, size); + + return 0; +} diff --git a/en/codes/c/chapter_sorting/quick_sort.c b/en/codes/c/chapter_sorting/quick_sort.c new file mode 100644 index 000000000..005eb14df --- /dev/null +++ b/en/codes/c/chapter_sorting/quick_sort.c @@ -0,0 +1,137 @@ +/** + * File: quick_sort.c + * Created Time: 2023-01-18 + * Author: Reanon (793584285@qq.com) + */ + +#include "../utils/common.h" + +/* Swap elements */ +void swap(int nums[], int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; +} + +/* Sentinel partition */ +int partition(int nums[], int left, int right) { + // Use nums[left] as the pivot + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) { + j--; // Search from right to left for the first element smaller than the pivot + } + while (i < j && nums[i] <= nums[left]) { + i++; // Search from left to right for the first element greater than the pivot + } + // Swap these two elements + swap(nums, i, j); + } + // Swap the pivot to the boundary between the two subarrays + swap(nums, i, left); + // Return the index of the pivot + return i; +} + +/* Quick sort */ +void quickSort(int nums[], int left, int right) { + // Terminate recursion when subarray length is 1 + if (left >= right) { + return; + } + // Sentinel partition + int pivot = partition(nums, left, right); + // Recursively process the left subarray and right subarray + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); +} + +// Quick sort with median-of-three optimization below + +/* Select the median of three candidate elements */ +int medianThree(int nums[], int left, int mid, int right) { + int l = nums[left], m = nums[mid], r = nums[right]; + if ((l <= m && m <= r) || (r <= m && m <= l)) + return mid; // m is between l and r + if ((m <= l && l <= r) || (r <= l && l <= m)) + return left; // l is between m and r + return right; +} + +/* Sentinel partition (median of three) */ +int partitionMedian(int nums[], int left, int right) { + // Select the median of three candidate elements + int med = medianThree(nums, left, (left + right) / 2, right); + // Swap the median to the array's leftmost position + swap(nums, left, med); + // Use nums[left] as the pivot + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j--; // Search from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) + i++; // Search from left to right for the first element greater than the pivot + swap(nums, i, j); // Swap these two elements + } + swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot +} + +/* Quick sort (median-of-three) */ +void quickSortMedian(int nums[], int left, int right) { + // Terminate recursion when subarray length is 1 + if (left >= right) + return; + // Sentinel partition + int pivot = partitionMedian(nums, left, right); + // Recursively process the left subarray and right subarray + quickSortMedian(nums, left, pivot - 1); + quickSortMedian(nums, pivot + 1, right); +} + +// Quick sort with recursion depth optimization below + +/* Quick sort (recursion depth optimization) */ +void quickSortTailCall(int nums[], int left, int right) { + // Terminate when subarray length is 1 + while (left < right) { + // Sentinel partition operation + int pivot = partition(nums, left, right); + // Perform quick sort on the shorter of the two subarrays + if (pivot - left < right - pivot) { + // Recursively sort the left subarray + quickSortTailCall(nums, left, pivot - 1); + // Remaining unsorted interval is [pivot + 1, right] + left = pivot + 1; + } else { + // Recursively sort the right subarray + quickSortTailCall(nums, pivot + 1, right); + // Remaining unsorted interval is [left, pivot - 1] + right = pivot - 1; + } + } +} + +/* Driver Code */ +int main() { + /* Quick sort */ + int nums[] = {2, 4, 1, 0, 3, 5}; + int size = sizeof(nums) / sizeof(int); + quickSort(nums, 0, size - 1); + printf("After quick sort completes, nums = "); + printArray(nums, size); + + /* Quick sort (recursion depth optimization) */ + int nums1[] = {2, 4, 1, 0, 3, 5}; + quickSortMedian(nums1, 0, size - 1); + printf("After quick sort (median pivot optimization), nums = "); + printArray(nums1, size); + + /* Quick sort (recursion depth optimization) */ + int nums2[] = {2, 4, 1, 0, 3, 5}; + quickSortTailCall(nums2, 0, size - 1); + printf("After quick sort (recursion depth optimization), nums = "); + printArray(nums1, size); + + return 0; +} diff --git a/en/codes/c/chapter_sorting/radix_sort.c b/en/codes/c/chapter_sorting/radix_sort.c new file mode 100644 index 000000000..1eb08c8ed --- /dev/null +++ b/en/codes/c/chapter_sorting/radix_sort.c @@ -0,0 +1,75 @@ +/** + * File: radix_sort.c + * Created Time: 2023-01-18 + * Author: Reanon (793584285@qq.com) + */ + +#include "../utils/common.h" + +/* Get the k-th digit of element num, where exp = 10^(k-1) */ +int digit(int num, int exp) { + // Passing exp instead of k can avoid repeated expensive exponentiation here + return (num / exp) % 10; +} + +/* Counting sort (based on nums k-th digit) */ +void countingSortDigit(int nums[], int size, int exp) { + // Decimal digit range is 0~9, therefore need a bucket array of length 10 + int *counter = (int *)malloc((sizeof(int) * 10)); + memset(counter, 0, sizeof(int) * 10); // Initialize to 0 to support subsequent memory release + // Count the occurrence of digits 0~9 + for (int i = 0; i < size; i++) { + // Get the k-th digit of nums[i], noted as d + int d = digit(nums[i], exp); + // Count the occurrence of digit d + counter[d]++; + } + // Calculate prefix sum, converting "occurrence count" into "array index" + for (int i = 1; i < 10; i++) { + counter[i] += counter[i - 1]; + } + // Traverse in reverse, based on bucket statistics, place each element into res + int *res = (int *)malloc(sizeof(int) * size); + for (int i = size - 1; i >= 0; i--) { + int d = digit(nums[i], exp); + int j = counter[d] - 1; // Get the index j for d in the array + res[j] = nums[i]; // Place the current element at index j + counter[d]--; // Decrease the count of d by 1 + } + // Use result to overwrite the original array nums + for (int i = 0; i < size; i++) { + nums[i] = res[i]; + } + // Free memory + free(res); + free(counter); +} + +/* Radix sort */ +void radixSort(int nums[], int size) { + // Get the maximum element of the array, used to determine the maximum number of digits + int max = INT32_MIN; + for (int i = 0; i < size; i++) { + if (nums[i] > max) { + max = nums[i]; + } + } + // Traverse from the lowest to the highest digit + for (int exp = 1; max >= exp; exp *= 10) + // Perform counting sort on the k-th digit of array elements + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // i.e., exp = 10^(k-1) + countingSortDigit(nums, size, exp); +} + +/* Driver Code */ +int main() { + // Radix sort + int nums[] = {10546151, 35663510, 42865989, 34862445, 81883077, + 88906420, 72429244, 30524779, 82060337, 63832996}; + int size = sizeof(nums) / sizeof(int); + radixSort(nums, size); + printf("After radix sort completes, nums = "); + printArray(nums, size); +} diff --git a/en/codes/c/chapter_sorting/selection_sort.c b/en/codes/c/chapter_sorting/selection_sort.c new file mode 100644 index 000000000..7a5301d49 --- /dev/null +++ b/en/codes/c/chapter_sorting/selection_sort.c @@ -0,0 +1,37 @@ +/** + * File: selection_sort.c + * Created Time: 2023-05-31 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Selection sort */ +void selectionSort(int nums[], int n) { + // Outer loop: unsorted interval is [i, n-1] + for (int i = 0; i < n - 1; i++) { + // Inner loop: find the smallest element within the unsorted interval + int k = i; + for (int j = i + 1; j < n; j++) { + if (nums[j] < nums[k]) + k = j; // Record the index of the smallest element + } + // Swap the smallest element with the first element of the unsorted interval + int temp = nums[i]; + nums[i] = nums[k]; + nums[k] = temp; + } +} + +/* Driver Code */ +int main() { + int nums[] = {4, 1, 3, 1, 5, 2}; + int n = sizeof(nums) / sizeof(nums[0]); + + selectionSort(nums, n); + + printf("After selection sort completes, nums = "); + printArray(nums, n); + + return 0; +} diff --git a/en/codes/c/chapter_stack_and_queue/CMakeLists.txt b/en/codes/c/chapter_stack_and_queue/CMakeLists.txt new file mode 100644 index 000000000..ed3ba840c --- /dev/null +++ b/en/codes/c/chapter_stack_and_queue/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(array_stack array_stack.c) +add_executable(linkedlist_stack linkedlist_stack.c) +add_executable(array_queue array_queue.c) +add_executable(linkedlist_queue linkedlist_queue.c) +add_executable(array_deque array_deque.c) +add_executable(linkedlist_deque linkedlist_deque.c) diff --git a/en/codes/c/chapter_stack_and_queue/array_deque.c b/en/codes/c/chapter_stack_and_queue/array_deque.c new file mode 100644 index 000000000..ab8bbe32f --- /dev/null +++ b/en/codes/c/chapter_stack_and_queue/array_deque.c @@ -0,0 +1,172 @@ +/** + * File: array_deque.c + * Created Time: 2023-03-13 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Double-ended queue based on circular array implementation */ +typedef struct { + int *nums; // Array for storing queue elements + int front; // Front pointer, points to the front of the queue element + int queSize; // Rear pointer, points to rear + 1 + int queCapacity; // Queue capacity +} ArrayDeque; + +/* Constructor */ +ArrayDeque *newArrayDeque(int capacity) { + ArrayDeque *deque = (ArrayDeque *)malloc(sizeof(ArrayDeque)); + // Initialize array + deque->queCapacity = capacity; + deque->nums = (int *)malloc(sizeof(int) * deque->queCapacity); + deque->front = deque->queSize = 0; + return deque; +} + +/* Destructor */ +void delArrayDeque(ArrayDeque *deque) { + free(deque->nums); + free(deque); +} + +/* Get the capacity of the double-ended queue */ +int capacity(ArrayDeque *deque) { + return deque->queCapacity; +} + +/* Get the length of the double-ended queue */ +int size(ArrayDeque *deque) { + return deque->queSize; +} + +/* Check if the double-ended queue is empty */ +bool empty(ArrayDeque *deque) { + return deque->queSize == 0; +} + +/* Calculate circular array index */ +int dequeIndex(ArrayDeque *deque, int i) { + // Use modulo operation to wrap the array head and tail together + // When i exceeds array end, wrap to head + // When i passes the head of the array, return to the tail + return ((i + capacity(deque)) % capacity(deque)); +} + +/* Front of the queue enqueue */ +void pushFirst(ArrayDeque *deque, int num) { + if (deque->queSize == capacity(deque)) { + printf("Deque is full\r\n"); + return; + } + // Use modulo operation to wrap front around to the tail after passing the head of the array + // Use modulo to wrap front from array head to rear + deque->front = dequeIndex(deque, deque->front - 1); + // Add num to queue front + deque->nums[deque->front] = num; + deque->queSize++; +} + +/* Rear of the queue enqueue */ +void pushLast(ArrayDeque *deque, int num) { + if (deque->queSize == capacity(deque)) { + printf("Deque is full\r\n"); + return; + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + int rear = dequeIndex(deque, deque->front + deque->queSize); + // Front pointer moves one position backward + deque->nums[rear] = num; + deque->queSize++; +} + +/* Return list for printing */ +int peekFirst(ArrayDeque *deque) { + // Access error: Deque is empty + assert(empty(deque) == 0); + return deque->nums[deque->front]; +} + +/* Driver Code */ +int peekLast(ArrayDeque *deque) { + // Access error: Deque is empty + assert(empty(deque) == 0); + int last = dequeIndex(deque, deque->front + deque->queSize - 1); + return deque->nums[last]; +} + +/* Rear of the queue dequeue */ +int popFirst(ArrayDeque *deque) { + int num = peekFirst(deque); + // Move front pointer backward by one position + deque->front = dequeIndex(deque, deque->front + 1); + deque->queSize--; + return num; +} + +/* Access rear of the queue element */ +int popLast(ArrayDeque *deque) { + int num = peekLast(deque); + deque->queSize--; + return num; +} + +/* Return array for printing */ +int *toArray(ArrayDeque *deque, int *queSize) { + *queSize = deque->queSize; + int *res = (int *)calloc(deque->queSize, sizeof(int)); + int j = deque->front; + for (int i = 0; i < deque->queSize; i++) { + res[i] = deque->nums[j % deque->queCapacity]; + j++; + } + return res; +} + +/* Driver Code */ +int main() { + /* Access front of the queue element */ + int capacity = 10; + int queSize; + ArrayDeque *deque = newArrayDeque(capacity); + pushLast(deque, 3); + pushLast(deque, 2); + pushLast(deque, 5); + printf("Double-ended queue deque = "); + printArray(toArray(deque, &queSize), queSize); + + /* Update element */ + int peekFirstNum = peekFirst(deque); + printf("Front element peekFirst = %d\r\n", peekFirstNum); + int peekLastNum = peekLast(deque); + printf("Rear element peekLast = %d\r\n", peekLastNum); + + /* Elements enqueue */ + pushLast(deque, 4); + printf("After element 4 enqueues at rear, deque = "); + printArray(toArray(deque, &queSize), queSize); + pushFirst(deque, 1); + printf("After element 1 enqueues at front, deque = "); + printArray(toArray(deque, &queSize), queSize); + + /* Element dequeue */ + int popLastNum = popLast(deque); + printf("Dequeue from rear = %d, deque after rear dequeue = ", popLastNum); + printArray(toArray(deque, &queSize), queSize); + int popFirstNum = popFirst(deque); + printf("Dequeue from front = %d, deque after front dequeue = ", popFirstNum); + printArray(toArray(deque, &queSize), queSize); + + /* Get the length of the queue */ + int dequeSize = size(deque); + printf("Deque size = %d\r\n", dequeSize); + + /* Check if the queue is empty */ + bool isEmpty = empty(deque); + printf("Is queue empty = %s\r\n", isEmpty ? "true" : "false"); + + // Free memory + delArrayDeque(deque); + + return 0; +} \ No newline at end of file diff --git a/en/codes/c/chapter_stack_and_queue/array_queue.c b/en/codes/c/chapter_stack_and_queue/array_queue.c new file mode 100644 index 000000000..28b28e68d --- /dev/null +++ b/en/codes/c/chapter_stack_and_queue/array_queue.c @@ -0,0 +1,134 @@ +/** + * File: array_queue.c + * Created Time: 2023-01-28 + * Author: Zero (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Queue based on circular array implementation */ +typedef struct { + int *nums; // Array for storing queue elements + int front; // Front pointer, points to the front of the queue element + int queSize; // Rear pointer, points to rear + 1 + int queCapacity; // Queue capacity +} ArrayQueue; + +/* Constructor */ +ArrayQueue *newArrayQueue(int capacity) { + ArrayQueue *queue = (ArrayQueue *)malloc(sizeof(ArrayQueue)); + // Initialize array + queue->queCapacity = capacity; + queue->nums = (int *)malloc(sizeof(int) * queue->queCapacity); + queue->front = queue->queSize = 0; + return queue; +} + +/* Destructor */ +void delArrayQueue(ArrayQueue *queue) { + free(queue->nums); + free(queue); +} + +/* Get the capacity of the queue */ +int capacity(ArrayQueue *queue) { + return queue->queCapacity; +} + +/* Get the length of the queue */ +int size(ArrayQueue *queue) { + return queue->queSize; +} + +/* Check if the queue is empty */ +bool empty(ArrayQueue *queue) { + return queue->queSize == 0; +} + +/* Return list for printing */ +int peek(ArrayQueue *queue) { + assert(size(queue) != 0); + return queue->nums[queue->front]; +} + +/* Enqueue */ +void push(ArrayQueue *queue, int num) { + if (size(queue) == capacity(queue)) { + printf("Queue is full\r\n"); + return; + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Add num to the rear of the queue + int rear = (queue->front + queue->queSize) % queue->queCapacity; + // Front pointer moves one position backward + queue->nums[rear] = num; + queue->queSize++; +} + +/* Dequeue */ +int pop(ArrayQueue *queue) { + int num = peek(queue); + // Move front pointer backward by one position, if it passes the tail, return to array head + queue->front = (queue->front + 1) % queue->queCapacity; + queue->queSize--; + return num; +} + +/* Return array for printing */ +int *toArray(ArrayQueue *queue, int *queSize) { + *queSize = queue->queSize; + int *res = (int *)calloc(queue->queSize, sizeof(int)); + int j = queue->front; + for (int i = 0; i < queue->queSize; i++) { + res[i] = queue->nums[j % queue->queCapacity]; + j++; + } + return res; +} + +/* Driver Code */ +int main() { + /* Access front of the queue element */ + int capacity = 10; + int queSize; + ArrayQueue *queue = newArrayQueue(capacity); + + /* Elements enqueue */ + push(queue, 1); + push(queue, 3); + push(queue, 2); + push(queue, 5); + push(queue, 4); + printf("Queue queue = "); + printArray(toArray(queue, &queSize), queSize); + + /* Return list for printing */ + int peekNum = peek(queue); + printf("Front element peek = %d\r\n", peekNum); + + /* Element dequeue */ + peekNum = pop(queue); + printf("Dequeue element pop = %d, queue after dequeue = ", peekNum); + printArray(toArray(queue, &queSize), queSize); + + /* Get the length of the queue */ + int queueSize = size(queue); + printf("Queue size = %d\r\n", queueSize); + + /* Check if the queue is empty */ + bool isEmpty = empty(queue); + printf("Is queue empty = %s\r\n", isEmpty ? "true" : "false"); + + /* Test circular array */ + for (int i = 0; i < 10; i++) { + push(queue, i); + pop(queue); + printf("After round %d enqueue + dequeue, queue = ", i); + printArray(toArray(queue, &queSize), queSize); + } + + // Free memory + delArrayQueue(queue); + + return 0; +} \ No newline at end of file diff --git a/en/codes/c/chapter_stack_and_queue/array_stack.c b/en/codes/c/chapter_stack_and_queue/array_stack.c new file mode 100644 index 000000000..df574bc61 --- /dev/null +++ b/en/codes/c/chapter_stack_and_queue/array_stack.c @@ -0,0 +1,103 @@ +/** + * File: array_stack.c + * Created Time: 2023-01-12 + * Author: Zero (glj0@outlook.com) + */ + +#include "../utils/common.h" + +#define MAX_SIZE 5000 + +/* Stack based on array implementation */ +typedef struct { + int *data; + int size; +} ArrayStack; + +/* Constructor */ +ArrayStack *newArrayStack() { + ArrayStack *stack = malloc(sizeof(ArrayStack)); + // Initialize with large capacity to avoid expansion + stack->data = malloc(sizeof(int) * MAX_SIZE); + stack->size = 0; + return stack; +} + +/* Destructor */ +void delArrayStack(ArrayStack *stack) { + free(stack->data); + free(stack); +} + +/* Get the length of the stack */ +int size(ArrayStack *stack) { + return stack->size; +} + +/* Check if the stack is empty */ +bool isEmpty(ArrayStack *stack) { + return stack->size == 0; +} + +/* Push */ +void push(ArrayStack *stack, int num) { + if (stack->size == MAX_SIZE) { + printf("Stack is full\n"); + return; + } + stack->data[stack->size] = num; + stack->size++; +} + +/* Return list for printing */ +int peek(ArrayStack *stack) { + if (stack->size == 0) { + printf("Stack is empty\n"); + return INT_MAX; + } + return stack->data[stack->size - 1]; +} + +/* Pop */ +int pop(ArrayStack *stack) { + int val = peek(stack); + stack->size--; + return val; +} + +/* Driver Code */ +int main() { + /* Access top of the stack element */ + ArrayStack *stack = newArrayStack(); + + /* Elements push onto stack */ + push(stack, 1); + push(stack, 3); + push(stack, 2); + push(stack, 5); + push(stack, 4); + printf("Stack stack = "); + printArray(stack->data, stack->size); + + /* Return list for printing */ + int val = peek(stack); + printf("Top element top = %d\n", val); + + /* Element pop from stack */ + val = pop(stack); + printf("Pop element pop = %d, stack after pop = ", val); + printArray(stack->data, stack->size); + + /* Get the length of the stack */ + int size = stack->size; + printf("Stack size = %d\n", size); + + /* Check if empty */ + bool empty = isEmpty(stack); + printf("Is stack empty = %s\n", empty ? "true" : "false"); + + // Free memory + delArrayStack(stack); + + return 0; +} diff --git a/en/codes/c/chapter_stack_and_queue/linkedlist_deque.c b/en/codes/c/chapter_stack_and_queue/linkedlist_deque.c new file mode 100644 index 000000000..fe84a8974 --- /dev/null +++ b/en/codes/c/chapter_stack_and_queue/linkedlist_deque.c @@ -0,0 +1,212 @@ +/** + * File: linkedlist_deque.c + * Created Time: 2023-03-13 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Doubly linked list node */ +typedef struct DoublyListNode { + int val; // Node value + struct DoublyListNode *next; // Successor node + struct DoublyListNode *prev; // Predecessor node +} DoublyListNode; + +/* Constructor */ +DoublyListNode *newDoublyListNode(int num) { + DoublyListNode *new = (DoublyListNode *)malloc(sizeof(DoublyListNode)); + new->val = num; + new->next = NULL; + new->prev = NULL; + return new; +} + +/* Destructor */ +void delDoublyListNode(DoublyListNode *node) { + free(node); +} + +/* Double-ended queue based on doubly linked list implementation */ +typedef struct { + DoublyListNode *front, *rear; // Head node front, tail node rear + int queSize; // Length of the double-ended queue +} LinkedListDeque; + +/* Constructor */ +LinkedListDeque *newLinkedListDeque() { + LinkedListDeque *deque = (LinkedListDeque *)malloc(sizeof(LinkedListDeque)); + deque->front = NULL; + deque->rear = NULL; + deque->queSize = 0; + return deque; +} + +/* Destructor */ +void delLinkedListdeque(LinkedListDeque *deque) { + // Free all nodes + for (int i = 0; i < deque->queSize && deque->front != NULL; i++) { + DoublyListNode *tmp = deque->front; + deque->front = deque->front->next; + free(tmp); + } + // Free deque structure + free(deque); +} + +/* Get the length of the queue */ +int size(LinkedListDeque *deque) { + return deque->queSize; +} + +/* Check if the queue is empty */ +bool empty(LinkedListDeque *deque) { + return (size(deque) == 0); +} + +/* Enqueue */ +void push(LinkedListDeque *deque, int num, bool isFront) { + DoublyListNode *node = newDoublyListNode(num); + // If list is empty, set both front and rear to node + if (empty(deque)) { + deque->front = deque->rear = node; + } + // Front of the queue enqueue operation + else if (isFront) { + // Add node to the head of the linked list + deque->front->prev = node; + node->next = deque->front; + deque->front = node; // Update head node + } + // Rear of the queue enqueue operation + else { + // Add node to the tail of the linked list + deque->rear->next = node; + node->prev = deque->rear; + deque->rear = node; + } + deque->queSize++; // Update queue length +} + +/* Front of the queue enqueue */ +void pushFirst(LinkedListDeque *deque, int num) { + push(deque, num, true); +} + +/* Rear of the queue enqueue */ +void pushLast(LinkedListDeque *deque, int num) { + push(deque, num, false); +} + +/* Return list for printing */ +int peekFirst(LinkedListDeque *deque) { + assert(size(deque) && deque->front); + return deque->front->val; +} + +/* Driver Code */ +int peekLast(LinkedListDeque *deque) { + assert(size(deque) && deque->rear); + return deque->rear->val; +} + +/* Dequeue */ +int pop(LinkedListDeque *deque, bool isFront) { + if (empty(deque)) + return -1; + int val; + // Temporarily store head node value + if (isFront) { + val = peekFirst(deque); // Delete head node + DoublyListNode *fNext = deque->front->next; + if (fNext) { + fNext->prev = NULL; + deque->front->next = NULL; + } + delDoublyListNode(deque->front); + deque->front = fNext; // Update head node + } + // Temporarily store tail node value + else { + val = peekLast(deque); // Delete tail node + DoublyListNode *rPrev = deque->rear->prev; + if (rPrev) { + rPrev->next = NULL; + deque->rear->prev = NULL; + } + delDoublyListNode(deque->rear); + deque->rear = rPrev; // Update tail node + } + deque->queSize--; // Update queue length + return val; +} + +/* Rear of the queue dequeue */ +int popFirst(LinkedListDeque *deque) { + return pop(deque, true); +} + +/* Access rear of the queue element */ +int popLast(LinkedListDeque *deque) { + return pop(deque, false); +} + +/* Print queue */ +void printLinkedListDeque(LinkedListDeque *deque) { + int *arr = malloc(sizeof(int) * deque->queSize); + // Copy data from list to array + int i; + DoublyListNode *node; + for (i = 0, node = deque->front; i < deque->queSize; i++) { + arr[i] = node->val; + node = node->next; + } + printArray(arr, deque->queSize); + free(arr); +} + +/* Driver Code */ +int main() { + /* Get the length of the double-ended queue */ + LinkedListDeque *deque = newLinkedListDeque(); + pushLast(deque, 3); + pushLast(deque, 2); + pushLast(deque, 5); + printf("Double-ended queue deque = "); + printLinkedListDeque(deque); + + /* Update element */ + int peekFirstNum = peekFirst(deque); + printf("Front element peekFirst = %d\r\n", peekFirstNum); + int peekLastNum = peekLast(deque); + printf("Front element peekLast = %d\r\n", peekLastNum); + + /* Elements enqueue */ + pushLast(deque, 4); + printf("After element 4 enqueues at back, deque ="); + printLinkedListDeque(deque); + pushFirst(deque, 1); + printf("After element 1 enqueues at front, deque ="); + printLinkedListDeque(deque); + + /* Element dequeue */ + int popLastNum = popLast(deque); + printf("Dequeue from rear popLast = %d, deque after rear dequeue = ", popLastNum); + printLinkedListDeque(deque); + int popFirstNum = popFirst(deque); + printf("Dequeue from front popFirst = %d, deque after front dequeue = ", popFirstNum); + printLinkedListDeque(deque); + + /* Get the length of the queue */ + int dequeSize = size(deque); + printf("Deque size = %d\r\n", dequeSize); + + /* Check if the queue is empty */ + bool isEmpty = empty(deque); + printf("Is deque empty = %s\r\n", isEmpty ? "true" : "false"); + + // Free memory + delLinkedListdeque(deque); + + return 0; +} diff --git a/en/codes/c/chapter_stack_and_queue/linkedlist_queue.c b/en/codes/c/chapter_stack_and_queue/linkedlist_queue.c new file mode 100644 index 000000000..471a73cd7 --- /dev/null +++ b/en/codes/c/chapter_stack_and_queue/linkedlist_queue.c @@ -0,0 +1,128 @@ +/** + * File: linkedlist_queue.c + * Created Time: 2023-03-13 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Queue based on linked list implementation */ +typedef struct { + ListNode *front, *rear; + int queSize; +} LinkedListQueue; + +/* Constructor */ +LinkedListQueue *newLinkedListQueue() { + LinkedListQueue *queue = (LinkedListQueue *)malloc(sizeof(LinkedListQueue)); + queue->front = NULL; + queue->rear = NULL; + queue->queSize = 0; + return queue; +} + +/* Destructor */ +void delLinkedListQueue(LinkedListQueue *queue) { + // Free all nodes + while (queue->front != NULL) { + ListNode *tmp = queue->front; + queue->front = queue->front->next; + free(tmp); + } + // Free queue structure + free(queue); +} + +/* Get the length of the queue */ +int size(LinkedListQueue *queue) { + return queue->queSize; +} + +/* Check if the queue is empty */ +bool empty(LinkedListQueue *queue) { + return (size(queue) == 0); +} + +/* Enqueue */ +void push(LinkedListQueue *queue, int num) { + // Add node at tail + ListNode *node = newListNode(num); + // If the queue is empty, make both front and rear point to the node + if (queue->front == NULL) { + queue->front = node; + queue->rear = node; + } + // If the queue is not empty, add the node after the tail node + else { + queue->rear->next = node; + queue->rear = node; + } + queue->queSize++; +} + +/* Return list for printing */ +int peek(LinkedListQueue *queue) { + assert(size(queue) && queue->front); + return queue->front->val; +} + +/* Dequeue */ +int pop(LinkedListQueue *queue) { + int num = peek(queue); + ListNode *tmp = queue->front; + queue->front = queue->front->next; + free(tmp); + queue->queSize--; + return num; +} + +/* Print queue */ +void printLinkedListQueue(LinkedListQueue *queue) { + int *arr = malloc(sizeof(int) * queue->queSize); + // Copy data from list to array + int i; + ListNode *node; + for (i = 0, node = queue->front; i < queue->queSize; i++) { + arr[i] = node->val; + node = node->next; + } + printArray(arr, queue->queSize); + free(arr); +} + +/* Driver Code */ +int main() { + /* Access front of the queue element */ + LinkedListQueue *queue = newLinkedListQueue(); + + /* Elements enqueue */ + push(queue, 1); + push(queue, 3); + push(queue, 2); + push(queue, 5); + push(queue, 4); + printf("Queue queue = "); + printLinkedListQueue(queue); + + /* Return list for printing */ + int peekNum = peek(queue); + printf("Front element peek = %d\r\n", peekNum); + + /* Element dequeue */ + peekNum = pop(queue); + printf("Dequeue element pop = %d, queue after dequeue = ", peekNum); + printLinkedListQueue(queue); + + /* Get the length of the queue */ + int queueSize = size(queue); + printf("Queue size = %d\r\n", queueSize); + + /* Check if the queue is empty */ + bool isEmpty = empty(queue); + printf("Is queue empty = %s\r\n", isEmpty ? "true" : "false"); + + // Free memory + delLinkedListQueue(queue); + + return 0; +} diff --git a/en/codes/c/chapter_stack_and_queue/linkedlist_stack.c b/en/codes/c/chapter_stack_and_queue/linkedlist_stack.c new file mode 100644 index 000000000..a300dfaa1 --- /dev/null +++ b/en/codes/c/chapter_stack_and_queue/linkedlist_stack.c @@ -0,0 +1,107 @@ +/** + * File: linkedlist_stack.c + * Created Time: 2023-01-12 + * Author: Zero (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Stack based on linked list implementation */ +typedef struct { + ListNode *top; // Use head node as stack top + int size; // Stack length +} LinkedListStack; + +/* Constructor */ +LinkedListStack *newLinkedListStack() { + LinkedListStack *s = malloc(sizeof(LinkedListStack)); + s->top = NULL; + s->size = 0; + return s; +} + +/* Destructor */ +void delLinkedListStack(LinkedListStack *s) { + while (s->top) { + ListNode *n = s->top->next; + free(s->top); + s->top = n; + } + free(s); +} + +/* Get the length of the stack */ +int size(LinkedListStack *s) { + return s->size; +} + +/* Check if the stack is empty */ +bool isEmpty(LinkedListStack *s) { + return size(s) == 0; +} + +/* Push */ +void push(LinkedListStack *s, int num) { + ListNode *node = (ListNode *)malloc(sizeof(ListNode)); + node->next = s->top; // Update new node's pointer field + node->val = num; // Update new node's data field + s->top = node; // Update stack top + s->size++; // Update stack size +} + +/* Return list for printing */ +int peek(LinkedListStack *s) { + if (s->size == 0) { + printf("Stack is empty\n"); + return INT_MAX; + } + return s->top->val; +} + +/* Pop */ +int pop(LinkedListStack *s) { + int val = peek(s); + ListNode *tmp = s->top; + s->top = s->top->next; + // Free memory + free(tmp); + s->size--; + return val; +} + +/* Driver Code */ +int main() { + /* Access top of the stack element */ + LinkedListStack *stack = newLinkedListStack(); + + /* Elements push onto stack */ + push(stack, 1); + push(stack, 3); + push(stack, 2); + push(stack, 5); + push(stack, 4); + + printf("Stack stack = "); + printLinkedList(stack->top); + + /* Return list for printing */ + int val = peek(stack); + printf("Top element top = %d\r\n", val); + + /* Element pop from stack */ + val = pop(stack); + printf("Pop element pop = %d, stack after pop = ", val); + printLinkedList(stack->top); + + /* Get the length of the stack */ + printf("Stack size = %d\n", size(stack)); + + /* Check if empty */ + bool empty = isEmpty(stack); + printf("Is stack empty = %s\n", empty ? "true" : "false"); + + // Free memory + delLinkedListStack(stack); + + return 0; +} diff --git a/en/codes/c/chapter_tree/CMakeLists.txt b/en/codes/c/chapter_tree/CMakeLists.txt new file mode 100644 index 000000000..9b4e825ff --- /dev/null +++ b/en/codes/c/chapter_tree/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(avl_tree avl_tree.c) +add_executable(binary_tree binary_tree.c) +add_executable(binary_tree_bfs binary_tree_bfs.c) +add_executable(binary_tree_dfs binary_tree_dfs.c) +add_executable(binary_search_tree binary_search_tree.c) +add_executable(array_binary_tree array_binary_tree.c) diff --git a/en/codes/c/chapter_tree/array_binary_tree.c b/en/codes/c/chapter_tree/array_binary_tree.c new file mode 100644 index 000000000..1ecf063fd --- /dev/null +++ b/en/codes/c/chapter_tree/array_binary_tree.c @@ -0,0 +1,166 @@ +/** + * File: array_binary_tree.c + * Created Time: 2023-07-29 + * Author: Gonglja (glj0@outlook.com) + */ + +#include "../utils/common.h" + +/* Binary tree structure in array representation */ +typedef struct { + int *tree; + int size; +} ArrayBinaryTree; + +/* Constructor */ +ArrayBinaryTree *newArrayBinaryTree(int *arr, int arrSize) { + ArrayBinaryTree *abt = (ArrayBinaryTree *)malloc(sizeof(ArrayBinaryTree)); + abt->tree = malloc(sizeof(int) * arrSize); + memcpy(abt->tree, arr, sizeof(int) * arrSize); + abt->size = arrSize; + return abt; +} + +/* Destructor */ +void delArrayBinaryTree(ArrayBinaryTree *abt) { + free(abt->tree); + free(abt); +} + +/* List capacity */ +int size(ArrayBinaryTree *abt) { + return abt->size; +} + +/* Get value of node at index i */ +int val(ArrayBinaryTree *abt, int i) { + // Return INT_MAX if index out of bounds, representing empty position + if (i < 0 || i >= size(abt)) + return INT_MAX; + return abt->tree[i]; +} + +/* Get index of left child node of node at index i */ +int left(int i) { + return 2 * i + 1; +} + +/* Get index of right child node of node at index i */ +int right(int i) { + return 2 * i + 2; +} + +/* Get index of parent node of node at index i */ +int parent(int i) { + return (i - 1) / 2; +} + +/* Level-order traversal */ +int *levelOrder(ArrayBinaryTree *abt, int *returnSize) { + int *res = (int *)malloc(sizeof(int) * size(abt)); + int index = 0; + // Traverse array directly + for (int i = 0; i < size(abt); i++) { + if (val(abt, i) != INT_MAX) + res[index++] = val(abt, i); + } + *returnSize = index; + return res; +} + +/* Depth-first traversal */ +void dfs(ArrayBinaryTree *abt, int i, char *order, int *res, int *index) { + // If empty position, return + if (val(abt, i) == INT_MAX) + return; + // Preorder traversal + if (strcmp(order, "pre") == 0) + res[(*index)++] = val(abt, i); + dfs(abt, left(i), order, res, index); + // Inorder traversal + if (strcmp(order, "in") == 0) + res[(*index)++] = val(abt, i); + dfs(abt, right(i), order, res, index); + // Postorder traversal + if (strcmp(order, "post") == 0) + res[(*index)++] = val(abt, i); +} + +/* Preorder traversal */ +int *preOrder(ArrayBinaryTree *abt, int *returnSize) { + int *res = (int *)malloc(sizeof(int) * size(abt)); + int index = 0; + dfs(abt, 0, "pre", res, &index); + *returnSize = index; + return res; +} + +/* Inorder traversal */ +int *inOrder(ArrayBinaryTree *abt, int *returnSize) { + int *res = (int *)malloc(sizeof(int) * size(abt)); + int index = 0; + dfs(abt, 0, "in", res, &index); + *returnSize = index; + return res; +} + +/* Postorder traversal */ +int *postOrder(ArrayBinaryTree *abt, int *returnSize) { + int *res = (int *)malloc(sizeof(int) * size(abt)); + int index = 0; + dfs(abt, 0, "post", res, &index); + *returnSize = index; + return res; +} + +/* Driver Code */ +int main() { + // Initialize binary tree + // Use INT_MAX to represent NULL + int arr[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; + int arrSize = sizeof(arr) / sizeof(arr[0]); + TreeNode *root = arrayToTree(arr, arrSize); + printf("\nInitialize binary tree\n"); + printf("Array representation of binary tree:\n"); + printArray(arr, arrSize); + printf("Linked list representation of binary tree:\n"); + printTree(root); + + ArrayBinaryTree *abt = newArrayBinaryTree(arr, arrSize); + + // Access node + int i = 1; + int l = left(i), r = right(i), p = parent(i); + printf("\nCurrent node index is %d, value is %d\n", i, val(abt, i)); + printf("Its left child index is %d, value is %d\n", l, l < arrSize ? val(abt, l) : INT_MAX); + printf("Its right child index is %d, value is %d\n", r, r < arrSize ? val(abt, r) : INT_MAX); + printf("Its parent node index is %d, value is %d\n", p, p < arrSize ? val(abt, p) : INT_MAX); + + // Traverse tree + int returnSize; + int *res; + + res = levelOrder(abt, &returnSize); + printf("\nLevel-order traversal: "); + printArray(res, returnSize); + free(res); + + res = preOrder(abt, &returnSize); + printf("Pre-order traversal: "); + printArray(res, returnSize); + free(res); + + res = inOrder(abt, &returnSize); + printf("In-order traversal: "); + printArray(res, returnSize); + free(res); + + res = postOrder(abt, &returnSize); + printf("Post-order traversal: "); + printArray(res, returnSize); + free(res); + + // Free memory + delArrayBinaryTree(abt); + return 0; +} diff --git a/en/codes/c/chapter_tree/avl_tree.c b/en/codes/c/chapter_tree/avl_tree.c new file mode 100644 index 000000000..1561fb26c --- /dev/null +++ b/en/codes/c/chapter_tree/avl_tree.c @@ -0,0 +1,259 @@ +/** + * File: avl_tree.c + * Created Time: 2023-01-15 + * Author: Reanon (793584285@qq.com) + */ + +#include "../utils/common.h" + +/* AVL tree structure */ +typedef struct { + TreeNode *root; +} AVLTree; + +/* Constructor */ +AVLTree *newAVLTree() { + AVLTree *tree = (AVLTree *)malloc(sizeof(AVLTree)); + tree->root = NULL; + return tree; +} + +/* Destructor */ +void delAVLTree(AVLTree *tree) { + freeMemoryTree(tree->root); + free(tree); +} + +/* Get node height */ +int height(TreeNode *node) { + // Empty node height is -1, leaf node height is 0 + if (node != NULL) { + return node->height; + } + return -1; +} + +/* Update node height */ +void updateHeight(TreeNode *node) { + int lh = height(node->left); + int rh = height(node->right); + // Node height equals the height of the tallest subtree + 1 + if (lh > rh) { + node->height = lh + 1; + } else { + node->height = rh + 1; + } +} + +/* Get balance factor */ +int balanceFactor(TreeNode *node) { + // Empty node balance factor is 0 + if (node == NULL) { + return 0; + } + // Node balance factor = left subtree height - right subtree height + return height(node->left) - height(node->right); +} + +/* Right rotation operation */ +TreeNode *rightRotate(TreeNode *node) { + TreeNode *child, *grandChild; + child = node->left; + grandChild = child->right; + // Using child as pivot, rotate node to the right + child->right = node; + node->left = grandChild; + // Update node height + updateHeight(node); + updateHeight(child); + // Return root node of subtree after rotation + return child; +} + +/* Left rotation operation */ +TreeNode *leftRotate(TreeNode *node) { + TreeNode *child, *grandChild; + child = node->right; + grandChild = child->left; + // Using child as pivot, rotate node to the left + child->left = node; + node->right = grandChild; + // Update node height + updateHeight(node); + updateHeight(child); + // Return root node of subtree after rotation + return child; +} + +/* Perform rotation operation to restore balance to this subtree */ +TreeNode *rotate(TreeNode *node) { + // Get balance factor of node + int bf = balanceFactor(node); + // Left-leaning tree + if (bf > 1) { + if (balanceFactor(node->left) >= 0) { + // Right rotation + return rightRotate(node); + } else { + // First left rotation then right rotation + node->left = leftRotate(node->left); + return rightRotate(node); + } + } + // Right-leaning tree + if (bf < -1) { + if (balanceFactor(node->right) <= 0) { + // Left rotation + return leftRotate(node); + } else { + // First right rotation then left rotation + node->right = rightRotate(node->right); + return leftRotate(node); + } + } + // Balanced tree, no rotation needed, return directly + return node; +} + +/* Recursively insert node (helper function) */ +TreeNode *insertHelper(TreeNode *node, int val) { + if (node == NULL) { + return newTreeNode(val); + } + /* 1. Find insertion position and insert node */ + if (val < node->val) { + node->left = insertHelper(node->left, val); + } else if (val > node->val) { + node->right = insertHelper(node->right, val); + } else { + // Duplicate node not inserted, return directly + return node; + } + // Update node height + updateHeight(node); + /* 2. Perform rotation operation to restore balance to this subtree */ + node = rotate(node); + // Return root node of subtree + return node; +} + +/* Insert node */ +void insert(AVLTree *tree, int val) { + tree->root = insertHelper(tree->root, val); +} + +/* Recursively remove node (helper function) */ +TreeNode *removeHelper(TreeNode *node, int val) { + TreeNode *child, *grandChild; + if (node == NULL) { + return NULL; + } + /* 1. Find node and delete */ + if (val < node->val) { + node->left = removeHelper(node->left, val); + } else if (val > node->val) { + node->right = removeHelper(node->right, val); + } else { + if (node->left == NULL || node->right == NULL) { + child = node->left; + if (node->right != NULL) { + child = node->right; + } + // Number of child nodes = 0, delete node directly and return + if (child == NULL) { + return NULL; + } else { + // Number of child nodes = 1, delete node directly + node = child; + } + } else { + // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + TreeNode *temp = node->right; + while (temp->left != NULL) { + temp = temp->left; + } + int tempVal = temp->val; + node->right = removeHelper(node->right, temp->val); + node->val = tempVal; + } + } + // Update node height + updateHeight(node); + /* 2. Perform rotation operation to restore balance to this subtree */ + node = rotate(node); + // Return root node of subtree + return node; +} + +/* Remove node */ +// Cannot use remove keyword here due to stdio.h inclusion +void removeItem(AVLTree *tree, int val) { + TreeNode *root = removeHelper(tree->root, val); +} + +/* Search node */ +TreeNode *search(AVLTree *tree, int val) { + TreeNode *cur = tree->root; + // Loop search, exit after passing leaf node + while (cur != NULL) { + if (cur->val < val) { + // Target node is in cur's right subtree + cur = cur->right; + } else if (cur->val > val) { + // Target node is in cur's left subtree + cur = cur->left; + } else { + // Found target node, exit loop + break; + } + } + // Found target node, exit loop + return cur; +} + +void testInsert(AVLTree *tree, int val) { + insert(tree, val); + printf("\nAfter inserting node %d, AVL tree is \n", val); + printTree(tree->root); +} + +void testRemove(AVLTree *tree, int val) { + removeItem(tree, val); + printf("\nAfter removing node %d, AVL tree is \n", val); + printTree(tree->root); +} + +/* Driver Code */ +int main() { + /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + AVLTree *tree = (AVLTree *)newAVLTree(); + /* Insert node */ + // Delete nodes + testInsert(tree, 1); + testInsert(tree, 2); + testInsert(tree, 3); + testInsert(tree, 4); + testInsert(tree, 5); + testInsert(tree, 8); + testInsert(tree, 7); + testInsert(tree, 9); + testInsert(tree, 10); + testInsert(tree, 6); + + /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + testInsert(tree, 7); + + /* Remove node */ + // Delete node with degree 1 + testRemove(tree, 8); // Delete node with degree 2 + testRemove(tree, 5); // Remove node with degree 1 + testRemove(tree, 4); // Remove node with degree 2 + + /* Search node */ + TreeNode *node = search(tree, 7); + printf("\nFound node object value = %d \n", node->val); + + // Free memory + delAVLTree(tree); + return 0; +} diff --git a/en/codes/c/chapter_tree/binary_search_tree.c b/en/codes/c/chapter_tree/binary_search_tree.c new file mode 100644 index 000000000..b09d7bfa0 --- /dev/null +++ b/en/codes/c/chapter_tree/binary_search_tree.c @@ -0,0 +1,171 @@ +/** + * File: binary_search_tree.c + * Created Time: 2023-01-11 + * Author: Reanon (793584285@qq.com) + */ + +#include "../utils/common.h" + +/* Binary search tree structure */ +typedef struct { + TreeNode *root; +} BinarySearchTree; + +/* Constructor */ +BinarySearchTree *newBinarySearchTree() { + // Initialize empty tree + BinarySearchTree *bst = (BinarySearchTree *)malloc(sizeof(BinarySearchTree)); + bst->root = NULL; + return bst; +} + +/* Destructor */ +void delBinarySearchTree(BinarySearchTree *bst) { + freeMemoryTree(bst->root); + free(bst); +} + +/* Get binary tree root node */ +TreeNode *getRoot(BinarySearchTree *bst) { + return bst->root; +} + +/* Search node */ +TreeNode *search(BinarySearchTree *bst, int num) { + TreeNode *cur = bst->root; + // Loop search, exit after passing leaf node + while (cur != NULL) { + if (cur->val < num) { + // Target node is in cur's right subtree + cur = cur->right; + } else if (cur->val > num) { + // Target node is in cur's left subtree + cur = cur->left; + } else { + // Found target node, exit loop + break; + } + } + // Return target node + return cur; +} + +/* Insert node */ +void insert(BinarySearchTree *bst, int num) { + // If tree is empty, initialize root node + if (bst->root == NULL) { + bst->root = newTreeNode(num); + return; + } + TreeNode *cur = bst->root, *pre = NULL; + // Loop search, exit after passing leaf node + while (cur != NULL) { + // Found duplicate node, return directly + if (cur->val == num) { + return; + } + pre = cur; + if (cur->val < num) { + // Insertion position is in cur's right subtree + cur = cur->right; + } else { + // Insertion position is in cur's left subtree + cur = cur->left; + } + } + // Insert node + TreeNode *node = newTreeNode(num); + if (pre->val < num) { + pre->right = node; + } else { + pre->left = node; + } +} + +/* Remove node */ +// Cannot use remove keyword here due to stdio.h inclusion +void removeItem(BinarySearchTree *bst, int num) { + // If tree is empty, return directly + if (bst->root == NULL) + return; + TreeNode *cur = bst->root, *pre = NULL; + // Loop search, exit after passing leaf node + while (cur != NULL) { + // Found node to delete, exit loop + if (cur->val == num) + break; + pre = cur; + if (cur->val < num) { + // Node to delete is in right subtree of root + cur = cur->right; + } else { + // Node to delete is in left subtree of root + cur = cur->left; + } + } + // If no node to delete, return directly + if (cur == NULL) + return; + // Check if node to delete has children + if (cur->left == NULL || cur->right == NULL) { + /* Number of child nodes = 0 or 1 */ + // When number of child nodes = 0 / 1, child = nullptr / that child node + TreeNode *child = cur->left != NULL ? cur->left : cur->right; + // Delete node cur + if (pre->left == cur) { + pre->left = child; + } else { + pre->right = child; + } + // Free memory + free(cur); + } else { + /* Number of child nodes = 2 */ + // Get next node of cur in inorder traversal + TreeNode *tmp = cur->right; + while (tmp->left != NULL) { + tmp = tmp->left; + } + int tmpVal = tmp->val; + // Recursively delete node tmp + removeItem(bst, tmp->val); + // Replace cur with tmp + cur->val = tmpVal; + } +} + +/* Driver Code */ +int main() { + /* Initialize binary search tree */ + int nums[] = {8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15}; + BinarySearchTree *bst = newBinarySearchTree(); + for (int i = 0; i < sizeof(nums) / sizeof(int); i++) { + insert(bst, nums[i]); + } + printf("Initialized binary tree is\n"); + printTree(getRoot(bst)); + + /* Search node */ + TreeNode *node = search(bst, 7); + printf("Found node object value = %d\n", node->val); + + /* Insert node */ + insert(bst, 16); + printf("After inserting node 16, binary tree is\n"); + printTree(getRoot(bst)); + + /* Remove node */ + removeItem(bst, 1); + printf("After removing node 1, binary tree is\n"); + printTree(getRoot(bst)); + removeItem(bst, 2); + printf("After removing node 2, binary tree is\n"); + printTree(getRoot(bst)); + removeItem(bst, 4); + printf("After removing node 4, binary tree is\n"); + printTree(getRoot(bst)); + + // Free memory + delBinarySearchTree(bst); + return 0; +} diff --git a/en/codes/c/chapter_tree/binary_tree.c b/en/codes/c/chapter_tree/binary_tree.c new file mode 100644 index 000000000..d0f006ebb --- /dev/null +++ b/en/codes/c/chapter_tree/binary_tree.c @@ -0,0 +1,43 @@ +/** + * File: binary_tree.c + * Created Time: 2023-01-11 + * Author: Reanon (793584285@qq.com) + */ + +#include "../utils/common.h" + +/* Driver Code */ +int main() { + /* Initialize binary tree */ + // Initialize nodes + TreeNode *n1 = newTreeNode(1); + TreeNode *n2 = newTreeNode(2); + TreeNode *n3 = newTreeNode(3); + TreeNode *n4 = newTreeNode(4); + TreeNode *n5 = newTreeNode(5); + // Build references (pointers) between nodes + n1->left = n2; + n1->right = n3; + n2->left = n4; + n2->right = n5; + printf("Initialize binary tree\n"); + printTree(n1); + + /* Insert node P between n1 -> n2 */ + TreeNode *P = newTreeNode(0); + // Delete node + n1->left = P; + P->left = n2; + printf("After inserting node P\n"); + printTree(n1); + + // Remove node P + n1->left = n2; + // Free memory + free(P); + printf("After removing node P\n"); + printTree(n1); + + freeMemoryTree(n1); + return 0; +} diff --git a/en/codes/c/chapter_tree/binary_tree_bfs.c b/en/codes/c/chapter_tree/binary_tree_bfs.c new file mode 100644 index 000000000..c8e023a50 --- /dev/null +++ b/en/codes/c/chapter_tree/binary_tree_bfs.c @@ -0,0 +1,73 @@ +/** + * File: binary_tree_bfs.c + * Created Time: 2023-01-11 + * Author: Reanon (793584285@qq.com) + */ + +#include "../utils/common.h" + +#define MAX_SIZE 100 + +/* Level-order traversal */ +int *levelOrder(TreeNode *root, int *size) { + /* Auxiliary queue */ + int front, rear; + int index, *arr; + TreeNode *node; + TreeNode **queue; + + /* Auxiliary queue */ + queue = (TreeNode **)malloc(sizeof(TreeNode *) * MAX_SIZE); + // Queue pointer + front = 0, rear = 0; + // Add root node + queue[rear++] = root; + // Initialize a list to save the traversal sequence + /* Auxiliary array */ + arr = (int *)malloc(sizeof(int) * MAX_SIZE); + // Array pointer + index = 0; + while (front < rear) { + // Dequeue + node = queue[front++]; + // Save node value + arr[index++] = node->val; + if (node->left != NULL) { + // Left child node enqueue + queue[rear++] = node->left; + } + if (node->right != NULL) { + // Right child node enqueue + queue[rear++] = node->right; + } + } + // Update array length value + *size = index; + arr = realloc(arr, sizeof(int) * (*size)); + + // Free auxiliary array space + free(queue); + return arr; +} + +/* Driver Code */ +int main() { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + int nums[] = {1, 2, 3, 4, 5, 6, 7}; + int size = sizeof(nums) / sizeof(int); + TreeNode *root = arrayToTree(nums, size); + printf("Initialize binary tree\n"); + printTree(root); + + /* Level-order traversal */ + // Need to pass array length + int *arr = levelOrder(root, &size); + printf("Level-order traversal node print sequence = "); + printArray(arr, size); + + // Free memory + freeMemoryTree(root); + free(arr); + return 0; +} diff --git a/en/codes/c/chapter_tree/binary_tree_dfs.c b/en/codes/c/chapter_tree/binary_tree_dfs.c new file mode 100644 index 000000000..0d77179a3 --- /dev/null +++ b/en/codes/c/chapter_tree/binary_tree_dfs.c @@ -0,0 +1,75 @@ +/** + * File: binary_tree_dfs.c + * Created Time: 2023-01-11 + * Author: Reanon (793584285@qq.com) + */ + +#include "../utils/common.h" + +#define MAX_SIZE 100 + +// Auxiliary array for storing traversal sequence +int arr[MAX_SIZE]; + +/* Preorder traversal */ +void preOrder(TreeNode *root, int *size) { + if (root == NULL) + return; + // Visit priority: root node -> left subtree -> right subtree + arr[(*size)++] = root->val; + preOrder(root->left, size); + preOrder(root->right, size); +} + +/* Inorder traversal */ +void inOrder(TreeNode *root, int *size) { + if (root == NULL) + return; + // Visit priority: left subtree -> root node -> right subtree + inOrder(root->left, size); + arr[(*size)++] = root->val; + inOrder(root->right, size); +} + +/* Postorder traversal */ +void postOrder(TreeNode *root, int *size) { + if (root == NULL) + return; + // Visit priority: left subtree -> right subtree -> root node + postOrder(root->left, size); + postOrder(root->right, size); + arr[(*size)++] = root->val; +} + +/* Driver Code */ +int main() { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + int nums[] = {1, 2, 3, 4, 5, 6, 7}; + int size = sizeof(nums) / sizeof(int); + TreeNode *root = arrayToTree(nums, size); + printf("Initialize binary tree\n"); + printTree(root); + + /* Preorder traversal */ + // Initialize auxiliary array + size = 0; + preOrder(root, &size); + printf("Pre-order traversal node print sequence = "); + printArray(arr, size); + + /* Inorder traversal */ + size = 0; + inOrder(root, &size); + printf("In-order traversal node print sequence = "); + printArray(arr, size); + + /* Postorder traversal */ + size = 0; + postOrder(root, &size); + printf("Post-order traversal node print sequence = "); + printArray(arr, size); + + freeMemoryTree(root); + return 0; +} diff --git a/en/codes/c/utils/CMakeLists.txt b/en/codes/c/utils/CMakeLists.txt new file mode 100644 index 000000000..c1ece2e38 --- /dev/null +++ b/en/codes/c/utils/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(utils + common_test.c + common.h print_util.h + list_node.h tree_node.h + uthash.h) \ No newline at end of file diff --git a/en/codes/c/utils/common.h b/en/codes/c/utils/common.h new file mode 100644 index 000000000..8b9adeff7 --- /dev/null +++ b/en/codes/c/utils/common.h @@ -0,0 +1,36 @@ +/** + * File: common.h + * Created Time: 2022-12-20 + * Author: MolDuM (moldum@163.com)、Reanon (793584285@qq.com) + */ + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include + +#include "list_node.h" +#include "print_util.h" +#include "tree_node.h" +#include "vertex.h" + +// hash table lib +#include "uthash.h" + +#include "vector.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // COMMON_H diff --git a/en/codes/c/utils/common_test.c b/en/codes/c/utils/common_test.c new file mode 100644 index 000000000..a889b423b --- /dev/null +++ b/en/codes/c/utils/common_test.c @@ -0,0 +1,35 @@ +/** + * File: include_test.c + * Created Time: 2023-01-10 + * Author: Reanon (793584285@qq.com) + */ + +#include "common.h" + +void testListNode() { + int nums[] = {2, 3, 5, 6, 7}; + int size = sizeof(nums) / sizeof(int); + ListNode *head = arrToLinkedList(nums, size); + printLinkedList(head); +} + +void testTreeNode() { + int nums[] = {1, 2, 3, INT_MAX, 5, 6, INT_MAX}; + int size = sizeof(nums) / sizeof(int); + TreeNode *root = arrayToTree(nums, size); + + // print tree + printTree(root); + + // tree to arr + int *arr = treeToArray(root, &size); + printArray(arr, size); +} + +int main(int argc, char *argv[]) { + printf("==testListNode==\n"); + testListNode(); + printf("==testTreeNode==\n"); + testTreeNode(); + return 0; +} diff --git a/en/codes/c/utils/list_node.h b/en/codes/c/utils/list_node.h new file mode 100644 index 000000000..c39483302 --- /dev/null +++ b/en/codes/c/utils/list_node.h @@ -0,0 +1,59 @@ +/** + * File: list_node.h + * Created Time: 2023-01-09 + * Author: Reanon (793584285@qq.com) + */ + +#ifndef LIST_NODE_H +#define LIST_NODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Linked list node structure */ +typedef struct ListNode { + int val; // Node value + struct ListNode *next; // Reference to next node +} ListNode; + +/* Constructor, initialize a new node */ +ListNode *newListNode(int val) { + ListNode *node; + node = (ListNode *)malloc(sizeof(ListNode)); + node->val = val; + node->next = NULL; + return node; +} + +/* Deserialize array to linked list */ +ListNode *arrToLinkedList(const int *arr, size_t size) { + if (size <= 0) { + return NULL; + } + + ListNode *dummy = newListNode(0); + ListNode *node = dummy; + for (int i = 0; i < size; i++) { + node->next = newListNode(arr[i]); + node = node->next; + } + return dummy->next; +} + +/* Free memory allocated to linked list */ +void freeMemoryLinkedList(ListNode *cur) { + // Free memory + ListNode *pre; + while (cur != NULL) { + pre = cur; + cur = cur->next; + free(pre); + } +} + +#ifdef __cplusplus +} +#endif + +#endif // LIST_NODE_H diff --git a/en/codes/c/utils/print_util.h b/en/codes/c/utils/print_util.h new file mode 100644 index 000000000..f239c3fcb --- /dev/null +++ b/en/codes/c/utils/print_util.h @@ -0,0 +1,131 @@ +/** + * File: print_util.h + * Created Time: 2022-12-21 + * Author: MolDum (moldum@163.com), Reanon (793584285@qq.com) + */ + +#ifndef PRINT_UTIL_H +#define PRINT_UTIL_H + +#include +#include +#include + +#include "list_node.h" +#include "tree_node.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Print array */ +void printArray(int arr[], int size) { + if (arr == NULL || size == 0) { + printf("[]"); + return; + } + printf("["); + for (int i = 0; i < size - 1; i++) { + printf("%d, ", arr[i]); + } + printf("%d]\n", arr[size - 1]); +} + +/* Print array */ +void printArrayFloat(float arr[], int size) { + if (arr == NULL || size == 0) { + printf("[]"); + return; + } + printf("["); + for (int i = 0; i < size - 1; i++) { + printf("%.2f, ", arr[i]); + } + printf("%.2f]\n", arr[size - 1]); +} + +/* Print linked list */ +void printLinkedList(ListNode *node) { + if (node == NULL) { + return; + } + while (node->next != NULL) { + printf("%d -> ", node->val); + node = node->next; + } + printf("%d\n", node->val); +} + +typedef struct Trunk { + struct Trunk *prev; + char *str; +} Trunk; + +Trunk *newTrunk(Trunk *prev, char *str) { + Trunk *trunk = (Trunk *)malloc(sizeof(Trunk)); + trunk->prev = prev; + trunk->str = (char *)malloc(sizeof(char) * 10); + strcpy(trunk->str, str); + return trunk; +} + +void showTrunks(Trunk *trunk) { + if (trunk == NULL) { + return; + } + showTrunks(trunk->prev); + printf("%s", trunk->str); +} + +/** + * Print binary tree + * This tree printer is borrowed from TECHIE DELIGHT + * https://www.techiedelight.com/c-program-print-binary-tree/ + */ +void printTreeHelper(TreeNode *node, Trunk *prev, bool isRight) { + if (node == NULL) { + return; + } + char *prev_str = " "; + Trunk *trunk = newTrunk(prev, prev_str); + printTreeHelper(node->right, trunk, true); + if (prev == NULL) { + trunk->str = "———"; + } else if (isRight) { + trunk->str = "/———"; + prev_str = " |"; + } else { + trunk->str = "\\———"; + prev->str = prev_str; + } + showTrunks(trunk); + printf("%d\n", node->val); + + if (prev != NULL) { + prev->str = prev_str; + } + trunk->str = " |"; + + printTreeHelper(node->left, trunk, false); +} + +/* Print binary tree */ +void printTree(TreeNode *root) { + printTreeHelper(root, NULL, false); +} + +/* Print heap */ +void printHeap(int arr[], int size) { + TreeNode *root; + printf("Heap array representation:"); + printArray(arr, size); + printf("Heap tree representation:\n"); + root = arrayToTree(arr, size); + printTree(root); +} + +#ifdef __cplusplus +} +#endif + +#endif // PRINT_UTIL_H diff --git a/en/codes/c/utils/tree_node.h b/en/codes/c/utils/tree_node.h new file mode 100644 index 000000000..a95f1a97a --- /dev/null +++ b/en/codes/c/utils/tree_node.h @@ -0,0 +1,107 @@ +/** + * File: tree_node.h + * Created Time: 2023-01-09 + * Author: Reanon (793584285@qq.com) + */ + +#ifndef TREE_NODE_H +#define TREE_NODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define MAX_NODE_SIZE 5000 + +/* Binary tree node structure */ +typedef struct TreeNode { + int val; // Node value + int height; // Node height + struct TreeNode *left; // Left child pointer + struct TreeNode *right; // Right child pointer +} TreeNode; + +/* Constructor */ +TreeNode *newTreeNode(int val) { + TreeNode *node; + + node = (TreeNode *)malloc(sizeof(TreeNode)); + node->val = val; + node->height = 0; + node->left = NULL; + node->right = NULL; + return node; +} + +// For the serialization encoding rules, please refer to: +// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ +// Array representation of binary tree: +// [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] +// Linked list representation of binary tree: +// /——— 15 +// /——— 7 +// /——— 3 +// | \——— 6 +// | \——— 12 +// ——— 1 +// \——— 2 +// | /——— 9 +// \——— 4 +// \——— 8 + +/* Deserialize a list into a binary tree: recursion */ +TreeNode *arrayToTreeDFS(int *arr, int size, int i) { + if (i < 0 || i >= size || arr[i] == INT_MAX) { + return NULL; + } + TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode)); + root->val = arr[i]; + root->left = arrayToTreeDFS(arr, size, 2 * i + 1); + root->right = arrayToTreeDFS(arr, size, 2 * i + 2); + return root; +} + +/* Deserialize a list into a binary tree */ +TreeNode *arrayToTree(int *arr, int size) { + return arrayToTreeDFS(arr, size, 0); +} + +/* Serialize a binary tree into a list: recursion */ +void treeToArrayDFS(TreeNode *root, int i, int *res, int *size) { + if (root == NULL) { + return; + } + while (i >= *size) { + res = realloc(res, (*size + 1) * sizeof(int)); + res[*size] = INT_MAX; + (*size)++; + } + res[i] = root->val; + treeToArrayDFS(root->left, 2 * i + 1, res, size); + treeToArrayDFS(root->right, 2 * i + 2, res, size); +} + +/* Serialize a binary tree into a list */ +int *treeToArray(TreeNode *root, int *size) { + *size = 0; + int *res = NULL; + treeToArrayDFS(root, 0, res, size); + return res; +} + +/* Free binary tree memory */ +void freeMemoryTree(TreeNode *root) { + if (root == NULL) + return; + freeMemoryTree(root->left); + freeMemoryTree(root->right); + free(root); +} + +#ifdef __cplusplus +} +#endif + +#endif // TREE_NODE_H diff --git a/en/codes/c/utils/uthash.h b/en/codes/c/utils/uthash.h new file mode 100644 index 000000000..68693bf39 --- /dev/null +++ b/en/codes/c/utils/uthash.h @@ -0,0 +1,1140 @@ +/* +Copyright (c) 2003-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.3.0 + +#include /* memcmp, memset, strlen */ +#include /* ptrdiff_t */ +#include /* exit */ + +#if defined(HASH_DEFINE_OWN_STDINT) && HASH_DEFINE_OWN_STDINT +/* This codepath is provided for backward compatibility, but I plan to remove it. */ +#warning "HASH_DEFINE_OWN_STDINT is deprecated; please use HASH_NO_STDINT instead" +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#elif defined(HASH_NO_STDINT) && HASH_NO_STDINT +#else +#include /* uint8_t, uint32_t */ +#endif + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__MCST__) /* Elbrus C Compiler */ +#define DECLTYPE(x) (__typeof(x)) +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE(x) +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_bzero +#define uthash_bzero(a,n) memset(a,'\0',n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif + +#ifndef HASH_FUNCTION +#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv) +#endif + +#ifndef HASH_KEYCMP +#define HASH_KEYCMP(a,b,n) memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ +} while (0) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FUNCTION(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl,oomed) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!(tbl)->bloom_bv) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl,oomed) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head,oomed) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } else { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + ) \ + } \ + } \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + do { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + char *_hs_saved_head = (char*)(head); \ + do { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + if (!(oomed)) { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + if (oomed) { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } else { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } else { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ +} while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ +} while (0) + +#endif + + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (const void*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ + HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh,head,delptrhh) \ +do { \ + const struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } else { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) { \ + (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ + } else { \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ + } \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ +} while (0) + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ +do { \ + unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ + HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ +} while (0) +#define HASH_ADD_STR(head,strfield,add) \ +do { \ + unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ +} while (0) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ +do { \ + unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ +} while (0) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#include /* fprintf, stderr */ +#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head,where) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void*)_thh->hh_prev, (void*)_prev); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev != (char*)_thh->prev) { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", \ + (where), (void*)_thh->prev, (void*)_prev); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head,where) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx + * (archive link: https://archive.is/Ivcan ) + */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key = (const unsigned char*)(key); \ + hashv = 0; \ + for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key = (const unsigned char*)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \ + default: ; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + break; \ + default: ; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ +do { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ + && !(addhh)->tbl->noexpand) { \ + HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + HASH_DEL_IN_BKT(head,addhh); \ + } \ + ) \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(head,delhh) \ +do { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ +} while (0) + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + if (!_he_new_buckets) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero(_he_new_buckets, \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ + _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ + (tbl)->nonideal_items++; \ + if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ + _he_newbkt->expand_mult++; \ + } \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ + } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ + ((tbl)->ineff_expands+1U) : 0U; \ + if ((tbl)->ineff_expands > 1U) { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ + } \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ + _hs_psize++; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + if (_hs_q == NULL) { \ + break; \ + } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else if ((cmpfcn( \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ + )) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt = NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if ((src) != NULL) { \ + for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ + _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { \ + _last_elt_hh->next = _elt; \ + } \ + if ((dst) == NULL) { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + } \ + ) \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + } \ + ) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if ((head) != NULL) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + (((head) != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + const void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/en/codes/c/utils/vector.h b/en/codes/c/utils/vector.h new file mode 100644 index 000000000..be9f522a1 --- /dev/null +++ b/en/codes/c/utils/vector.h @@ -0,0 +1,259 @@ +/** + * File: vector.h + * Created Time: 2023-07-13 + * Author: Zuoxun (845242523@qq.com)、Gonglja (glj0@outlook.com) + */ + +#ifndef VECTOR_H +#define VECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Define vector type */ +typedef struct vector { + int size; // Current vector size + int capacity; // Current vector capacity + int depth; // Current vector depth + void **data; // Pointer array to data +} vector; + +/* Construct vector */ +vector *newVector() { + vector *v = malloc(sizeof(vector)); + v->size = 0; + v->capacity = 4; + v->depth = 1; + v->data = malloc(v->capacity * sizeof(void *)); + return v; +} + +/* Construct vector with specified size and default value */ +vector *_newVector(int size, void *elem, int elemSize) { + vector *v = malloc(sizeof(vector)); + v->size = size; + v->capacity = size; + v->depth = 1; + v->data = malloc(v->capacity * sizeof(void *)); + for (int i = 0; i < size; i++) { + void *tmp = malloc(sizeof(char) * elemSize); + memcpy(tmp, elem, elemSize); + v->data[i] = tmp; + } + return v; +} + +/* Destruct vector */ +void delVector(vector *v) { + if (v) { + if (v->depth == 0) { + return; + } else if (v->depth == 1) { + for (int i = 0; i < v->size; i++) { + free(v->data[i]); + } + free(v); + } else { + for (int i = 0; i < v->size; i++) { + delVector(v->data[i]); + } + v->depth--; + } + } +} + +/* Add element (by copy) to vector tail */ +void vectorPushback(vector *v, void *elem, int elemSize) { + if (v->size == v->capacity) { + v->capacity *= 2; + v->data = realloc(v->data, v->capacity * sizeof(void *)); + } + void *tmp = malloc(sizeof(char) * elemSize); + memcpy(tmp, elem, elemSize); + v->data[v->size++] = tmp; +} + +/* Pop element from vector tail */ +void vectorPopback(vector *v) { + if (v->size != 0) { + free(v->data[v->size - 1]); + v->size--; + } +} + +/* Clear vector */ +void vectorClear(vector *v) { + delVector(v); + v->size = 0; + v->capacity = 4; + v->depth = 1; + v->data = malloc(v->capacity * sizeof(void *)); +} + +/* Get vector size */ +int vectorSize(vector *v) { + return v->size; +} + +/* Get vector tail element */ +void *vectorBack(vector *v) { + int n = v->size; + return n > 0 ? v->data[n - 1] : NULL; +} + +/* Get vector head element */ +void *vectorFront(vector *v) { + return v->size > 0 ? v->data[0] : NULL; +} + +/* Get vector element at index pos */ +void *vectorAt(vector *v, int pos) { + if (pos < 0 || pos >= v->size) { + printf("vectorAt: out of range\n"); + return NULL; + } + return v->data[pos]; +} + +/* Set vector element at index pos */ +void vectorSet(vector *v, int pos, void *elem, int elemSize) { + if (pos < 0 || pos >= v->size) { + printf("vectorSet: out of range\n"); + return; + } + free(v->data[pos]); + void *tmp = malloc(sizeof(char) * elemSize); + memcpy(tmp, elem, elemSize); + v->data[pos] = tmp; +} + +/* Expand vector capacity */ +void vectorExpand(vector *v) { + v->capacity *= 2; + v->data = realloc(v->data, v->capacity * sizeof(void *)); +} + +/* Shrink vector capacity */ +void vectorShrink(vector *v) { + v->capacity /= 2; + v->data = realloc(v->data, v->capacity * sizeof(void *)); +} + +/* Insert element at vector index pos */ +void vectorInsert(vector *v, int pos, void *elem, int elemSize) { + if (v->size == v->capacity) { + vectorExpand(v); + } + for (int j = v->size; j > pos; j--) { + v->data[j] = v->data[j - 1]; + } + void *tmp = malloc(sizeof(char) * elemSize); + memcpy(tmp, elem, elemSize); + v->data[pos] = tmp; + v->size++; +} + +/* Delete element at vector index pos */ +void vectorErase(vector *v, int pos) { + if (v->size != 0) { + free(v->data[pos]); + for (int j = pos; j < v->size - 1; j++) { + v->data[j] = v->data[j + 1]; + } + v->size--; + } +} + +/* Swap vector elements */ +void vectorSwap(vector *v, int i, int j) { + void *tmp = v->data[i]; + v->data[i] = v->data[j]; + v->data[j] = tmp; +} + +/* Is vector empty */ +bool vectorEmpty(vector *v) { + return v->size == 0; +} + +/* Is vector full */ +bool vectorFull(vector *v) { + return v->size == v->capacity; +} + +/* Are vectors equal */ +bool vectorEqual(vector *v1, vector *v2) { + if (v1->size != v2->size) { + printf("size not equal\n"); + return false; + } + for (int i = 0; i < v1->size; i++) { + void *a = v1->data[i]; + void *b = v2->data[i]; + if (memcmp(a, b, sizeof(a)) != 0) { + printf("data %d not equal\n", i); + return false; + } + } + return true; +} + +/* Sort vector internally */ +void vectorSort(vector *v, int (*cmp)(const void *, const void *)) { + qsort(v->data, v->size, sizeof(void *), cmp); +} + +/* Print function, need to pass a function to print variables */ +/* Currently only supports printing vector with depth 1 */ +void printVector(vector *v, void (*printFunc)(vector *v, void *p)) { + if (v) { + if (v->depth == 0) { + return; + } else if (v->depth == 1) { + if(v->size == 0) { + printf("\n"); + return; + } + for (int i = 0; i < v->size; i++) { + if (i == 0) { + printf("["); + } else if (i == v->size - 1) { + printFunc(v, v->data[i]); + printf("]\r\n"); + break; + } + printFunc(v, v->data[i]); + printf(","); + } + } else { + for (int i = 0; i < v->size; i++) { + printVector(v->data[i], printFunc); + } + v->depth--; + } + } +} + +/* Currently only supports printing vector with depth 2 */ +void printVectorMatrix(vector *vv, void (*printFunc)(vector *v, void *p)) { + printf("[\n"); + for (int i = 0; i < vv->size; i++) { + vector *v = (vector *)vv->data[i]; + printf(" ["); + for (int j = 0; j < v->size; j++) { + printFunc(v, v->data[j]); + if (j != v->size - 1) + printf(","); + } + printf("],"); + printf("\n"); + } + printf("]\n"); +} + +#ifdef __cplusplus +} +#endif + +#endif // VECTOR_H diff --git a/en/codes/c/utils/vertex.h b/en/codes/c/utils/vertex.h new file mode 100644 index 000000000..0cab3534a --- /dev/null +++ b/en/codes/c/utils/vertex.h @@ -0,0 +1,49 @@ +/** + * File: vertex.h + * Created Time: 2023-10-28 + * Author: krahets (krahets@163.com) + */ + +#ifndef VERTEX_H +#define VERTEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Vertex structure */ +typedef struct { + int val; +} Vertex; + +/* Constructor, initialize a new node */ +Vertex *newVertex(int val) { + Vertex *vet; + vet = (Vertex *)malloc(sizeof(Vertex)); + vet->val = val; + return vet; +} + +/* Convert value array to vertex array */ +Vertex **valsToVets(int *vals, int size) { + Vertex **vertices = (Vertex **)malloc(size * sizeof(Vertex *)); + for (int i = 0; i < size; ++i) { + vertices[i] = newVertex(vals[i]); + } + return vertices; +} + +/* Convert vertex array to value array */ +int *vetsToVals(Vertex **vertices, int size) { + int *vals = (int *)malloc(size * sizeof(int)); + for (int i = 0; i < size; ++i) { + vals[i] = vertices[i]->val; + } + return vals; +} + +#ifdef __cplusplus +} +#endif + +#endif // VERTEX_H diff --git a/en/codes/cpp/chapter_array_and_linkedlist/array.cpp b/en/codes/cpp/chapter_array_and_linkedlist/array.cpp index b555d9db3..d89f28fc1 100644 --- a/en/codes/cpp/chapter_array_and_linkedlist/array.cpp +++ b/en/codes/cpp/chapter_array_and_linkedlist/array.cpp @@ -6,18 +6,18 @@ #include "../utils/common.hpp" -/* Random access to elements */ +/* Random access to element */ int randomAccess(int *nums, int size) { - // Randomly select a number in the range [0, size) + // Randomly select a number from interval [0, size) int randomIndex = rand() % size; - // Retrieve and return a random element + // Retrieve and return the random element int randomNum = nums[randomIndex]; return randomNum; } /* Extend array length */ int *extend(int *nums, int size, int enlarge) { - // Initialize an extended length array + // Initialize an array with extended length int *res = new int[size + enlarge]; // Copy all elements from the original array to the new array for (int i = 0; i < size; i++) { @@ -25,23 +25,23 @@ int *extend(int *nums, int size, int enlarge) { } // Free memory delete[] nums; - // Return the new array after expansion + // Return the extended new array return res; } -/* Insert element num at `index` */ +/* Insert element num at index index in the array */ void insert(int *nums, int size, int num, int index) { - // Move all elements after `index` one position backward + // Move all elements at and after index index backward by one position for (int i = size - 1; i > index; i--) { nums[i] = nums[i - 1]; } - // Assign num to the element at index + // Assign num to the element at index index nums[index] = num; } -/* Remove the element at `index` */ +/* Remove the element at index index */ void remove(int *nums, int size, int index) { - // Move all elements after `index` one position forward + // Move all elements after index index forward by one position for (int i = index; i < size - 1; i++) { nums[i] = nums[i + 1]; } @@ -56,7 +56,7 @@ void traverse(int *nums, int size) { } } -/* Search for a specified element in the array */ +/* Find the specified element in the array */ int find(int *nums, int size, int target) { for (int i = 0; i < size; i++) { if (nums[i] == target) @@ -67,7 +67,7 @@ int find(int *nums, int size, int target) { /* Driver Code */ int main() { - /* Initialize an array */ + /* Initialize array */ int size = 5; int *arr = new int[size]; cout << "Array arr = "; @@ -77,33 +77,33 @@ int main() { cout << "Array nums = "; printArray(nums, size); - /* Random access */ + /* Insert element */ int randomNum = randomAccess(nums, size); - cout << "Get a random element from nums = " << randomNum << endl; + cout << "Get random element in nums " << randomNum << endl; - /* Length extension */ + /* Traverse array */ int enlarge = 3; nums = extend(nums, size, enlarge); size += enlarge; - cout << "Extend the array length to 8, resulting in nums = "; + cout << "Extend array length to 8, resulting in nums = "; printArray(nums, size); /* Insert element */ insert(nums, size, 6, 3); - cout << "Insert the number 6 at index 3, resulting in nums = "; + cout << "Insert number 6 at index 3, resulting in nums = "; printArray(nums, size); /* Remove element */ remove(nums, size, 2); - cout << "Remove the element at index 2, resulting in nums = "; + cout << "Remove element at index 2, resulting in nums = "; printArray(nums, size); /* Traverse array */ traverse(nums, size); - /* Search for elements */ + /* Find element */ int index = find(nums, size, 3); - cout << "Find element 3 in nums, index = " << index << endl; + cout << "Find element 3 in nums, get index = " << index << endl; // Free memory delete[] arr; diff --git a/en/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp b/en/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp index 2767807f8..a054ba086 100644 --- a/en/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp +++ b/en/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp @@ -25,7 +25,7 @@ void remove(ListNode *n0) { delete P; } -/* Access the node at `index` in the linked list */ +/* Access the node at index index in the linked list */ ListNode *access(ListNode *head, int index) { for (int i = 0; i < index; i++) { if (head == nullptr) @@ -35,7 +35,7 @@ ListNode *access(ListNode *head, int index) { return head; } -/* Search for the first node with value target in the linked list */ +/* Find the first node with value target in the linked list */ int find(ListNode *head, int target) { int index = 0; while (head != nullptr) { @@ -61,26 +61,26 @@ int main() { n1->next = n2; n2->next = n3; n3->next = n4; - cout << "The initialized linked list is" << endl; + cout << "Initialized linked list is" << endl; printLinkedList(n0); /* Insert node */ insert(n0, new ListNode(0)); - cout << "Linked list after inserting the node is" << endl; + cout << "Linked list after inserting node is" << endl; printLinkedList(n0); /* Remove node */ remove(n0); - cout << "Linked list after removing the node is" << endl; + cout << "Linked list after removing node is" << endl; printLinkedList(n0); /* Access node */ ListNode *node = access(n0, 3); - cout << "The value of the node at index 3 in the linked list = " << node->val << endl; + cout << "Value of node at index 3 in linked list = " << node->val << endl; /* Search node */ int index = find(n0, 2); - cout << "The index of the node with value 2 in the linked list = " << index << endl; + cout << "Index of node with value 2 in linked list = " << index << endl; // Free memory freeMemoryLinkedList(n0); diff --git a/en/codes/cpp/chapter_array_and_linkedlist/list.cpp b/en/codes/cpp/chapter_array_and_linkedlist/list.cpp index 7c5d6e287..1a3a9cd80 100644 --- a/en/codes/cpp/chapter_array_and_linkedlist/list.cpp +++ b/en/codes/cpp/chapter_array_and_linkedlist/list.cpp @@ -13,21 +13,21 @@ int main() { cout << "List nums = "; printVector(nums); - /* Access element */ - int num = nums[1]; - cout << "Access the element at index 1, obtained num = " << num << endl; - /* Update element */ + int num = nums[1]; + cout << "Access element at index 1, get num = " << num << endl; + + /* Add elements at the end */ nums[1] = 0; - cout << "Update the element at index 1 to 0, resulting in nums = "; + cout << "Update element at index 1 to 0, resulting in nums = "; printVector(nums); - /* Clear list */ + /* Remove element */ nums.clear(); - cout << "After clearing the list, nums = "; + cout << "After clearing list, nums = "; printVector(nums); - /* Add element at the end */ + /* Direct traversal of list elements */ nums.push_back(1); nums.push_back(3); nums.push_back(2); @@ -36,22 +36,22 @@ int main() { cout << "After adding elements, nums = "; printVector(nums); - /* Insert element in the middle */ + /* Sort list */ nums.insert(nums.begin() + 3, 6); - cout << "Insert the number 6 at index 3, resulting in nums = "; + cout << "Insert number 6 at index 3, resulting in nums = "; printVector(nums); /* Remove element */ nums.erase(nums.begin() + 3); - cout << "Remove the element at index 3, resulting in nums = "; + cout << "Remove element at index 3, resulting in nums = "; printVector(nums); - /* Traverse the list by index */ + /* Traverse list by index */ int count = 0; for (int i = 0; i < nums.size(); i++) { count += nums[i]; } - /* Traverse the list elements */ + /* Directly traverse list elements */ count = 0; for (int x : nums) { count += x; @@ -65,7 +65,7 @@ int main() { /* Sort list */ sort(nums.begin(), nums.end()); - cout << "After sorting the list, nums = "; + cout << "After sorting list, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_array_and_linkedlist/my_list.cpp b/en/codes/cpp/chapter_array_and_linkedlist/my_list.cpp index 8af4c6f1d..d3390b679 100644 --- a/en/codes/cpp/chapter_array_and_linkedlist/my_list.cpp +++ b/en/codes/cpp/chapter_array_and_linkedlist/my_list.cpp @@ -12,7 +12,7 @@ class MyList { int *arr; // Array (stores list elements) int arrCapacity = 10; // List capacity int arrSize = 0; // List length (current number of elements) - int extendRatio = 2; // Multiple for each list expansion + int extendRatio = 2; // Multiple by which the list capacity is extended each time public: /* Constructor */ @@ -35,7 +35,7 @@ class MyList { return arrCapacity; } - /* Access element */ + /* Update element */ int get(int index) { // If the index is out of bounds, throw an exception, as below if (index < 0 || index >= size()) @@ -43,16 +43,16 @@ class MyList { return arr[index]; } - /* Update element */ + /* Add elements at the end */ void set(int index, int num) { if (index < 0 || index >= size()) throw out_of_range("Index out of bounds"); arr[index] = num; } - /* Add element at the end */ + /* Direct traversal of list elements */ void add(int num) { - // When the number of elements exceeds capacity, trigger the expansion mechanism + // When the number of elements exceeds capacity, trigger the extension mechanism if (size() == capacity()) extendCapacity(); arr[size()] = num; @@ -60,14 +60,14 @@ class MyList { arrSize++; } - /* Insert element in the middle */ + /* Sort list */ void insert(int index, int num) { if (index < 0 || index >= size()) throw out_of_range("Index out of bounds"); - // When the number of elements exceeds capacity, trigger the expansion mechanism + // When the number of elements exceeds capacity, trigger the extension mechanism if (size() == capacity()) extendCapacity(); - // Move all elements after `index` one position backward + // Move all elements after index index forward by one position for (int j = size() - 1; j >= index; j--) { arr[j + 1] = arr[j]; } @@ -81,7 +81,7 @@ class MyList { if (index < 0 || index >= size()) throw out_of_range("Index out of bounds"); int num = arr[index]; - // Move all elements after `index` one position forward + // Create a new array with length _extend_ratio times the original array, and copy the original array to the new array for (int j = index; j < size() - 1; j++) { arr[j] = arr[j + 1]; } @@ -91,9 +91,9 @@ class MyList { return num; } - /* Extend list */ + /* Driver Code */ void extendCapacity() { - // Create a new array with a length multiple of the original array by extendRatio + // Create a new array with length extendRatio times the original array int newCapacity = capacity() * extendRatio; int *tmp = arr; arr = new int[newCapacity]; @@ -106,9 +106,9 @@ class MyList { arrCapacity = newCapacity; } - /* Convert the list to a Vector for printing */ + /* Convert list to Vector for printing */ vector toVector() { - // Only convert elements within valid length range + // Elements enqueue vector vec(size()); for (int i = 0; i < size(); i++) { vec[i] = arr[i]; @@ -121,7 +121,7 @@ class MyList { int main() { /* Initialize list */ MyList *nums = new MyList(); - /* Add element at the end */ + /* Direct traversal of list elements */ nums->add(1); nums->add(3); nums->add(2); @@ -132,34 +132,34 @@ int main() { printVector(vec); cout << "Capacity = " << nums->capacity() << ", length = " << nums->size() << endl; - /* Insert element in the middle */ + /* Sort list */ nums->insert(3, 6); - cout << "Insert the number 6 at index 3, resulting in nums = "; + cout << "Insert number 6 at index 3, resulting in nums = "; vec = nums->toVector(); printVector(vec); /* Remove element */ nums->remove(3); - cout << "Remove the element at index 3, resulting in nums = "; + cout << "Remove element at index 3, resulting in nums = "; vec = nums->toVector(); printVector(vec); - /* Access element */ - int num = nums->get(1); - cout << "Access the element at index 1, obtained num = " << num << endl; - /* Update element */ + int num = nums->get(1); + cout << "Access element at index 1, get num = " << num << endl; + + /* Add elements at the end */ nums->set(1, 0); - cout << "Update the element at index 1 to 0, resulting in nums = "; + cout << "Update element at index 1 to 0, resulting in nums = "; vec = nums->toVector(); printVector(vec); - /* Test expansion mechanism */ + /* Test capacity expansion mechanism */ for (int i = 0; i < 10; i++) { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism at this time + // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism nums->add(i); } - cout << "After extending, list nums = "; + cout << "List nums after expansion = "; vec = nums->toVector(); printVector(vec); cout << "Capacity = " << nums->capacity() << ", length = " << nums->size() << endl; diff --git a/en/codes/cpp/chapter_backtracking/n_queens.cpp b/en/codes/cpp/chapter_backtracking/n_queens.cpp index 6fbbacd8e..224330c47 100644 --- a/en/codes/cpp/chapter_backtracking/n_queens.cpp +++ b/en/codes/cpp/chapter_backtracking/n_queens.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: n queens */ +/* Backtracking algorithm: N queens */ void backtrack(int row, int n, vector> &state, vector>> &res, vector &cols, vector &diags1, vector &diags2) { // When all rows are placed, record the solution @@ -16,30 +16,30 @@ void backtrack(int row, int n, vector> &state, vector>> nQueens(int n) { - // Initialize an n*n size chessboard, where 'Q' represents the queen and '#' represents an empty spot + // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell vector> state(n, vector(n, "#")); - vector cols(n, false); // Record columns with queens - vector diags1(2 * n - 1, false); // Record main diagonals with queens - vector diags2(2 * n - 1, false); // Record minor diagonals with queens + vector cols(n, false); // Record whether there is a queen in the column + vector diags1(2 * n - 1, false); // Record whether there is a queen on the main diagonal + vector diags2(2 * n - 1, false); // Record whether there is a queen on the anti-diagonal vector>> res; backtrack(0, n, state, res, cols, diags1, diags2); @@ -52,8 +52,8 @@ int main() { int n = 4; vector>> res = nQueens(n); - cout << "Input the dimensions of the chessboard as " << n << endl; - cout << "Total number of queen placement solutions = " << res.size() << endl; + cout << "Input board size is " << n << endl; + cout << "Total queen placement solutions: " << res.size() << endl; for (const vector> &state : res) { cout << "--------------------" << endl; for (const vector &row : state) { diff --git a/en/codes/cpp/chapter_backtracking/permutations_i.cpp b/en/codes/cpp/chapter_backtracking/permutations_i.cpp index be9907ab6..10813b7fd 100644 --- a/en/codes/cpp/chapter_backtracking/permutations_i.cpp +++ b/en/codes/cpp/chapter_backtracking/permutations_i.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: Permutation I */ +/* Backtracking algorithm: Permutations I */ void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) { // When the state length equals the number of elements, record the solution if (state.size() == choices.size()) { @@ -18,19 +18,19 @@ void backtrack(vector &state, const vector &choices, vector &sel int choice = choices[i]; // Pruning: do not allow repeated selection of elements if (!selected[i]) { - // Attempt: make a choice, update the state + // Attempt: make choice, update state selected[i] = true; state.push_back(choice); // Proceed to the next round of selection backtrack(state, choices, selected, res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state selected[i] = false; state.pop_back(); } } } -/* Permutation I */ +/* Permutations I */ vector> permutationsI(vector nums) { vector state; vector selected(nums.size(), false); diff --git a/en/codes/cpp/chapter_backtracking/permutations_ii.cpp b/en/codes/cpp/chapter_backtracking/permutations_ii.cpp index 4cdbe8faa..cb2ccd061 100644 --- a/en/codes/cpp/chapter_backtracking/permutations_ii.cpp +++ b/en/codes/cpp/chapter_backtracking/permutations_ii.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: Permutation II */ +/* Backtracking algorithm: Permutations II */ void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) { // When the state length equals the number of elements, record the solution if (state.size() == choices.size()) { @@ -19,20 +19,20 @@ void backtrack(vector &state, const vector &choices, vector &sel int choice = choices[i]; // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements if (!selected[i] && duplicated.find(choice) == duplicated.end()) { - // Attempt: make a choice, update the state - duplicated.emplace(choice); // Record selected element values + // Attempt: make choice, update state + duplicated.emplace(choice); // Record the selected element value selected[i] = true; state.push_back(choice); // Proceed to the next round of selection backtrack(state, choices, selected, res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state selected[i] = false; state.pop_back(); } } } -/* Permutation II */ +/* Permutations II */ vector> permutationsII(vector nums) { vector state; vector selected(nums.size(), false); diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp index b92cf43d7..9d1134ede 100644 --- a/en/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp @@ -8,7 +8,7 @@ vector res; -/* Pre-order traversal: Example one */ +/* Preorder traversal: Example 1 */ void preOrder(TreeNode *root) { if (root == nullptr) { return; @@ -27,7 +27,7 @@ int main() { cout << "\nInitialize binary tree" << endl; printTree(root); - // Pre-order traversal + // Preorder traversal preOrder(root); cout << "\nOutput all nodes with value 7" << endl; diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp index 08905abc2..1c1bb0875 100644 --- a/en/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp @@ -9,7 +9,7 @@ vector path; vector> res; -/* Pre-order traversal: Example two */ +/* Preorder traversal: Example 2 */ void preOrder(TreeNode *root) { if (root == nullptr) { return; @@ -22,7 +22,7 @@ void preOrder(TreeNode *root) { } preOrder(root->left); preOrder(root->right); - // Retract + // Backtrack path.pop_back(); } @@ -32,10 +32,10 @@ int main() { cout << "\nInitialize binary tree" << endl; printTree(root); - // Pre-order traversal + // Preorder traversal preOrder(root); - cout << "\nOutput all root-to-node 7 paths" << endl; + cout << "\nOutput all paths from root node to node 7" << endl; for (vector &path : res) { vector vals; for (TreeNode *node : path) { diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp index 2e9f0a7c9..4d8cc19fe 100644 --- a/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp @@ -9,7 +9,7 @@ vector path; vector> res; -/* Pre-order traversal: Example three */ +/* Preorder traversal: Example 3 */ void preOrder(TreeNode *root) { // Pruning if (root == nullptr || root->val == 3) { @@ -23,7 +23,7 @@ void preOrder(TreeNode *root) { } preOrder(root->left); preOrder(root->right); - // Retract + // Backtrack path.pop_back(); } @@ -33,10 +33,10 @@ int main() { cout << "\nInitialize binary tree" << endl; printTree(root); - // Pre-order traversal + // Preorder traversal preOrder(root); - cout << "\nOutput all root-to-node 7 paths, requiring paths not to include nodes with value 3" << endl; + cout << "\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3" << endl; for (vector &path : res) { vector vals; for (TreeNode *node : path) { diff --git a/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp index a1f06fa73..1441c02e0 100644 --- a/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp +++ b/en/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Determine if the current state is a solution */ +/* Check if the current state is a solution */ bool isSolution(vector &state) { return !state.empty() && state.back()->val == 7; } @@ -16,7 +16,7 @@ void recordSolution(vector &state, vector> &res) res.push_back(state); } -/* Determine if the choice is legal under the current state */ +/* Check if the choice is valid under the current state */ bool isValid(vector &state, TreeNode *choice) { return choice != nullptr && choice->val != 3; } @@ -31,23 +31,23 @@ void undoChoice(vector &state, TreeNode *choice) { state.pop_back(); } -/* Backtracking algorithm: Example three */ +/* Backtracking algorithm: Example 3 */ void backtrack(vector &state, vector &choices, vector> &res) { - // Check if it's a solution + // Check if it is a solution if (isSolution(state)) { // Record solution recordSolution(state, res); } // Traverse all choices for (TreeNode *choice : choices) { - // Pruning: check if the choice is legal + // Pruning: check if the choice is valid if (isValid(state, choice)) { - // Attempt: make a choice, update the state + // Attempt: make choice, update state makeChoice(state, choice); // Proceed to the next round of selection vector nextChoices{choice->left, choice->right}; backtrack(state, nextChoices, res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state undoChoice(state, choice); } } @@ -65,7 +65,7 @@ int main() { vector> res; backtrack(state, choices, res); - cout << "\nOutput all root-to-node 7 paths, requiring paths not to include nodes with value 3" << endl; + cout << "\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3" << endl; for (vector &path : res) { vector vals; for (TreeNode *node : path) { diff --git a/en/codes/cpp/chapter_backtracking/subset_sum_i.cpp b/en/codes/cpp/chapter_backtracking/subset_sum_i.cpp index d5971e903..d2c406730 100644 --- a/en/codes/cpp/chapter_backtracking/subset_sum_i.cpp +++ b/en/codes/cpp/chapter_backtracking/subset_sum_i.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: Subset Sum I */ +/* Backtracking algorithm: Subset sum I */ void backtrack(vector &state, int target, vector &choices, int start, vector> &res) { // When the subset sum equals target, record the solution if (target == 0) { @@ -14,23 +14,23 @@ void backtrack(vector &state, int target, vector &choices, int start, return; } // Traverse all choices - // Pruning two: start traversing from start to avoid generating duplicate subsets + // Pruning 2: start traversing from start to avoid generating duplicate subsets for (int i = start; i < choices.size(); i++) { - // Pruning one: if the subset sum exceeds target, end the loop immediately + // Pruning 1: if the subset sum exceeds target, end the loop directly // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target if (target - choices[i] < 0) { break; } - // Attempt: make a choice, update target, start + // Attempt: make choice, update target, start state.push_back(choices[i]); // Proceed to the next round of selection backtrack(state, target - choices[i], choices, i, res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state state.pop_back(); } } -/* Solve Subset Sum I */ +/* Solve subset sum I */ vector> subsetSumI(vector &nums, int target) { vector state; // State (subset) sort(nums.begin(), nums.end()); // Sort nums @@ -50,7 +50,7 @@ int main() { cout << "Input array nums = "; printVector(nums); cout << "target = " << target << endl; - cout << "All subsets summing to " << target << "is" << endl; + cout << "All subsets with sum equal to " << target << " are res = " << endl; printVectorMatrix(res); return 0; diff --git a/en/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp b/en/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp index e06e6d0a4..273e11c62 100644 --- a/en/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp +++ b/en/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: Subset Sum I */ +/* Backtracking algorithm: Subset sum I */ void backtrack(vector &state, int target, int total, vector &choices, vector> &res) { // When the subset sum equals target, record the solution if (total == target) { @@ -15,20 +15,20 @@ void backtrack(vector &state, int target, int total, vector &choices, } // Traverse all choices for (size_t i = 0; i < choices.size(); i++) { - // Pruning: if the subset sum exceeds target, skip that choice + // Pruning: if the subset sum exceeds target, skip this choice if (total + choices[i] > target) { continue; } - // Attempt: make a choice, update elements and total + // Attempt: make choice, update element sum total state.push_back(choices[i]); // Proceed to the next round of selection backtrack(state, target, total + choices[i], choices, res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state state.pop_back(); } } -/* Solve Subset Sum I (including duplicate subsets) */ +/* Solve subset sum I (including duplicate subsets) */ vector> subsetSumINaive(vector &nums, int target) { vector state; // State (subset) int total = 0; // Subset sum @@ -47,7 +47,7 @@ int main() { cout << "Input array nums = "; printVector(nums); cout << "target = " << target << endl; - cout << "All subsets summing to " << target << "is" << endl; + cout << "All subsets with sum equal to " << target << " are res = " << endl; printVectorMatrix(res); return 0; diff --git a/en/codes/cpp/chapter_backtracking/subset_sum_ii.cpp b/en/codes/cpp/chapter_backtracking/subset_sum_ii.cpp index 49589cad0..b452f1f97 100644 --- a/en/codes/cpp/chapter_backtracking/subset_sum_ii.cpp +++ b/en/codes/cpp/chapter_backtracking/subset_sum_ii.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Backtracking algorithm: Subset Sum II */ +/* Backtracking algorithm: Subset sum II */ void backtrack(vector &state, int target, vector &choices, int start, vector> &res) { // When the subset sum equals target, record the solution if (target == 0) { @@ -14,28 +14,28 @@ void backtrack(vector &state, int target, vector &choices, int start, return; } // Traverse all choices - // Pruning two: start traversing from start to avoid generating duplicate subsets - // Pruning three: start traversing from start to avoid repeatedly selecting the same element + // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Pruning 3: start traversing from start to avoid repeatedly selecting the same element for (int i = start; i < choices.size(); i++) { - // Pruning one: if the subset sum exceeds target, end the loop immediately + // Pruning 1: if the subset sum exceeds target, end the loop directly // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target if (target - choices[i] < 0) { break; } - // Pruning four: if the element equals the left element, it indicates that the search branch is repeated, skip it + // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly if (i > start && choices[i] == choices[i - 1]) { continue; } - // Attempt: make a choice, update target, start + // Attempt: make choice, update target, start state.push_back(choices[i]); // Proceed to the next round of selection backtrack(state, target - choices[i], choices, i + 1, res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state state.pop_back(); } } -/* Solve Subset Sum II */ +/* Solve subset sum II */ vector> subsetSumII(vector &nums, int target) { vector state; // State (subset) sort(nums.begin(), nums.end()); // Sort nums @@ -55,7 +55,7 @@ int main() { cout << "Input array nums = "; printVector(nums); cout << "target = " << target << endl; - cout << "All subsets summing to " << target << "is" << endl; + cout << "All subsets with sum equal to " << target << " are res = " << endl; printVectorMatrix(res); return 0; diff --git a/en/codes/cpp/chapter_computational_complexity/iteration.cpp b/en/codes/cpp/chapter_computational_complexity/iteration.cpp index d8858ebbd..09eb65f22 100644 --- a/en/codes/cpp/chapter_computational_complexity/iteration.cpp +++ b/en/codes/cpp/chapter_computational_complexity/iteration.cpp @@ -9,7 +9,7 @@ /* for loop */ int forLoop(int n) { int res = 0; - // Loop sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n for (int i = 1; i <= n; ++i) { res += i; } @@ -20,7 +20,7 @@ int forLoop(int n) { int whileLoop(int n) { int res = 0; int i = 1; // Initialize condition variable - // Loop sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n while (i <= n) { res += i; i++; // Update condition variable @@ -32,7 +32,7 @@ int whileLoop(int n) { int whileLoopII(int n) { int res = 0; int i = 1; // Initialize condition variable - // Loop sum 1, 4, 10, ... + // Sum 1, 4, 10, ... while (i <= n) { res += i; // Update condition variable @@ -42,7 +42,7 @@ int whileLoopII(int n) { return res; } -/* Double for loop */ +/* Nested for loop */ string nestedForLoop(int n) { ostringstream res; // Loop i = 1, 2, ..., n-1, n @@ -61,16 +61,16 @@ int main() { int res; res = forLoop(n); - cout << "\nSum result of the for loop res = " << res << endl; + cout << "\nfor loop sum result res = " << res << endl; res = whileLoop(n); - cout << "\nSum result of the while loop res = " << res << endl; + cout << "\nwhile loop sum result res = " << res << endl; res = whileLoopII(n); - cout << "\nSum result of the while loop (with two updates) res = " << res << endl; + cout << "\nwhile loop (two updates) sum result res = " << res << endl; string resStr = nestedForLoop(n); - cout << "\nResult of the double for loop traversal = " << resStr << endl; + cout << "\nDouble for loop traversal result " << resStr << endl; return 0; } diff --git a/en/codes/cpp/chapter_computational_complexity/recursion.cpp b/en/codes/cpp/chapter_computational_complexity/recursion.cpp index 686691b5c..bc6ecc067 100644 --- a/en/codes/cpp/chapter_computational_complexity/recursion.cpp +++ b/en/codes/cpp/chapter_computational_complexity/recursion.cpp @@ -11,25 +11,25 @@ int recur(int n) { // Termination condition if (n == 1) return 1; - // Recursive: recursive call + // Recurse: recursive call int res = recur(n - 1); // Return: return result return n + res; } -/* Simulate recursion with iteration */ +/* Simulate recursion using iteration */ int forLoopRecur(int n) { // Use an explicit stack to simulate the system call stack stack stack; int res = 0; - // Recursive: recursive call + // Recurse: recursive call for (int i = n; i > 0; i--) { - // Simulate "recursive" by "pushing onto the stack" + // Simulate "recurse" with "push" stack.push(i); } // Return: return result while (!stack.empty()) { - // Simulate "return" by "popping from the stack" + // Simulate "return" with "pop" res += stack.top(); stack.pop(); } @@ -46,7 +46,7 @@ int tailRecur(int n, int res) { return tailRecur(n - 1, res + n); } -/* Fibonacci sequence: Recursion */ +/* Fibonacci sequence: recursion */ int fib(int n) { // Termination condition f(1) = 0, f(2) = 1 if (n == 1 || n == 2) @@ -63,16 +63,16 @@ int main() { int res; res = recur(n); - cout << "\nSum result of the recursive function res = " << res << endl; + cout << "\nRecursive function sum result res = " << res << endl; res = forLoopRecur(n); - cout << "\nSum result using iteration to simulate recursion res = " << res << endl; + cout << "\nUsing iteration to simulate recursive sum result res = " << res << endl; res = tailRecur(n, 0); - cout << "\nSum result of the tail-recursive function res = " << res << endl; + cout << "\nTail recursive function sum result res = " << res << endl; res = fib(n); - cout << "The " << n << "th number in the Fibonacci sequence is " << res << endl; + cout << "\nThe " << n << "th term of the Fibonacci sequence is " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_computational_complexity/space_complexity.cpp b/en/codes/cpp/chapter_computational_complexity/space_complexity.cpp index bcc0c1c55..66e1d4eb9 100644 --- a/en/codes/cpp/chapter_computational_complexity/space_complexity.cpp +++ b/en/codes/cpp/chapter_computational_complexity/space_complexity.cpp @@ -12,26 +12,26 @@ int func() { return 0; } -/* Constant complexity */ +/* Constant order */ void constant(int n) { // Constants, variables, objects occupy O(1) space const int a = 0; int b = 0; vector nums(10000); ListNode node(0); - // Variables in a loop occupy O(1) space + // Variables in the loop occupy O(1) space for (int i = 0; i < n; i++) { int c = 0; } - // Functions in a loop occupy O(1) space + // Functions in the loop occupy O(1) space for (int i = 0; i < n; i++) { func(); } } -/* Linear complexity */ +/* Linear order */ void linear(int n) { - // Array of length n occupies O(n) space + // Array of length n uses O(n) space vector nums(n); // A list of length n occupies O(n) space vector nodes; @@ -45,7 +45,7 @@ void linear(int n) { } } -/* Linear complexity (recursive implementation) */ +/* Linear order (recursive implementation) */ void linearRecur(int n) { cout << "Recursion n = " << n << endl; if (n == 1) @@ -53,9 +53,9 @@ void linearRecur(int n) { linearRecur(n - 1); } -/* Quadratic complexity */ +/* Exponential order */ void quadratic(int n) { - // A two-dimensional list occupies O(n^2) space + // 2D list uses O(n^2) space vector> numMatrix; for (int i = 0; i < n; i++) { vector tmp; @@ -66,16 +66,16 @@ void quadratic(int n) { } } -/* Quadratic complexity (recursive implementation) */ +/* Quadratic order (recursive implementation) */ int quadraticRecur(int n) { if (n <= 0) return 0; vector nums(n); - cout << "Recursive n = " << n << ", length of nums = " << nums.size() << endl; + cout << "In recursion n = " << n << ", nums length = " << nums.size() << endl; return quadraticRecur(n - 1); } -/* Exponential complexity (building a full binary tree) */ +/* Driver Code */ TreeNode *buildTree(int n) { if (n == 0) return nullptr; @@ -88,15 +88,15 @@ TreeNode *buildTree(int n) { /* Driver Code */ int main() { int n = 5; - // Constant complexity + // Constant order constant(n); - // Linear complexity + // Linear order linear(n); linearRecur(n); - // Quadratic complexity + // Exponential order quadratic(n); quadraticRecur(n); - // Exponential complexity + // Exponential order TreeNode *root = buildTree(n); printTree(root); diff --git a/en/codes/cpp/chapter_computational_complexity/time_complexity.cpp b/en/codes/cpp/chapter_computational_complexity/time_complexity.cpp index 3035e75d5..23f18dca9 100644 --- a/en/codes/cpp/chapter_computational_complexity/time_complexity.cpp +++ b/en/codes/cpp/chapter_computational_complexity/time_complexity.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Constant complexity */ +/* Constant order */ int constant(int n) { int count = 0; int size = 100000; @@ -15,7 +15,7 @@ int constant(int n) { return count; } -/* Linear complexity */ +/* Linear order */ int linear(int n) { int count = 0; for (int i = 0; i < n; i++) @@ -23,20 +23,20 @@ int linear(int n) { return count; } -/* Linear complexity (traversing an array) */ +/* Linear order (traversing array) */ int arrayTraversal(vector &nums) { int count = 0; - // Loop count is proportional to the length of the array + // Number of iterations is proportional to the array length for (int num : nums) { count++; } return count; } -/* Quadratic complexity */ +/* Exponential order */ int quadratic(int n) { int count = 0; - // Loop count is squared in relation to the data size n + // Number of iterations is quadratically related to the data size n for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { count++; @@ -45,29 +45,29 @@ int quadratic(int n) { return count; } -/* Quadratic complexity (bubble sort) */ +/* Quadratic order (bubble sort) */ int bubbleSort(vector &nums) { int count = 0; // Counter // Outer loop: unsorted range is [0, i] for (int i = nums.size() - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - count += 3; // Element swap includes 3 individual operations + count += 3; // Element swap includes 3 unit operations } } } return count; } -/* Exponential complexity (loop implementation) */ +/* Exponential order (loop implementation) */ int exponential(int n) { int count = 0, base = 1; - // Cells split into two every round, forming the sequence 1, 2, 4, 8, ..., 2^(n-1) + // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) for (int i = 0; i < n; i++) { for (int j = 0; j < base; j++) { count++; @@ -78,14 +78,14 @@ int exponential(int n) { return count; } -/* Exponential complexity (recursive implementation) */ +/* Exponential order (recursive implementation) */ int expRecur(int n) { if (n == 1) return 1; return expRecur(n - 1) + expRecur(n - 1) + 1; } -/* Logarithmic complexity (loop implementation) */ +/* Logarithmic order (loop implementation) */ int logarithmic(int n) { int count = 0; while (n > 1) { @@ -95,14 +95,14 @@ int logarithmic(int n) { return count; } -/* Logarithmic complexity (recursive implementation) */ +/* Logarithmic order (recursive implementation) */ int logRecur(int n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } -/* Linear logarithmic complexity */ +/* Linearithmic order */ int linearLogRecur(int n) { if (n <= 1) return 1; @@ -113,12 +113,12 @@ int linearLogRecur(int n) { return count; } -/* Factorial complexity (recursive implementation) */ +/* Factorial order (recursive implementation) */ int factorialRecur(int n) { if (n == 0) return 1; int count = 0; - // From 1 split into n + // Split from 1 into n for (int i = 0; i < n; i++) { count += factorialRecur(n - 1); } @@ -127,42 +127,42 @@ int factorialRecur(int n) { /* Driver Code */ int main() { - // Can modify n to experience the trend of operation count changes under various complexities + // You can modify n to run and observe the trend of the number of operations for various complexities int n = 8; cout << "Input data size n = " << n << endl; int count = constant(n); - cout << "Number of constant complexity operations = " << count << endl; + cout << "Constant order operation count = " << count << endl; count = linear(n); - cout << "Number of linear complexity operations = " << count << endl; + cout << "Linear order operation count = " << count << endl; vector arr(n); count = arrayTraversal(arr); - cout << "Number of linear complexity operations (traversing the array) = " << count << endl; + cout << "Linear order (array traversal) operation count = " << count << endl; count = quadratic(n); - cout << "Number of quadratic order operations = " << count << endl; + cout << "Quadratic order operation count = " << count << endl; vector nums(n); for (int i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] count = bubbleSort(nums); - cout << "Number of quadratic order operations (bubble sort) = " << count << endl; + cout << "Quadratic order (bubble sort) operation count = " << count << endl; count = exponential(n); - cout << "Number of exponential complexity operations (implemented by loop) = " << count << endl; + cout << "Exponential order (loop implementation) operation count = " << count << endl; count = expRecur(n); - cout << "Number of exponential complexity operations (implemented by recursion) = " << count << endl; + cout << "Exponential order (recursive implementation) operation count = " << count << endl; count = logarithmic(n); - cout << "Number of logarithmic complexity operations (implemented by loop) = " << count << endl; + cout << "Logarithmic order (loop implementation) operation count = " << count << endl; count = logRecur(n); - cout << "Number of logarithmic complexity operations (implemented by recursion) = " << count << endl; + cout << "Logarithmic order (recursive implementation) operation count = " << count << endl; count = linearLogRecur(n); - cout << "Number of linear logarithmic complexity operations (implemented by recursion) = " << count << endl; + cout << "Linearithmic order (recursive implementation) operation count = " << count << endl; count = factorialRecur(n); - cout << "Number of factorial complexity operations (implemented by recursion) = " << count << endl; + cout << "Factorial order (recursive implementation) operation count = " << count << endl; return 0; } diff --git a/en/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp b/en/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp index 10bb2a8f2..8f30f2f7d 100644 --- a/en/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp +++ b/en/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp @@ -6,14 +6,14 @@ #include "../utils/common.hpp" -/* Generate an array with elements {1, 2, ..., n} in a randomly shuffled order */ +/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ vector randomNumbers(int n) { vector nums(n); // Generate array nums = { 1, 2, 3, ..., n } for (int i = 0; i < n; i++) { nums[i] = i + 1; } - // Generate a random seed using system time + // Use system time to generate random seed unsigned seed = chrono::system_clock::now().time_since_epoch().count(); // Randomly shuffle array elements shuffle(nums.begin(), nums.end(), default_random_engine(seed)); @@ -23,8 +23,8 @@ vector randomNumbers(int n) { /* Find the index of number 1 in array nums */ int findOne(vector &nums) { for (int i = 0; i < nums.size(); i++) { - // When element 1 is at the start of the array, achieve best time complexity O(1) - // When element 1 is at the end of the array, achieve worst time complexity O(n) + // When element 1 is at the head of the array, best time complexity O(1) is achieved + // When element 1 is at the tail of the array, worst time complexity O(n) is achieved if (nums[i] == 1) return i; } @@ -37,9 +37,9 @@ int main() { int n = 100; vector nums = randomNumbers(n); int index = findOne(nums); - cout << "\nThe array [ 1, 2, ..., n ] after being shuffled = "; + cout << "\nArray [ 1, 2, ..., n ] after shuffling = "; printVector(nums); - cout << "The index of number 1 is " << index << endl; + cout << "Index of number 1 is " << index << endl; } return 0; } diff --git a/en/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp b/en/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp index 78f9e8333..d8b3ba398 100644 --- a/en/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp +++ b/en/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp @@ -8,20 +8,20 @@ /* Binary search: problem f(i, j) */ int dfs(vector &nums, int target, int i, int j) { - // If the interval is empty, indicating no target element, return -1 + // If the interval is empty, it means there is no target element, return -1 if (i > j) { return -1; } - // Calculate midpoint index m - int m = i + (j - i) / 2; + // Calculate the midpoint index m + int m = (i + j) / 2; if (nums[m] < target) { - // Recursive subproblem f(m+1, j) + // Recursion subproblem f(m+1, j) return dfs(nums, target, m + 1, j); } else if (nums[m] > target) { - // Recursive subproblem f(i, m-1) + // Recursion subproblem f(i, m-1) return dfs(nums, target, i, m - 1); } else { - // Found the target element, thus return its index + // Found the target element, return its index return m; } } @@ -29,7 +29,7 @@ int dfs(vector &nums, int target, int i, int j) { /* Binary search */ int binarySearch(vector &nums, int target) { int n = nums.size(); - // Solve problem f(0, n-1) + // Solve the problem f(0, n-1) return dfs(nums, target, 0, n - 1); } @@ -38,9 +38,9 @@ int main() { int target = 6; vector nums = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; - // Binary search (double closed interval) + // Binary search (closed interval on both sides) int index = binarySearch(nums, target); - cout << "Index of target element 6 =" << index << endl; + cout << "Index of target element 6 = " << index << endl; return 0; -} +} \ No newline at end of file diff --git a/en/codes/cpp/chapter_divide_and_conquer/build_tree.cpp b/en/codes/cpp/chapter_divide_and_conquer/build_tree.cpp index 636d0db7f..bb59dc459 100644 --- a/en/codes/cpp/chapter_divide_and_conquer/build_tree.cpp +++ b/en/codes/cpp/chapter_divide_and_conquer/build_tree.cpp @@ -6,26 +6,26 @@ #include "../utils/common.hpp" -/* Build binary tree: Divide and conquer */ +/* Build binary tree: divide and conquer */ TreeNode *dfs(vector &preorder, unordered_map &inorderMap, int i, int l, int r) { - // Terminate when subtree interval is empty + // Terminate when the subtree interval is empty if (r - l < 0) return NULL; - // Initialize root node + // Initialize the root node TreeNode *root = new TreeNode(preorder[i]); - // Query m to divide left and right subtrees + // Query m to divide the left and right subtrees int m = inorderMap[preorder[i]]; - // Subproblem: build left subtree + // Subproblem: build the left subtree root->left = dfs(preorder, inorderMap, i + 1, l, m - 1); - // Subproblem: build right subtree + // Subproblem: build the right subtree root->right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r); - // Return root node + // Return the root node return root; } /* Build binary tree */ TreeNode *buildTree(vector &preorder, vector &inorder) { - // Initialize hash table, storing in-order elements to indices mapping + // Initialize hash map, storing the mapping from inorder elements to indices unordered_map inorderMap; for (int i = 0; i < inorder.size(); i++) { inorderMap[inorder[i]] = i; @@ -38,9 +38,9 @@ TreeNode *buildTree(vector &preorder, vector &inorder) { int main() { vector preorder = {3, 9, 2, 1, 7}; vector inorder = {9, 3, 1, 2, 7}; - cout << "Pre-order traversal = "; + cout << "Preorder traversal = "; printVector(preorder); - cout << "In-order traversal = "; + cout << "Inorder traversal = "; printVector(inorder); TreeNode *root = buildTree(preorder, inorder); diff --git a/en/codes/cpp/chapter_divide_and_conquer/hanota.cpp b/en/codes/cpp/chapter_divide_and_conquer/hanota.cpp index c98f6fd86..dd644025d 100644 --- a/en/codes/cpp/chapter_divide_and_conquer/hanota.cpp +++ b/en/codes/cpp/chapter_divide_and_conquer/hanota.cpp @@ -6,40 +6,40 @@ #include "../utils/common.hpp" -/* Move a disc */ +/* Move a disk */ void move(vector &src, vector &tar) { - // Take out a disc from the top of src + // Take out a disk from the top of src int pan = src.back(); src.pop_back(); - // Place the disc on top of tar + // Place the disk on top of tar tar.push_back(pan); } /* Solve the Tower of Hanoi problem f(i) */ void dfs(int i, vector &src, vector &buf, vector &tar) { - // If only one disc remains on src, move it to tar + // If there is only one disk left in src, move it directly to tar if (i == 1) { move(src, tar); return; } - // Subproblem f(i-1): move the top i-1 discs from src with the help of tar to buf + // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf); - // Subproblem f(1): move the remaining one disc from src to tar + // Subproblem f(1): move the remaining disk from src to tar move(src, tar); - // Subproblem f(i-1): move the top i-1 discs from buf with the help of src to tar + // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar); } /* Solve the Tower of Hanoi problem */ void solveHanota(vector &A, vector &B, vector &C) { int n = A.size(); - // Move the top n discs from A with the help of B to C + // Move the top n disks from A to C using B dfs(n, A, B, C); } /* Driver Code */ int main() { - // The tail of the list is the top of the pillar + // The tail of the list is the top of the rod vector A = {5, 4, 3, 2, 1}; vector B = {}; vector C = {}; diff --git a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp index 5477b9aea..168293121 100644 --- a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp @@ -9,25 +9,25 @@ /* Backtracking */ void backtrack(vector &choices, int state, int n, vector &res) { - // When climbing to the nth step, add 1 to the number of solutions + // When climbing to the n-th stair, add 1 to the solution count if (state == n) res[0]++; // Traverse all choices for (auto &choice : choices) { - // Pruning: do not allow climbing beyond the nth step + // Pruning: not allowed to go beyond the n-th stair if (state + choice > n) continue; - // Attempt: make a choice, update the state + // Attempt: make choice, update state backtrack(choices, state + choice, n, res); - // Retract + // Backtrack } } /* Climbing stairs: Backtracking */ int climbingStairsBacktrack(int n) { - vector choices = {1, 2}; // Can choose to climb up 1 step or 2 steps - int state = 0; // Start climbing from the 0th step - vector res = {0}; // Use res[0] to record the number of solutions + vector choices = {1, 2}; // Can choose to climb up 1 or 2 stairs + int state = 0; // Start climbing from the 0-th stair + vector res = {0}; // Use res[0] to record the solution count backtrack(choices, state, n, res); return res[0]; } @@ -37,7 +37,7 @@ int main() { int n = 9; int res = climbingStairsBacktrack(n); - cout << "There are " << res << " solutions to climb " << n << " stairs" << endl; + cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp index 454d53338..87b70fbbe 100644 --- a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp @@ -6,14 +6,14 @@ #include "../utils/common.hpp" -/* Constrained climbing stairs: Dynamic programming */ +/* Climbing stairs with constraint: Dynamic programming */ int climbingStairsConstraintDP(int n) { if (n == 1 || n == 2) { return 1; } - // Initialize dp table, used to store subproblem solutions + // Initialize dp table, used to store solutions to subproblems vector> dp(n + 1, vector(3, 0)); - // Initial state: preset the smallest subproblem solution + // Initial state: preset the solution to the smallest subproblem dp[1][1] = 1; dp[1][2] = 0; dp[2][1] = 0; @@ -31,7 +31,7 @@ int main() { int n = 9; int res = climbingStairsConstraintDP(n); - cout << "There are " << res << " solutions to climb " << n << " stairs" << endl; + cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp index 3072f956e..dd326e4cd 100644 --- a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp @@ -26,7 +26,7 @@ int main() { int n = 9; int res = climbingStairsDFS(n); - cout << "There are " << res << " solutions to climb " << n << " stairs" << endl; + cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp index a1b2ef24f..8f59f5c84 100644 --- a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp @@ -6,12 +6,12 @@ #include "../utils/common.hpp" -/* Memoized search */ +/* Memoization search */ int dfs(int i, vector &mem) { // Known dp[1] and dp[2], return them if (i == 1 || i == 2) return i; - // If there is a record for dp[i], return it + // If record dp[i] exists, return it directly if (mem[i] != -1) return mem[i]; // dp[i] = dp[i-1] + dp[i-2] @@ -21,9 +21,9 @@ int dfs(int i, vector &mem) { return count; } -/* Climbing stairs: Memoized search */ +/* Climbing stairs: Memoization search */ int climbingStairsDFSMem(int n) { - // mem[i] records the total number of solutions for climbing to the ith step, -1 means no record + // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record vector mem(n + 1, -1); return dfs(n, mem); } @@ -33,7 +33,7 @@ int main() { int n = 9; int res = climbingStairsDFSMem(n); - cout << "There are " << res << " solutions to climb " << n << " stairs" << endl; + cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp index f8974fb4c..76ca8980d 100644 --- a/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp @@ -10,9 +10,9 @@ int climbingStairsDP(int n) { if (n == 1 || n == 2) return n; - // Initialize dp table, used to store subproblem solutions + // Initialize dp table, used to store solutions to subproblems vector dp(n + 1); - // Initial state: preset the smallest subproblem solution + // Initial state: preset the solution to the smallest subproblem dp[1] = 1; dp[2] = 2; // State transition: gradually solve larger subproblems from smaller ones @@ -40,10 +40,10 @@ int main() { int n = 9; int res = climbingStairsDP(n); - cout << "There are " << res << " solutions to climb " << n << " stairs" << endl; + cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; res = climbingStairsDPComp(n); - cout << "There are " << res << " solutions to climb " << n << " stairs" << endl; + cout << "Climbing " << n << " stairs has " << res << " solutions" << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/coin_change.cpp b/en/codes/cpp/chapter_dynamic_programming/coin_change.cpp index 80c974f9b..8299c8295 100644 --- a/en/codes/cpp/chapter_dynamic_programming/coin_change.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/coin_change.cpp @@ -16,14 +16,14 @@ int coinChangeDP(vector &coins, int amt) { for (int a = 1; a <= amt; a++) { dp[0][a] = MAX; } - // State transition: the rest of the rows and columns + // State transition: rest of the rows and columns for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeding the target amount, do not choose coin i + // If exceeds target amount, don't select coin i dp[i][a] = dp[i - 1][a]; } else { - // The smaller value between not choosing and choosing coin i + // The smaller value between not selecting and selecting coin i dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); } } @@ -42,10 +42,10 @@ int coinChangeDPComp(vector &coins, int amt) { for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeding the target amount, do not choose coin i + // If exceeds target amount, don't select coin i dp[a] = dp[a]; } else { - // The smaller value between not choosing and choosing coin i + // The smaller value between not selecting and selecting coin i dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1); } } @@ -60,11 +60,11 @@ int main() { // Dynamic programming int res = coinChangeDP(coins, amt); - cout << "The minimum number of coins required to make up the target amount is " << res << endl; + cout << "Minimum number of coins needed to make target amount is " << res << endl; // Space-optimized dynamic programming res = coinChangeDPComp(coins, amt); - cout << "The minimum number of coins required to make up the target amount is " << res << endl; + cout << "Minimum number of coins needed to make target amount is " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp b/en/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp index 4a99a5e5a..0efc98159 100644 --- a/en/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp @@ -19,10 +19,10 @@ int coinChangeIIDP(vector &coins, int amt) { for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeding the target amount, do not choose coin i + // If exceeds target amount, don't select coin i dp[i][a] = dp[i - 1][a]; } else { - // The sum of the two options of not choosing and choosing coin i + // Sum of the two options: not selecting and selecting coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; } } @@ -40,10 +40,10 @@ int coinChangeIIDPComp(vector &coins, int amt) { for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeding the target amount, do not choose coin i + // If exceeds target amount, don't select coin i dp[a] = dp[a]; } else { - // The sum of the two options of not choosing and choosing coin i + // Sum of the two options: not selecting and selecting coin i dp[a] = dp[a] + dp[a - coins[i - 1]]; } } @@ -58,11 +58,11 @@ int main() { // Dynamic programming int res = coinChangeIIDP(coins, amt); - cout << "The number of coin combinations to make up the target amount is " << res << endl; + cout << "Number of coin combinations to make target amount is " << res << endl; // Space-optimized dynamic programming res = coinChangeIIDPComp(coins, amt); - cout << "The number of coin combinations to make up the target amount is " << res << endl; + cout << "Number of coin combinations to make target amount is " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/edit_distance.cpp b/en/codes/cpp/chapter_dynamic_programming/edit_distance.cpp index 08062503c..7911d8dfd 100644 --- a/en/codes/cpp/chapter_dynamic_programming/edit_distance.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/edit_distance.cpp @@ -6,50 +6,50 @@ #include "../utils/common.hpp" -/* Edit distance: Brute force search */ +/* Edit distance: Brute-force search */ int editDistanceDFS(string s, string t, int i, int j) { // If both s and t are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return the length of t + // If s is empty, return length of t if (i == 0) return j; - // If t is empty, return the length of s + // If t is empty, return length of s if (j == 0) return i; - // If the two characters are equal, skip these two characters + // If two characters are equal, skip both characters if (s[i - 1] == t[j - 1]) return editDistanceDFS(s, t, i - 1, j - 1); - // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 int insert = editDistanceDFS(s, t, i, j - 1); int del = editDistanceDFS(s, t, i - 1, j); int replace = editDistanceDFS(s, t, i - 1, j - 1); - // Return the minimum number of edits + // Return minimum edit steps return min(min(insert, del), replace) + 1; } -/* Edit distance: Memoized search */ +/* Edit distance: Memoization search */ int editDistanceDFSMem(string s, string t, vector> &mem, int i, int j) { // If both s and t are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return the length of t + // If s is empty, return length of t if (i == 0) return j; - // If t is empty, return the length of s + // If t is empty, return length of s if (j == 0) return i; - // If there is a record, return it + // If there's a record, return it directly if (mem[i][j] != -1) return mem[i][j]; - // If the two characters are equal, skip these two characters + // If two characters are equal, skip both characters if (s[i - 1] == t[j - 1]) return editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 int insert = editDistanceDFSMem(s, t, mem, i, j - 1); int del = editDistanceDFSMem(s, t, mem, i - 1, j); int replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Record and return the minimum number of edits + // Record and return minimum edit steps mem[i][j] = min(min(insert, del), replace) + 1; return mem[i][j]; } @@ -65,14 +65,14 @@ int editDistanceDP(string s, string t) { for (int j = 1; j <= m; j++) { dp[0][j] = j; } - // State transition: the rest of the rows and columns + // State transition: rest of the rows and columns for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s[i - 1] == t[j - 1]) { - // If the two characters are equal, skip these two characters + // If two characters are equal, skip both characters dp[i][j] = dp[i - 1][j - 1]; } else { - // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; } } @@ -88,22 +88,22 @@ int editDistanceDPComp(string s, string t) { for (int j = 1; j <= m; j++) { dp[j] = j; } - // State transition: the rest of the rows + // State transition: rest of the rows for (int i = 1; i <= n; i++) { // State transition: first column int leftup = dp[0]; // Temporarily store dp[i-1, j-1] dp[0] = i; - // State transition: the rest of the columns + // State transition: rest of the columns for (int j = 1; j <= m; j++) { int temp = dp[j]; if (s[i - 1] == t[j - 1]) { - // If the two characters are equal, skip these two characters + // If two characters are equal, skip both characters dp[j] = leftup; } else { - // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1; } - leftup = temp; // Update for the next round of dp[i-1, j-1] + leftup = temp; // Update for next round's dp[i-1, j-1] } } return dp[m]; @@ -115,22 +115,22 @@ int main() { string t = "pack"; int n = s.length(), m = t.length(); - // Brute force search + // Brute-force search int res = editDistanceDFS(s, t, n, m); - cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits.\n"; + cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits"; - // Memoized search + // Memoization search vector> mem(n + 1, vector(m + 1, -1)); res = editDistanceDFSMem(s, t, mem, n, m); - cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits.\n"; + cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits"; // Dynamic programming res = editDistanceDP(s, t); - cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits.\n"; + cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits"; // Space-optimized dynamic programming res = editDistanceDPComp(s, t); - cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits.\n"; + cout << "Changing " << s << " to " << t << " requires a minimum of " << res << " edits"; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/knapsack.cpp b/en/codes/cpp/chapter_dynamic_programming/knapsack.cpp index ec03fc641..3531b0323 100644 --- a/en/codes/cpp/chapter_dynamic_programming/knapsack.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/knapsack.cpp @@ -4,46 +4,46 @@ using namespace std; -/* 0-1 Knapsack: Brute force search */ +/* 0-1 knapsack: Brute-force search */ int knapsackDFS(vector &wgt, vector &val, int i, int c) { - // If all items have been chosen or the knapsack has no remaining capacity, return value 0 + // If all items have been selected or knapsack has no remaining capacity, return value 0 if (i == 0 || c == 0) { return 0; } - // If exceeding the knapsack capacity, can only choose not to put it in the knapsack + // If exceeds knapsack capacity, can only choose not to put it in if (wgt[i - 1] > c) { return knapsackDFS(wgt, val, i - 1, c); } // Calculate the maximum value of not putting in and putting in item i int no = knapsackDFS(wgt, val, i - 1, c); int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Return the greater value of the two options + // Return the larger value of the two options return max(no, yes); } -/* 0-1 Knapsack: Memoized search */ +/* 0-1 knapsack: Memoization search */ int knapsackDFSMem(vector &wgt, vector &val, vector> &mem, int i, int c) { - // If all items have been chosen or the knapsack has no remaining capacity, return value 0 + // If all items have been selected or knapsack has no remaining capacity, return value 0 if (i == 0 || c == 0) { return 0; } - // If there is a record, return it + // If there's a record, return it directly if (mem[i][c] != -1) { return mem[i][c]; } - // If exceeding the knapsack capacity, can only choose not to put it in the knapsack + // If exceeds knapsack capacity, can only choose not to put it in if (wgt[i - 1] > c) { return knapsackDFSMem(wgt, val, mem, i - 1, c); } // Calculate the maximum value of not putting in and putting in item i int no = knapsackDFSMem(wgt, val, mem, i - 1, c); int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Record and return the greater value of the two options + // Record and return the larger value of the two options mem[i][c] = max(no, yes); return mem[i][c]; } -/* 0-1 Knapsack: Dynamic programming */ +/* 0-1 knapsack: Dynamic programming */ int knapsackDP(vector &wgt, vector &val, int cap) { int n = wgt.size(); // Initialize dp table @@ -52,10 +52,10 @@ int knapsackDP(vector &wgt, vector &val, int cap) { for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeding the knapsack capacity, do not choose item i + // If exceeds knapsack capacity, don't select item i dp[i][c] = dp[i - 1][c]; } else { - // The greater value between not choosing and choosing item i + // The larger value between not selecting and selecting item i dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); } } @@ -63,7 +63,7 @@ int knapsackDP(vector &wgt, vector &val, int cap) { return dp[n][cap]; } -/* 0-1 Knapsack: Space-optimized dynamic programming */ +/* 0-1 knapsack: Space-optimized dynamic programming */ int knapsackDPComp(vector &wgt, vector &val, int cap) { int n = wgt.size(); // Initialize dp table @@ -73,7 +73,7 @@ int knapsackDPComp(vector &wgt, vector &val, int cap) { // Traverse in reverse order for (int c = cap; c >= 1; c--) { if (wgt[i - 1] <= c) { - // The greater value between not choosing and choosing item i + // The larger value between not selecting and selecting item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -88,22 +88,22 @@ int main() { int cap = 50; int n = wgt.size(); - // Brute force search + // Brute-force search int res = knapsackDFS(wgt, val, n, cap); - cout << "The maximum value within the bag capacity is " << res << endl; + cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; - // Memoized search + // Memoization search vector> mem(n + 1, vector(cap + 1, -1)); res = knapsackDFSMem(wgt, val, mem, n, cap); - cout << "The maximum value within the bag capacity is " << res << endl; + cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; // Dynamic programming res = knapsackDP(wgt, val, cap); - cout << "The maximum value within the bag capacity is " << res << endl; + cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; // Space-optimized dynamic programming res = knapsackDPComp(wgt, val, cap); - cout << "The maximum value within the bag capacity is " << res << endl; + cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp b/en/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp index 86f0a6efe..dc9a83b30 100644 --- a/en/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp @@ -6,14 +6,14 @@ #include "../utils/common.hpp" -/* Climbing stairs with minimum cost: Dynamic programming */ +/* Minimum cost climbing stairs: Dynamic programming */ int minCostClimbingStairsDP(vector &cost) { int n = cost.size() - 1; if (n == 1 || n == 2) return cost[n]; - // Initialize dp table, used to store subproblem solutions + // Initialize dp table, used to store solutions to subproblems vector dp(n + 1); - // Initial state: preset the smallest subproblem solution + // Initial state: preset the solution to the smallest subproblem dp[1] = cost[1]; dp[2] = cost[2]; // State transition: gradually solve larger subproblems from smaller ones @@ -23,7 +23,7 @@ int minCostClimbingStairsDP(vector &cost) { return dp[n]; } -/* Climbing stairs with minimum cost: Space-optimized dynamic programming */ +/* Minimum cost climbing stairs: Space-optimized dynamic programming */ int minCostClimbingStairsDPComp(vector &cost) { int n = cost.size() - 1; if (n == 1 || n == 2) @@ -40,14 +40,14 @@ int minCostClimbingStairsDPComp(vector &cost) { /* Driver Code */ int main() { vector cost = {0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1}; - cout << "Input the cost list for stairs"; + cout << "Input stair cost list is "; printVector(cost); int res = minCostClimbingStairsDP(cost); - cout << "Minimum cost to climb the stairs " << res << endl; + cout << "Minimum cost to climb stairs is " << res << endl; res = minCostClimbingStairsDPComp(cost); - cout << "Minimum cost to climb the stairs " << res << endl; + cout << "Minimum cost to climb stairs is " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp b/en/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp index c81c3a54e..b98bcccd9 100644 --- a/en/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp @@ -6,41 +6,41 @@ #include "../utils/common.hpp" -/* Minimum path sum: Brute force search */ +/* Minimum path sum: Brute-force search */ int minPathSumDFS(vector> &grid, int i, int j) { // If it's the top-left cell, terminate the search if (i == 0 && j == 0) { return grid[0][0]; } - // If the row or column index is out of bounds, return a +∞ cost + // If row or column index is out of bounds, return +∞ cost if (i < 0 || j < 0) { return INT_MAX; } - // Calculate the minimum path cost from the top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) int up = minPathSumDFS(grid, i - 1, j); int left = minPathSumDFS(grid, i, j - 1); - // Return the minimum path cost from the top-left to (i, j) + // Return the minimum path cost from top-left to (i, j) return min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX; } -/* Minimum path sum: Memoized search */ +/* Minimum path sum: Memoization search */ int minPathSumDFSMem(vector> &grid, vector> &mem, int i, int j) { // If it's the top-left cell, terminate the search if (i == 0 && j == 0) { return grid[0][0]; } - // If the row or column index is out of bounds, return a +∞ cost + // If row or column index is out of bounds, return +∞ cost if (i < 0 || j < 0) { return INT_MAX; } - // If there is a record, return it + // If there's a record, return it directly if (mem[i][j] != -1) { return mem[i][j]; } - // The minimum path cost from the left and top cells + // Minimum path cost for left and upper cells int up = minPathSumDFSMem(grid, mem, i - 1, j); int left = minPathSumDFSMem(grid, mem, i, j - 1); - // Record and return the minimum path cost from the top-left to (i, j) + // Record and return the minimum path cost from top-left to (i, j) mem[i][j] = min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX; return mem[i][j]; } @@ -59,7 +59,7 @@ int minPathSumDP(vector> &grid) { for (int i = 1; i < n; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } - // State transition: the rest of the rows and columns + // State transition: rest of the rows and columns for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; @@ -78,11 +78,11 @@ int minPathSumDPComp(vector> &grid) { for (int j = 1; j < m; j++) { dp[j] = dp[j - 1] + grid[0][j]; } - // State transition: the rest of the rows + // State transition: rest of the rows for (int i = 1; i < n; i++) { // State transition: first column dp[0] = dp[0] + grid[i][0]; - // State transition: the rest of the columns + // State transition: rest of the columns for (int j = 1; j < m; j++) { dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]; } @@ -95,22 +95,22 @@ int main() { vector> grid = {{1, 3, 1, 5}, {2, 2, 4, 2}, {5, 3, 2, 1}, {4, 3, 5, 2}}; int n = grid.size(), m = grid[0].size(); - // Brute force search + // Brute-force search int res = minPathSumDFS(grid, n - 1, m - 1); - cout << "The minimum path sum from the top left corner to the bottom right corner is " << res << endl; + cout << "Minimum path sum from top-left to bottom-right is " << res << endl; - // Memoized search + // Memoization search vector> mem(n, vector(m, -1)); res = minPathSumDFSMem(grid, mem, n - 1, m - 1); - cout << "The minimum path sum from the top left corner to the bottom right corner is " << res << endl; + cout << "Minimum path sum from top-left to bottom-right is " << res << endl; // Dynamic programming res = minPathSumDP(grid); - cout << "The minimum path sum from the top left corner to the bottom right corner is " << res << endl; + cout << "Minimum path sum from top-left to bottom-right is " << res << endl; // Space-optimized dynamic programming res = minPathSumDPComp(grid); - cout << "The minimum path sum from the top left corner to the bottom right corner is " << res << endl; + cout << "Minimum path sum from top-left to bottom-right is " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp b/en/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp index 4ed94f07a..5410185b8 100644 --- a/en/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp +++ b/en/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Complete knapsack: Dynamic programming */ +/* Unbounded knapsack: Dynamic programming */ int unboundedKnapsackDP(vector &wgt, vector &val, int cap) { int n = wgt.size(); // Initialize dp table @@ -15,10 +15,10 @@ int unboundedKnapsackDP(vector &wgt, vector &val, int cap) { for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeding the knapsack capacity, do not choose item i + // If exceeds knapsack capacity, don't select item i dp[i][c] = dp[i - 1][c]; } else { - // The greater value between not choosing and choosing item i + // The larger value between not selecting and selecting item i dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); } } @@ -26,7 +26,7 @@ int unboundedKnapsackDP(vector &wgt, vector &val, int cap) { return dp[n][cap]; } -/* Complete knapsack: Space-optimized dynamic programming */ +/* Unbounded knapsack: Space-optimized dynamic programming */ int unboundedKnapsackDPComp(vector &wgt, vector &val, int cap) { int n = wgt.size(); // Initialize dp table @@ -35,10 +35,10 @@ int unboundedKnapsackDPComp(vector &wgt, vector &val, int cap) { for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeding the knapsack capacity, do not choose item i + // If exceeds knapsack capacity, don't select item i dp[c] = dp[c]; } else { - // The greater value between not choosing and choosing item i + // The larger value between not selecting and selecting item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -54,11 +54,11 @@ int main() { // Dynamic programming int res = unboundedKnapsackDP(wgt, val, cap); - cout << "The maximum value within the bag capacity is " << res << endl; + cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; // Space-optimized dynamic programming res = unboundedKnapsackDPComp(wgt, val, cap); - cout << "The maximum value within the bag capacity is " << res << endl; + cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_graph/graph_adjacency_list.cpp b/en/codes/cpp/chapter_graph/graph_adjacency_list.cpp index 1fa9489c4..810bdb146 100644 --- a/en/codes/cpp/chapter_graph/graph_adjacency_list.cpp +++ b/en/codes/cpp/chapter_graph/graph_adjacency_list.cpp @@ -12,7 +12,7 @@ class GraphAdjList { // Adjacency list, key: vertex, value: all adjacent vertices of that vertex unordered_map> adjList; - /* Remove a specified node from vector */ + /* Remove specified node from vector */ void remove(vector &vec, Vertex *vet) { for (int i = 0; i < vec.size(); i++) { if (vec[i] == vet) { @@ -59,7 +59,7 @@ class GraphAdjList { void addVertex(Vertex *vet) { if (adjList.count(vet)) return; - // Add a new linked list to the adjacency list + // Add a new linked list in the adjacency list adjList[vet] = vector(); } @@ -67,15 +67,15 @@ class GraphAdjList { void removeVertex(Vertex *vet) { if (!adjList.count(vet)) throw invalid_argument("Vertex does not exist"); - // Remove the vertex vet's corresponding linked list from the adjacency list + // Remove the linked list corresponding to vertex vet in the adjacency list adjList.erase(vet); - // Traverse other vertices' linked lists, removing all edges containing vet + // Traverse the linked lists of other vertices and remove all edges containing vet for (auto &adj : adjList) { remove(adj.second, vet); } } - /* Print the adjacency list */ + /* Print adjacency list */ void print() { cout << "Adjacency list =" << endl; for (auto &adj : adjList) { @@ -87,4 +87,4 @@ class GraphAdjList { } }; -// See test case in graph_adjacency_list_test.cpp +// See graph_adjacency_list_test.cpp for test cases diff --git a/en/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp b/en/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp index 92d96effa..e95b724ba 100644 --- a/en/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp +++ b/en/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp @@ -8,36 +8,36 @@ /* Driver Code */ int main() { - /* Initialize undirected graph */ + /* Add edge */ vector v = valsToVets(vector{1, 3, 2, 5, 4}); vector> edges = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[3]}, {v[2], v[4]}, {v[3], v[4]}}; GraphAdjList graph(edges); - cout << "\nAfter initialization, the graph is" << endl; + cout << "\nAfter initialization, graph is" << endl; graph.print(); /* Add edge */ - // Vertices 1, 2 i.e., v[0], v[2] + // Vertices 1, 3 are v[0], v[1] graph.addEdge(v[0], v[2]); - cout << "\nAfter adding edge 1-2, the graph is" << endl; + cout << "\nAfter adding edge 1-2, graph is" << endl; graph.print(); /* Remove edge */ - // Vertices 1, 3 i.e., v[0], v[1] + // Vertex 3 is v[1] graph.removeEdge(v[0], v[1]); - cout << "\nAfter removing edge 1-3, the graph is" << endl; + cout << "\nAfter removing edge 1-3, graph is" << endl; graph.print(); /* Add vertex */ Vertex *v5 = new Vertex(6); graph.addVertex(v5); - cout << "\nAfter adding vertex 6, the graph is" << endl; + cout << "\nAfter adding vertex 6, graph is" << endl; graph.print(); /* Remove vertex */ - // Vertex 3 i.e., v[1] + // Vertex 3 is v[1] graph.removeVertex(v[1]); - cout << "\nAfter removing vertex 3, the graph is" << endl; + cout << "\nAfter removing vertex 3, graph is" << endl; graph.print(); // Free memory diff --git a/en/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp b/en/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp index 245ac42f2..f21c60a42 100644 --- a/en/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp +++ b/en/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp @@ -8,8 +8,8 @@ /* Undirected graph class based on adjacency matrix */ class GraphAdjMat { - vector vertices; // Vertex list, elements represent "vertex value", index represents "vertex index" - vector> adjMat; // Adjacency matrix, row and column indices correspond to "vertex index" + vector vertices; // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + vector> adjMat; // Adjacency matrix, where the row and column indices correspond to the "vertex index" public: /* Constructor */ @@ -19,7 +19,7 @@ class GraphAdjMat { addVertex(val); } // Add edge - // Edges elements represent vertex indices + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices for (const vector &edge : edges) { addEdge(edge[0], edge[1]); } @@ -33,7 +33,7 @@ class GraphAdjMat { /* Add vertex */ void addVertex(int val) { int n = size(); - // Add new vertex value to the vertex list + // Add the value of the new vertex to the vertex list vertices.push_back(val); // Add a row to the adjacency matrix adjMat.emplace_back(vector(n, 0)); @@ -48,30 +48,30 @@ class GraphAdjMat { if (index >= size()) { throw out_of_range("Vertex does not exist"); } - // Remove vertex at `index` from the vertex list + // Remove the vertex at index from the vertex list vertices.erase(vertices.begin() + index); - // Remove the row at `index` from the adjacency matrix + // Remove the row at index from the adjacency matrix adjMat.erase(adjMat.begin() + index); - // Remove the column at `index` from the adjacency matrix + // Remove the column at index from the adjacency matrix for (vector &row : adjMat) { row.erase(row.begin() + index); } } /* Add edge */ - // Parameters i, j correspond to vertices element indices + // Parameters i, j correspond to the vertices element indices void addEdge(int i, int j) { // Handle index out of bounds and equality if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { throw out_of_range("Vertex does not exist"); } - // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., satisfies (i, j) == (j, i) + // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) adjMat[i][j] = 1; adjMat[j][i] = 1; } /* Remove edge */ - // Parameters i, j correspond to vertices element indices + // Parameters i, j correspond to the vertices element indices void removeEdge(int i, int j) { // Handle index out of bounds and equality if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { @@ -92,35 +92,35 @@ class GraphAdjMat { /* Driver Code */ int main() { - /* Initialize undirected graph */ - // Edges elements represent vertex indices + /* Add edge */ + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices vector vertices = {1, 3, 2, 5, 4}; vector> edges = {{0, 1}, {0, 3}, {1, 2}, {2, 3}, {2, 4}, {3, 4}}; GraphAdjMat graph(vertices, edges); - cout << "\nAfter initialization, the graph is" << endl; + cout << "\nAfter initialization, graph is" << endl; graph.print(); /* Add edge */ - // Indices of vertices 1, 2 are 0, 2 respectively + // Add vertex graph.addEdge(0, 2); - cout << "\nAfter adding edge 1-2, the graph is" << endl; + cout << "\nAfter adding edge 1-2, graph is" << endl; graph.print(); /* Remove edge */ - // Indices of vertices 1, 3 are 0, 1 respectively + // Vertices 1, 3 have indices 0, 1 respectively graph.removeEdge(0, 1); - cout << "\nAfter removing edge 1-3, the graph is" << endl; + cout << "\nAfter removing edge 1-3, graph is" << endl; graph.print(); /* Add vertex */ graph.addVertex(6); - cout << "\nAfter adding vertex 6, the graph is" << endl; + cout << "\nAfter adding vertex 6, graph is" << endl; graph.print(); /* Remove vertex */ - // Index of vertex 3 is 1 + // Vertex 3 has index 1 graph.removeVertex(1); - cout << "\nAfter removing vertex 3, the graph is" << endl; + cout << "\nAfter removing vertex 3, graph is" << endl; graph.print(); return 0; diff --git a/en/codes/cpp/chapter_graph/graph_bfs.cpp b/en/codes/cpp/chapter_graph/graph_bfs.cpp index 93ab145ba..573500ea4 100644 --- a/en/codes/cpp/chapter_graph/graph_bfs.cpp +++ b/en/codes/cpp/chapter_graph/graph_bfs.cpp @@ -8,11 +8,11 @@ #include "./graph_adjacency_list.cpp" /* Breadth-first traversal */ -// Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex vector graphBFS(GraphAdjList &graph, Vertex *startVet) { // Vertex traversal sequence vector res; - // Hash set, used to record visited vertices + // Hash set for recording vertices that have been visited unordered_set visited = {startVet}; // Queue used to implement BFS queue que; @@ -20,29 +20,29 @@ vector graphBFS(GraphAdjList &graph, Vertex *startVet) { // Starting from vertex vet, loop until all vertices are visited while (!que.empty()) { Vertex *vet = que.front(); - que.pop(); // Dequeue the vertex at the head of the queue + que.pop(); // Dequeue the front vertex res.push_back(vet); // Record visited vertex - // Traverse all adjacent vertices of that vertex + // Traverse all adjacent vertices of this vertex for (auto adjVet : graph.adjList[vet]) { if (visited.count(adjVet)) - continue; // Skip already visited vertices + continue; // Skip vertices that have been visited que.push(adjVet); // Only enqueue unvisited vertices - visited.emplace(adjVet); // Mark the vertex as visited + visited.emplace(adjVet); // Mark this vertex as visited } } - // Return the vertex traversal sequence + // Return vertex traversal sequence return res; } /* Driver Code */ int main() { - /* Initialize undirected graph */ + /* Add edge */ vector v = valsToVets({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); vector> edges = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[1], v[4]}, {v[2], v[5]}, {v[3], v[4]}, {v[3], v[6]}, {v[4], v[5]}, {v[4], v[7]}, {v[5], v[8]}, {v[6], v[7]}, {v[7], v[8]}}; GraphAdjList graph(edges); - cout << "\nAfter initialization, the graph is\n"; + cout << "\nAfter initialization, graph is\\n"; graph.print(); /* Breadth-first traversal */ diff --git a/en/codes/cpp/chapter_graph/graph_dfs.cpp b/en/codes/cpp/chapter_graph/graph_dfs.cpp index 79e1e4356..050e8b0a8 100644 --- a/en/codes/cpp/chapter_graph/graph_dfs.cpp +++ b/en/codes/cpp/chapter_graph/graph_dfs.cpp @@ -10,22 +10,22 @@ /* Depth-first traversal helper function */ void dfs(GraphAdjList &graph, unordered_set &visited, vector &res, Vertex *vet) { res.push_back(vet); // Record visited vertex - visited.emplace(vet); // Mark the vertex as visited - // Traverse all adjacent vertices of that vertex + visited.emplace(vet); // Mark this vertex as visited + // Traverse all adjacent vertices of this vertex for (Vertex *adjVet : graph.adjList[vet]) { if (visited.count(adjVet)) - continue; // Skip already visited vertices + continue; // Skip vertices that have been visited // Recursively visit adjacent vertices dfs(graph, visited, res, adjVet); } } /* Depth-first traversal */ -// Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex vector graphDFS(GraphAdjList &graph, Vertex *startVet) { // Vertex traversal sequence vector res; - // Hash set, used to record visited vertices + // Hash set for recording vertices that have been visited unordered_set visited; dfs(graph, visited, res, startVet); return res; @@ -33,12 +33,12 @@ vector graphDFS(GraphAdjList &graph, Vertex *startVet) { /* Driver Code */ int main() { - /* Initialize undirected graph */ + /* Add edge */ vector v = valsToVets(vector{0, 1, 2, 3, 4, 5, 6}); vector> edges = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[5]}, {v[4], v[5]}, {v[5], v[6]}}; GraphAdjList graph(edges); - cout << "\nAfter initialization, the graph is" << endl; + cout << "\nAfter initialization, graph is" << endl; graph.print(); /* Depth-first traversal */ diff --git a/en/codes/cpp/chapter_greedy/coin_change_greedy.cpp b/en/codes/cpp/chapter_greedy/coin_change_greedy.cpp index c7d1135b9..5e9a9328c 100644 --- a/en/codes/cpp/chapter_greedy/coin_change_greedy.cpp +++ b/en/codes/cpp/chapter_greedy/coin_change_greedy.cpp @@ -6,14 +6,14 @@ #include "../utils/common.hpp" -/* Coin change: Greedy */ +/* Coin change: Greedy algorithm */ int coinChangeGreedy(vector &coins, int amt) { - // Assume coins list is ordered + // Assume coins list is sorted int i = coins.size() - 1; int count = 0; - // Loop for greedy selection until no remaining amount + // Loop to make greedy choices until no remaining amount while (amt > 0) { - // Find the smallest coin close to and less than the remaining amount + // Find the coin that is less than and closest to the remaining amount while (i > 0 && coins[i] > amt) { i--; } @@ -27,34 +27,34 @@ int coinChangeGreedy(vector &coins, int amt) { /* Driver Code */ int main() { - // Greedy: can ensure finding a global optimal solution + // Greedy algorithm: Can guarantee finding the global optimal solution vector coins = {1, 5, 10, 20, 50, 100}; int amt = 186; int res = coinChangeGreedy(coins, amt); cout << "\ncoins = "; printVector(coins); cout << "amt = " << amt << endl; - cout << "The minimum number of coins required to make up " << amt << " is " << res << endl; + cout << "Minimum number of coins needed to make " << amt << " is " << res << endl; - // Greedy: cannot ensure finding a global optimal solution + // Greedy algorithm: Cannot guarantee finding the global optimal solution coins = {1, 20, 50}; amt = 60; res = coinChangeGreedy(coins, amt); cout << "\ncoins = "; printVector(coins); cout << "amt = " << amt << endl; - cout << "The minimum number of coins required to make up " << amt << " is " << res << endl; - cout << "In reality, the minimum number needed is 3, i.e., 20 + 20 + 20" << endl; + cout << "Minimum number of coins needed to make " << amt << " is " << res << endl; + cout << "Actually the minimum number needed is 3, i.e., 20 + 20 + 20" << endl; - // Greedy: cannot ensure finding a global optimal solution + // Greedy algorithm: Cannot guarantee finding the global optimal solution coins = {1, 49, 50}; amt = 98; res = coinChangeGreedy(coins, amt); cout << "\ncoins = "; printVector(coins); cout << "amt = " << amt << endl; - cout << "The minimum number of coins required to make up " << amt << " is " << res << endl; - cout << "In reality, the minimum number needed is 2, i.e., 49 + 49" << endl; + cout << "Minimum number of coins needed to make " << amt << " is " << res << endl; + cout << "Actually the minimum number needed is 2, i.e., 49 + 49" << endl; return 0; } diff --git a/en/codes/cpp/chapter_greedy/fractional_knapsack.cpp b/en/codes/cpp/chapter_greedy/fractional_knapsack.cpp index 1781e865c..fedb8bddd 100644 --- a/en/codes/cpp/chapter_greedy/fractional_knapsack.cpp +++ b/en/codes/cpp/chapter_greedy/fractional_knapsack.cpp @@ -16,9 +16,9 @@ class Item { } }; -/* Fractional knapsack: Greedy */ +/* Fractional knapsack: Greedy algorithm */ double fractionalKnapsack(vector &wgt, vector &val, int cap) { - // Create an item list, containing two properties: weight, value + // Create item list with two attributes: weight, value vector items; for (int i = 0; i < wgt.size(); i++) { items.push_back(Item(wgt[i], val[i])); @@ -29,13 +29,13 @@ double fractionalKnapsack(vector &wgt, vector &val, int cap) { double res = 0; for (auto &item : items) { if (item.w <= cap) { - // If the remaining capacity is sufficient, put the entire item into the knapsack + // If remaining capacity is sufficient, put the entire current item into the knapsack res += item.v; cap -= item.w; } else { - // If the remaining capacity is insufficient, put part of the item into the knapsack + // If remaining capacity is insufficient, put part of the current item into the knapsack res += (double)item.v / item.w * cap; - // No remaining capacity left, thus break the loop + // No remaining capacity, so break out of the loop break; } } @@ -50,7 +50,7 @@ int main() { // Greedy algorithm double res = fractionalKnapsack(wgt, val, cap); - cout << "The maximum value within the bag capacity is " << res << endl; + cout << "Maximum item value not exceeding knapsack capacity is " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_greedy/max_capacity.cpp b/en/codes/cpp/chapter_greedy/max_capacity.cpp index d573d1e5c..4f112c001 100644 --- a/en/codes/cpp/chapter_greedy/max_capacity.cpp +++ b/en/codes/cpp/chapter_greedy/max_capacity.cpp @@ -6,15 +6,15 @@ #include "../utils/common.hpp" -/* Maximum capacity: Greedy */ +/* Max capacity: Greedy algorithm */ int maxCapacity(vector &ht) { - // Initialize i, j, making them split the array at both ends + // Initialize i, j to be at both ends of the array int i = 0, j = ht.size() - 1; - // Initial maximum capacity is 0 + // Initial max capacity is 0 int res = 0; // Loop for greedy selection until the two boards meet while (i < j) { - // Update maximum capacity + // Update max capacity int cap = min(ht[i], ht[j]) * (j - i); res = max(res, cap); // Move the shorter board inward @@ -33,7 +33,7 @@ int main() { // Greedy algorithm int res = maxCapacity(ht); - cout << "The maximum capacity is " << res << endl; + cout << "Maximum capacity is " << res << endl; return 0; } diff --git a/en/codes/cpp/chapter_greedy/max_product_cutting.cpp b/en/codes/cpp/chapter_greedy/max_product_cutting.cpp index f81ce63cd..dfe816dbf 100644 --- a/en/codes/cpp/chapter_greedy/max_product_cutting.cpp +++ b/en/codes/cpp/chapter_greedy/max_product_cutting.cpp @@ -6,17 +6,17 @@ #include "../utils/common.hpp" -/* Maximum product of cutting: Greedy */ +/* Max product cutting: Greedy algorithm */ int maxProductCutting(int n) { // When n <= 3, must cut out a 1 if (n <= 3) { return 1 * (n - 1); } - // Greedy cut out 3s, a is the number of 3s, b is the remainder + // Greedily cut out 3, a is the number of 3s, b is the remainder int a = n / 3; int b = n % 3; if (b == 1) { - // When the remainder is 1, convert a pair of 1 * 3 into 2 * 2 + // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 return (int)pow(3, a - 1) * 2 * 2; } if (b == 2) { diff --git a/en/codes/cpp/chapter_hashing/array_hash_map.cpp b/en/codes/cpp/chapter_hashing/array_hash_map.cpp index bb4955303..7d91ae332 100644 --- a/en/codes/cpp/chapter_hashing/array_hash_map.cpp +++ b/en/codes/cpp/chapter_hashing/array_hash_map.cpp @@ -24,7 +24,7 @@ class ArrayHashMap { public: ArrayHashMap() { - // Initialize an array, containing 100 buckets + // Initialize array with 100 buckets buckets = vector(100); } @@ -107,4 +107,4 @@ class ArrayHashMap { } }; -// See test case in array_hash_map_test.cpp +// See array_hash_map_test.cpp for test cases diff --git a/en/codes/cpp/chapter_hashing/array_hash_map_test.cpp b/en/codes/cpp/chapter_hashing/array_hash_map_test.cpp index 6ad35ec82..0afe1d515 100644 --- a/en/codes/cpp/chapter_hashing/array_hash_map_test.cpp +++ b/en/codes/cpp/chapter_hashing/array_hash_map_test.cpp @@ -13,23 +13,23 @@ int main() { /* Add operation */ // Add key-value pair (key, value) to the hash table - map.put(12836, "Ha"); - map.put(15937, "Luo"); - map.put(16750, "Suan"); - map.put(13276, "Fa"); - map.put(10583, "Ya"); - cout << "\nAfter adding, the hash table is\nKey -> Value" << endl; + map.put(12836, "Xiao Ha"); + map.put(15937, "Xiao Luo"); + map.put(16750, "Xiao Suan"); + map.put(13276, "Xiao Fa"); + map.put(10583, "Xiao Ya"); + cout << "\nAfter adding is complete, hash table is\nKey -> Value" << endl; map.print(); /* Query operation */ - // Enter key to the hash table, get value + // Input key into hash table to get value string name = map.get(15937); - cout << "\nEnter student ID 15937, found name " << name << endl; + cout << "\nInput student ID 15937, query name " << name << endl; /* Remove operation */ - // Remove key-value pair (key, value) from the hash table + // Remove key-value pair (key, value) from hash table map.remove(10583); - cout << "\nAfter removing 10583, the hash table is\nKey -> Value" << endl; + cout << "\nAfter removing 10583, hash table is\nKey -> Value" << endl; map.print(); /* Traverse hash table */ @@ -38,12 +38,12 @@ int main() { cout << kv->key << " -> " << kv->val << endl; } - cout << "\nIndividually traverse keys Key" << endl; + cout << "\nTraverse keys only Key" << endl; for (auto key : map.keySet()) { cout << key << endl; } - cout << "\nIndividually traverse values Value" << endl; + cout << "\nTraverse values only Value" << endl; for (auto val : map.valueSet()) { cout << val << endl; } diff --git a/en/codes/cpp/chapter_hashing/built_in_hash.cpp b/en/codes/cpp/chapter_hashing/built_in_hash.cpp index 8a3d3c51d..0f09b1a44 100644 --- a/en/codes/cpp/chapter_hashing/built_in_hash.cpp +++ b/en/codes/cpp/chapter_hashing/built_in_hash.cpp @@ -10,20 +10,20 @@ int main() { int num = 3; size_t hashNum = hash()(num); - cout << "The hash value of integer " << num << " is " << hashNum << "\n"; + cout << "Hash value of integer " << num << " is " << hashNum << "\n"; bool bol = true; size_t hashBol = hash()(bol); - cout << "The hash value of boolean " << bol << " is " << hashBol << "\n"; + cout << "Hash value of boolean " << bol << " is " << hashBol << "\n"; double dec = 3.14159; size_t hashDec = hash()(dec); - cout << "The hash value of decimal " << dec << " is " << hashDec << "\n"; + cout << "Hash value of decimal " << dec << " is " << hashDec << "\n"; - string str = "Hello algorithm"; + string str = "Hello Algo"; size_t hashStr = hash()(str); - cout << "The hash value of string " << str << " is " << hashStr << "\n"; + cout << "Hash value of string " << str << " is " << hashStr << "\n"; - // In C++, the built-in std:hash() only provides hash values for basic data types - // Hash value calculation for arrays and objects must be implemented manually + // In C++, built-in std::hash() only provides hash calculation for basic data types + // Hash calculation for arrays and objects needs to be implemented manually } diff --git a/en/codes/cpp/chapter_hashing/hash_map.cpp b/en/codes/cpp/chapter_hashing/hash_map.cpp index 53c149be8..e3a8accea 100644 --- a/en/codes/cpp/chapter_hashing/hash_map.cpp +++ b/en/codes/cpp/chapter_hashing/hash_map.cpp @@ -13,23 +13,23 @@ int main() { /* Add operation */ // Add key-value pair (key, value) to the hash table - map[12836] = "Ha"; - map[15937] = "Luo"; - map[16750] = "Suan"; - map[13276] = "Fa"; - map[10583] = "Ya"; - cout << "\nAfter adding, the hash table is\nKey -> Value" << endl; + map[12836] = "Xiao Ha"; + map[15937] = "Xiao Luo"; + map[16750] = "Xiao Suan"; + map[13276] = "Xiao Fa"; + map[10583] = "Xiao Ya"; + cout << "\nAfter adding is complete, hash table is\nKey -> Value" << endl; printHashMap(map); /* Query operation */ - // Enter key to the hash table, get value + // Input key into hash table to get value string name = map[15937]; - cout << "\nEnter student ID 15937, found name " << name << endl; + cout << "\nInput student ID 15937, query name " << name << endl; /* Remove operation */ - // Remove key-value pair (key, value) from the hash table + // Remove key-value pair (key, value) from hash table map.erase(10583); - cout << "\nAfter removing 10583, the hash table is\nKey -> Value" << endl; + cout << "\nAfter removing 10583, hash table is\nKey -> Value" << endl; printHashMap(map); /* Traverse hash table */ @@ -37,7 +37,7 @@ int main() { for (auto kv : map) { cout << kv.first << " -> " << kv.second << endl; } - cout << "\nIterate through Key->Value using an iterator" << endl; + cout << "\nTraverse Key->Value using iterator" << endl; for (auto iter = map.begin(); iter != map.end(); iter++) { cout << iter->first << "->" << iter->second << endl; } diff --git a/en/codes/cpp/chapter_hashing/hash_map_chaining.cpp b/en/codes/cpp/chapter_hashing/hash_map_chaining.cpp index 7a903958a..19deb4290 100644 --- a/en/codes/cpp/chapter_hashing/hash_map_chaining.cpp +++ b/en/codes/cpp/chapter_hashing/hash_map_chaining.cpp @@ -6,7 +6,7 @@ #include "./array_hash_map.cpp" -/* Chained address hash table */ +/* Hash table with separate chaining */ class HashMapChaining { private: int size; // Number of key-value pairs @@ -44,31 +44,31 @@ class HashMapChaining { /* Query operation */ string get(int key) { int index = hashFunc(key); - // Traverse the bucket, if the key is found, return the corresponding val + // Traverse bucket, if key is found, return corresponding val for (Pair *pair : buckets[index]) { if (pair->key == key) { return pair->val; } } - // If key not found, return an empty string + // Return empty string if key not found return ""; } /* Add operation */ void put(int key, string val) { - // When the load factor exceeds the threshold, perform expansion + // When load factor exceeds threshold, perform expansion if (loadFactor() > loadThres) { extend(); } int index = hashFunc(key); - // Traverse the bucket, if the specified key is encountered, update the corresponding val and return + // Traverse bucket, if specified key is encountered, update corresponding val and return for (Pair *pair : buckets[index]) { if (pair->key == key) { pair->val = val; return; } } - // If the key is not found, add the key-value pair to the end + // If key does not exist, append key-value pair to the end buckets[index].push_back(new Pair(key, val)); size++; } @@ -77,11 +77,11 @@ class HashMapChaining { void remove(int key) { int index = hashFunc(key); auto &bucket = buckets[index]; - // Traverse the bucket, remove the key-value pair from it + // Traverse bucket and remove key-value pair from it for (int i = 0; i < bucket.size(); i++) { if (bucket[i]->key == key) { Pair *tmp = bucket[i]; - bucket.erase(bucket.begin() + i); // Remove key-value pair + bucket.erase(bucket.begin() + i); // Remove key-value pair from it delete tmp; // Free memory size--; return; @@ -89,16 +89,16 @@ class HashMapChaining { } } - /* Extend hash table */ + /* Expand hash table */ void extend() { // Temporarily store the original hash table vector> bucketsTmp = buckets; - // Initialize the extended new hash table + // Initialize expanded new hash table capacity *= extendRatio; buckets.clear(); buckets.resize(capacity); size = 0; - // Move key-value pairs from the original hash table to the new hash table + // Move key-value pairs from original hash table to new hash table for (auto &bucket : bucketsTmp) { for (Pair *pair : bucket) { put(pair->key, pair->val); @@ -127,23 +127,23 @@ int main() { /* Add operation */ // Add key-value pair (key, value) to the hash table - map.put(12836, "Ha"); - map.put(15937, "Luo"); - map.put(16750, "Suan"); - map.put(13276, "Fa"); - map.put(10583, "Ya"); - cout << "\nAfter adding, the hash table is\nKey -> Value" << endl; + map.put(12836, "Xiao Ha"); + map.put(15937, "Xiao Luo"); + map.put(16750, "Xiao Suan"); + map.put(13276, "Xiao Fa"); + map.put(10583, "Xiao Ya"); + cout << "\nAfter adding is complete, hash table is\nKey -> Value" << endl; map.print(); /* Query operation */ - // Enter key to the hash table, get value + // Input key into hash table to get value string name = map.get(13276); - cout << "\nEnter student ID 13276, found name " << name << endl; + cout << "\nInput student ID 13276, query name " << name << endl; /* Remove operation */ - // Remove key-value pair (key, value) from the hash table + // Remove key-value pair (key, value) from hash table map.remove(12836); - cout << "\nAfter removing 12836, the hash table is\nKey -> Value" << endl; + cout << "\nAfter removing 12836, hash table is\nKey -> Value" << endl; map.print(); return 0; diff --git a/en/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp b/en/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp index a66233ad9..cd0fbfc47 100644 --- a/en/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp +++ b/en/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp @@ -6,7 +6,7 @@ #include "./array_hash_map.cpp" -/* Open addressing hash table */ +/* Hash table with open addressing */ class HashMapOpenAddressing { private: int size; // Number of key-value pairs @@ -14,7 +14,7 @@ class HashMapOpenAddressing { const double loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion const int extendRatio = 2; // Expansion multiplier vector buckets; // Bucket array - Pair *TOMBSTONE = new Pair(-1, "-1"); // Removal mark + Pair *TOMBSTONE = new Pair(-1, "-1"); // Removal marker public: /* Constructor */ @@ -41,15 +41,15 @@ class HashMapOpenAddressing { return (double)size / capacity; } - /* Search for the bucket index corresponding to key */ + /* Search for bucket index corresponding to key */ int findBucket(int key) { int index = hashFunc(key); int firstTombstone = -1; // Linear probing, break when encountering an empty bucket while (buckets[index] != nullptr) { - // If the key is encountered, return the corresponding bucket index + // If key is encountered, return the corresponding bucket index if (buckets[index]->key == key) { - // If a removal mark was encountered earlier, move the key-value pair to that index + // If a removal marker was encountered before, move the key-value pair to that index if (firstTombstone != -1) { buckets[firstTombstone] = buckets[index]; buckets[index] = TOMBSTONE; @@ -57,52 +57,52 @@ class HashMapOpenAddressing { } return index; // Return bucket index } - // Record the first encountered removal mark + // Record the first removal marker encountered if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { firstTombstone = index; } - // Calculate the bucket index, return to the head if exceeding the tail + // Calculate bucket index, wrap around to the head if past the tail index = (index + 1) % capacity; } - // If the key does not exist, return the index of the insertion point + // If key does not exist, return the index for insertion return firstTombstone == -1 ? index : firstTombstone; } /* Query operation */ string get(int key) { - // Search for the bucket index corresponding to key + // Search for bucket index corresponding to key int index = findBucket(key); - // If the key-value pair is found, return the corresponding val + // If key-value pair is found, return corresponding val if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { return buckets[index]->val; } - // If key-value pair does not exist, return an empty string + // Return empty string if key-value pair does not exist return ""; } /* Add operation */ void put(int key, string val) { - // When the load factor exceeds the threshold, perform expansion + // When load factor exceeds threshold, perform expansion if (loadFactor() > loadThres) { extend(); } - // Search for the bucket index corresponding to key + // Search for bucket index corresponding to key int index = findBucket(key); - // If the key-value pair is found, overwrite val and return + // If key-value pair is found, overwrite val and return if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { buckets[index]->val = val; return; } - // If the key-value pair does not exist, add the key-value pair + // If key-value pair does not exist, add the key-value pair buckets[index] = new Pair(key, val); size++; } /* Remove operation */ void remove(int key) { - // Search for the bucket index corresponding to key + // Search for bucket index corresponding to key int index = findBucket(key); - // If the key-value pair is found, cover it with a removal mark + // If key-value pair is found, overwrite it with removal marker if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { delete buckets[index]; buckets[index] = TOMBSTONE; @@ -110,15 +110,15 @@ class HashMapOpenAddressing { } } - /* Extend hash table */ + /* Expand hash table */ void extend() { // Temporarily store the original hash table vector bucketsTmp = buckets; - // Initialize the extended new hash table + // Initialize expanded new hash table capacity *= extendRatio; buckets = vector(capacity, nullptr); size = 0; - // Move key-value pairs from the original hash table to the new hash table + // Move key-value pairs from original hash table to new hash table for (Pair *pair : bucketsTmp) { if (pair != nullptr && pair != TOMBSTONE) { put(pair->key, pair->val); @@ -148,23 +148,23 @@ int main() { // Add operation // Add key-value pair (key, val) to the hash table - hashmap.put(12836, "Ha"); - hashmap.put(15937, "Luo"); - hashmap.put(16750, "Suan"); - hashmap.put(13276, "Fa"); - hashmap.put(10583, "Ya"); - cout << "\nAfter adding, the hash table is\nKey -> Value" << endl; + hashmap.put(12836, "Xiao Ha"); + hashmap.put(15937, "Xiao Luo"); + hashmap.put(16750, "Xiao Suan"); + hashmap.put(13276, "Xiao Fa"); + hashmap.put(10583, "Xiao Ya"); + cout << "\nAfter adding is complete, hash table is\nKey -> Value" << endl; hashmap.print(); // Query operation - // Enter key to the hash table, get value val + // Input key into hash table to get value val string name = hashmap.get(13276); - cout << "\nEnter student ID 13276, found name " << name << endl; + cout << "\nInput student ID 13276, query name " << name << endl; // Remove operation - // Remove key-value pair (key, val) from the hash table + // Remove key-value pair (key, val) from hash table hashmap.remove(16750); - cout << "\nAfter removing 16750, the hash table is\nKey -> Value" << endl; + cout << "\nAfter removing 16750, hash table is\nKey -> Value" << endl; hashmap.print(); return 0; diff --git a/en/codes/cpp/chapter_hashing/simple_hash.cpp b/en/codes/cpp/chapter_hashing/simple_hash.cpp index 1cfe03fab..f52c70317 100644 --- a/en/codes/cpp/chapter_hashing/simple_hash.cpp +++ b/en/codes/cpp/chapter_hashing/simple_hash.cpp @@ -48,7 +48,7 @@ int rotHash(string key) { /* Driver Code */ int main() { - string key = "Hello algorithm"; + string key = "Hello Algo"; int hash = addHash(key); cout << "Additive hash value is " << hash << endl; diff --git a/en/codes/cpp/chapter_heap/heap.cpp b/en/codes/cpp/chapter_heap/heap.cpp index a87d39e4a..c33029882 100644 --- a/en/codes/cpp/chapter_heap/heap.cpp +++ b/en/codes/cpp/chapter_heap/heap.cpp @@ -7,40 +7,40 @@ #include "../utils/common.hpp" void testPush(priority_queue &heap, int val) { - heap.push(val); // Push the element into heap - cout << "\nAfter element " << val << " is added to the heap" << endl; + heap.push(val); // Element enters heap + cout << "\nAfter element " << val << " pushes to heap" << endl; printHeap(heap); } void testPop(priority_queue &heap) { int val = heap.top(); heap.pop(); - cout << "\nAfter the top element " << val << " is removed from the heap" << endl; + cout << "\nAfter heap top element " << val << " pops from heap" << endl; printHeap(heap); } /* Driver Code */ int main() { - /* Initialize the heap */ - // Initialize min-heap + /* Initialize heap */ + // Python's heapq module implements min heap by default // priority_queue, greater> minHeap; - // Initialize max-heap + // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap priority_queue, less> maxHeap; - cout << "\nThe following test case is for max-heap" << endl; + cout << "\nThe following test cases are for max heap" << endl; - /* Push the element into heap */ + /* Element enters heap */ testPush(maxHeap, 1); testPush(maxHeap, 3); testPush(maxHeap, 2); testPush(maxHeap, 5); testPush(maxHeap, 4); - /* Access heap top element */ + /* Check if heap is empty */ int peek = maxHeap.top(); - cout << "\nTop element of the heap is " << peek << endl; + cout << "\nHeap top element is " << peek << endl; - /* Pop the element at the heap top */ + /* Time complexity is O(n), not O(nlogn) */ testPop(maxHeap); testPop(maxHeap); testPop(maxHeap); @@ -49,17 +49,17 @@ int main() { /* Get heap size */ int size = maxHeap.size(); - cout << "\nNumber of elements in the heap is " << size << endl; + cout << "\nHeap size is " << size << endl; - /* Determine if heap is empty */ + /* Check if heap is empty */ bool isEmpty = maxHeap.empty(); - cout << "\nIs the heap empty " << isEmpty << endl; + cout << "\nIs heap empty " << isEmpty << endl; - /* Enter list and build heap */ + /* Input list and build heap */ // Time complexity is O(n), not O(nlogn) vector input{1, 3, 2, 5, 4}; priority_queue, greater> minHeap(input.begin(), input.end()); - cout << "After inputting the list and building a min-heap" << endl; + cout << "After input list and building min heap" << endl; printHeap(minHeap); return 0; diff --git a/en/codes/cpp/chapter_heap/my_heap.cpp b/en/codes/cpp/chapter_heap/my_heap.cpp index 92ee6e1ca..8a0649fb2 100644 --- a/en/codes/cpp/chapter_heap/my_heap.cpp +++ b/en/codes/cpp/chapter_heap/my_heap.cpp @@ -6,10 +6,10 @@ #include "../utils/common.hpp" -/* Max-heap */ +/* Max heap */ class MaxHeap { private: - // Using a dynamic array to avoid the need for resizing + // Use dynamic array to avoid expansion issues vector maxHeap; /* Get index of left child node */ @@ -24,34 +24,34 @@ class MaxHeap { /* Get index of parent node */ int parent(int i) { - return (i - 1) / 2; // Integer division down + return (i - 1) / 2; // Floor division } - /* Start heapifying node i, from bottom to top */ + /* Starting from node i, heapify from bottom to top */ void siftUp(int i) { while (true) { // Get parent node of node i int p = parent(i); - // When "crossing the root node" or "node does not need repair", end heapification + // When "crossing root node" or "node needs no repair", end heapify if (p < 0 || maxHeap[i] <= maxHeap[p]) break; // Swap two nodes swap(maxHeap[i], maxHeap[p]); - // Loop upwards heapification + // Loop upward heapify i = p; } } - /* Start heapifying node i, from top to bottom */ + /* Starting from node i, heapify from top to bottom */ void siftDown(int i) { while (true) { - // Determine the largest node among i, l, r, noted as ma + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break int l = left(i), r = right(i), ma = i; if (l < size() && maxHeap[l] > maxHeap[ma]) ma = l; if (r < size() && maxHeap[r] > maxHeap[ma]) ma = r; - // If node i is the largest or indices l, r are out of bounds, no further heapification needed, break + // Swap two nodes if (ma == i) break; swap(maxHeap[i], maxHeap[ma]); @@ -63,9 +63,9 @@ class MaxHeap { public: /* Constructor, build heap based on input list */ MaxHeap(vector nums) { - // Add all list elements into the heap + // Add list elements to heap as is maxHeap = nums; - // Heapify all nodes except leaves + // Heapify all nodes except leaf nodes for (int i = parent(size() - 1); i >= 0; i--) { siftDown(i); } @@ -76,17 +76,17 @@ class MaxHeap { return maxHeap.size(); } - /* Determine if heap is empty */ + /* Check if heap is empty */ bool isEmpty() { return size() == 0; } - /* Access heap top element */ + /* Access top element */ int peek() { return maxHeap[0]; } - /* Push the element into heap */ + /* Element enters heap */ void push(int val) { // Add node maxHeap.push_back(val); @@ -96,23 +96,23 @@ class MaxHeap { /* Element exits heap */ void pop() { - // Empty handling + // Handle empty case if (isEmpty()) { throw out_of_range("Heap is empty"); } - // Swap the root node with the rightmost leaf node (swap the first element with the last element) + // Delete node swap(maxHeap[0], maxHeap[size() - 1]); // Remove node maxHeap.pop_back(); - // Heapify from top to bottom + // Return top element siftDown(0); } - /* Print heap (binary tree)*/ + /* Driver Code*/ void print() { - cout << "Array representation of the heap:"; + cout << "Heap array representation:"; printVector(maxHeap); - cout << "Tree representation of the heap:" << endl; + cout << "Heap tree representation:" << endl; TreeNode *root = vectorToTree(maxHeap); printTree(root); freeMemoryTree(root); @@ -121,35 +121,35 @@ class MaxHeap { /* Driver Code */ int main() { - /* Initialize max-heap */ + /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ vector vec{9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2}; MaxHeap maxHeap(vec); - cout << "\nEnter list and build heap" << endl; + cout << "\nAfter inputting list and building heap" << endl; maxHeap.print(); - /* Access heap top element */ + /* Check if heap is empty */ int peek = maxHeap.peek(); - cout << "\nTop element of the heap is " << peek << endl; + cout << "\nHeap top element is " << peek << endl; - /* Push the element into heap */ + /* Element enters heap */ int val = 7; maxHeap.push(val); - cout << "\nAfter element " << val << " is added to the heap" << endl; + cout << "\nAfter element " << val << " pushes to heap" << endl; maxHeap.print(); - /* Pop the element at the heap top */ + /* Time complexity is O(n), not O(nlogn) */ peek = maxHeap.peek(); maxHeap.pop(); - cout << "\nAfter the top element " << peek << " is removed from the heap" << endl; + cout << "\nAfter heap top element " << peek << " pops from heap" << endl; maxHeap.print(); /* Get heap size */ int size = maxHeap.size(); - cout << "\nNumber of elements in the heap is " << size << endl; + cout << "\nHeap size is " << size << endl; - /* Determine if heap is empty */ + /* Check if heap is empty */ bool isEmpty = maxHeap.isEmpty(); - cout << "\nIs the heap empty " << isEmpty << endl; + cout << "\nIs heap empty " << isEmpty << endl; return 0; } diff --git a/en/codes/cpp/chapter_heap/top_k.cpp b/en/codes/cpp/chapter_heap/top_k.cpp index a95de446a..1bb9a1d92 100644 --- a/en/codes/cpp/chapter_heap/top_k.cpp +++ b/en/codes/cpp/chapter_heap/top_k.cpp @@ -6,17 +6,17 @@ #include "../utils/common.hpp" -/* Using heap to find the largest k elements in an array */ +/* Find the largest k elements in array based on heap */ priority_queue, greater> topKHeap(vector &nums, int k) { - // Initialize min-heap + // Python's heapq module implements min heap by default priority_queue, greater> heap; - // Enter the first k elements of the array into the heap + // Enter the first k elements of array into heap for (int i = 0; i < k; i++) { heap.push(nums[i]); } - // From the k+1th element, keep the heap length as k + // Starting from the (k+1)th element, maintain heap length as k for (int i = k; i < nums.size(); i++) { - // If the current element is larger than the heap top element, remove the heap top element and enter the current element into the heap + // If current element is greater than top element, top element exits heap, current element enters heap if (nums[i] > heap.top()) { heap.pop(); heap.push(nums[i]); @@ -31,7 +31,7 @@ int main() { int k = 3; priority_queue, greater> res = topKHeap(nums, k); - cout << "The largest " << k << " elements are:"; + cout << "The largest " << k << " elements are: "; printHeap(res); return 0; diff --git a/en/codes/cpp/chapter_searching/binary_search.cpp b/en/codes/cpp/chapter_searching/binary_search.cpp index cfa143eb0..e3de96213 100644 --- a/en/codes/cpp/chapter_searching/binary_search.cpp +++ b/en/codes/cpp/chapter_searching/binary_search.cpp @@ -6,39 +6,39 @@ #include "../utils/common.hpp" -/* Binary search (double closed interval) */ +/* Binary search (closed interval on both sides) */ int binarySearch(vector &nums, int target) { - // Initialize double closed interval [0, n-1], i.e., i, j point to the first element and last element of the array respectively + // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array int i = 0, j = nums.size() - 1; - // Loop until the search interval is empty (when i > j, it is empty) + // Loop, exit when the search interval is empty (empty when i > j) while (i <= j) { - int m = i + (j - i) / 2; // Calculate midpoint index m - if (nums[m] < target) // This situation indicates that target is in the interval [m+1, j] + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) // This means target is in the interval [m+1, j] i = m + 1; - else if (nums[m] > target) // This situation indicates that target is in the interval [i, m-1] + else if (nums[m] > target) // This means target is in the interval [i, m-1] j = m - 1; - else // Found the target element, thus return its index + else // Found the target element, return its index return m; } - // Did not find the target element, thus return -1 + // Target element not found, return -1 return -1; } -/* Binary search (left closed right open interval) */ +/* Binary search (left-closed right-open interval) */ int binarySearchLCRO(vector &nums, int target) { - // Initialize left closed right open interval [0, n), i.e., i, j point to the first element and the last element +1 of the array respectively + // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 int i = 0, j = nums.size(); - // Loop until the search interval is empty (when i = j, it is empty) + // Loop, exit when the search interval is empty (empty when i = j) while (i < j) { - int m = i + (j - i) / 2; // Calculate midpoint index m - if (nums[m] < target) // This situation indicates that target is in the interval [m+1, j) + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) // This means target is in the interval [m+1, j) i = m + 1; - else if (nums[m] > target) // This situation indicates that target is in the interval [i, m) + else if (nums[m] > target) // This means target is in the interval [i, m) j = m; - else // Found the target element, thus return its index + else // Found the target element, return its index return m; } - // Did not find the target element, thus return -1 + // Target element not found, return -1 return -1; } @@ -47,13 +47,13 @@ int main() { int target = 6; vector nums = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35}; - /* Binary search (double closed interval) */ + /* Binary search (closed interval on both sides) */ int index = binarySearch(nums, target); - cout << "Index of target element 6 =" << index << endl; + cout << "Index of target element 6 = " << index << endl; - /* Binary search (left closed right open interval) */ + /* Binary search (left-closed right-open interval) */ index = binarySearchLCRO(nums, target); - cout << "Index of target element 6 =" << index << endl; + cout << "Index of target element 6 = " << index << endl; return 0; } diff --git a/en/codes/cpp/chapter_searching/binary_search_edge.cpp b/en/codes/cpp/chapter_searching/binary_search_edge.cpp index 1960fda2d..a2e794d1c 100644 --- a/en/codes/cpp/chapter_searching/binary_search_edge.cpp +++ b/en/codes/cpp/chapter_searching/binary_search_edge.cpp @@ -8,13 +8,13 @@ /* Binary search for insertion point (with duplicate elements) */ int binarySearchInsertion(const vector &nums, int target) { - int i = 0, j = nums.size() - 1; // Initialize double closed interval [0, n-1] + int i = 0, j = nums.size() - 1; // Initialize closed interval [0, n-1] while (i <= j) { - int m = i + (j - i) / 2; // Calculate midpoint index m + int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // Target is in interval [m+1, j] + i = m + 1; // target is in the interval [m+1, j] } else { - j = m - 1; // First element less than target is in interval [i, m-1] + j = m - 1; // The first element less than target is in the interval [i, m-1] } } // Return insertion point i @@ -25,7 +25,7 @@ int binarySearchInsertion(const vector &nums, int target) { int binarySearchLeftEdge(vector &nums, int target) { // Equivalent to finding the insertion point of target int i = binarySearchInsertion(nums, target); - // Did not find target, thus return -1 + // Target not found, return -1 if (i == nums.size() || nums[i] != target) { return -1; } @@ -39,7 +39,7 @@ int binarySearchRightEdge(vector &nums, int target) { int i = binarySearchInsertion(nums, target + 1); // j points to the rightmost target, i points to the first element greater than target int j = i - 1; - // Did not find target, thus return -1 + // Target not found, return -1 if (j == -1 || nums[j] != target) { return -1; } @@ -54,12 +54,12 @@ int main() { cout << "\nArray nums = "; printVector(nums); - // Binary search for left and right boundaries + // Binary search left and right boundaries for (int target : {6, 7}) { int index = binarySearchLeftEdge(nums, target); - cout << "The leftmost index of element " << target << " is " << index << endl; + cout << "Index of leftmost element " << target << " is " << index << endl; index = binarySearchRightEdge(nums, target); - cout << "The rightmost index of element " << target << " is " << index << endl; + cout << "Index of rightmost element " << target << " is " << index << endl; } return 0; diff --git a/en/codes/cpp/chapter_searching/binary_search_insertion.cpp b/en/codes/cpp/chapter_searching/binary_search_insertion.cpp index b8b56e8a3..2278b2062 100644 --- a/en/codes/cpp/chapter_searching/binary_search_insertion.cpp +++ b/en/codes/cpp/chapter_searching/binary_search_insertion.cpp @@ -8,32 +8,32 @@ /* Binary search for insertion point (no duplicate elements) */ int binarySearchInsertionSimple(vector &nums, int target) { - int i = 0, j = nums.size() - 1; // Initialize double closed interval [0, n-1] + int i = 0, j = nums.size() - 1; // Initialize closed interval [0, n-1] while (i <= j) { - int m = i + (j - i) / 2; // Calculate midpoint index m + int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // Target is in interval [m+1, j] + i = m + 1; // target is in the interval [m+1, j] } else if (nums[m] > target) { - j = m - 1; // Target is in interval [i, m-1] + j = m - 1; // target is in the interval [i, m-1] } else { return m; // Found target, return insertion point m } } - // Did not find target, return insertion point i + // Target not found, return insertion point i return i; } /* Binary search for insertion point (with duplicate elements) */ int binarySearchInsertion(vector &nums, int target) { - int i = 0, j = nums.size() - 1; // Initialize double closed interval [0, n-1] + int i = 0, j = nums.size() - 1; // Initialize closed interval [0, n-1] while (i <= j) { - int m = i + (j - i) / 2; // Calculate midpoint index m + int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // Target is in interval [m+1, j] + i = m + 1; // target is in the interval [m+1, j] } else if (nums[m] > target) { - j = m - 1; // Target is in interval [i, m-1] + j = m - 1; // target is in the interval [i, m-1] } else { - j = m - 1; // First element less than target is in interval [i, m-1] + j = m - 1; // The first element less than target is in the interval [i, m-1] } } // Return insertion point i @@ -49,7 +49,7 @@ int main() { // Binary search for insertion point for (int target : {6, 9}) { int index = binarySearchInsertionSimple(nums, target); - cout << "The insertion point index for element " << target << " is " << index << endl; + cout << "Insertion point index for element " << target << " is " << index << endl; } // Array with duplicate elements @@ -59,7 +59,7 @@ int main() { // Binary search for insertion point for (int target : {2, 6, 20}) { int index = binarySearchInsertion(nums, target); - cout << "The insertion point index for element " << target << " is " << index << endl; + cout << "Insertion point index for element " << target << " is " << index << endl; } return 0; diff --git a/en/codes/cpp/chapter_searching/hashing_search.cpp b/en/codes/cpp/chapter_searching/hashing_search.cpp index ad2a9fcf7..eb1e6a451 100644 --- a/en/codes/cpp/chapter_searching/hashing_search.cpp +++ b/en/codes/cpp/chapter_searching/hashing_search.cpp @@ -9,7 +9,7 @@ /* Hash search (array) */ int hashingSearchArray(unordered_map map, int target) { // Hash table's key: target element, value: index - // If the hash table does not contain this key, return -1 + // If this key does not exist in the hash table, return -1 if (map.find(target) == map.end()) return -1; return map[target]; @@ -18,7 +18,7 @@ int hashingSearchArray(unordered_map map, int target) { /* Hash search (linked list) */ ListNode *hashingSearchLinkedList(unordered_map map, int target) { // Hash table key: target node value, value: node object - // If the key is not in the hash table, return nullptr + // Return nullptr if key does not exist in hash table if (map.find(target) == map.end()) return nullptr; return map[target]; @@ -36,7 +36,7 @@ int main() { map[nums[i]] = i; // key: element, value: index } int index = hashingSearchArray(map, target); - cout << "The index of target element 3 is " << index << endl; + cout << "Index of target element 3 = " << index << endl; /* Hash search (linked list) */ ListNode *head = vecToLinkedList(nums); @@ -47,7 +47,7 @@ int main() { head = head->next; } ListNode *node = hashingSearchLinkedList(map1, target); - cout << "The corresponding node object for target node value 3 is " << node << endl; + cout << "Node object corresponding to target node value 3 is " << node << endl; return 0; } diff --git a/en/codes/cpp/chapter_searching/linear_search.cpp b/en/codes/cpp/chapter_searching/linear_search.cpp index fca7d2588..56cae04be 100644 --- a/en/codes/cpp/chapter_searching/linear_search.cpp +++ b/en/codes/cpp/chapter_searching/linear_search.cpp @@ -10,24 +10,24 @@ int linearSearchArray(vector &nums, int target) { // Traverse array for (int i = 0; i < nums.size(); i++) { - // Found the target element, thus return its index + // Found the target element, return its index if (nums[i] == target) return i; } - // Did not find the target element, thus return -1 + // Target element not found, return -1 return -1; } /* Linear search (linked list) */ ListNode *linearSearchLinkedList(ListNode *head, int target) { - // Traverse the list + // Traverse the linked list while (head != nullptr) { // Found the target node, return it if (head->val == target) return head; head = head->next; } - // If the target node is not found, return nullptr + // Target node not found, return nullptr return nullptr; } @@ -38,12 +38,12 @@ int main() { /* Perform linear search in array */ vector nums = {1, 5, 3, 2, 4, 7, 5, 9, 10, 8}; int index = linearSearchArray(nums, target); - cout << "The index of target element 3 is " << index << endl; + cout << "Index of target element 3 = " << index << endl; /* Perform linear search in linked list */ ListNode *head = vecToLinkedList(nums); ListNode *node = linearSearchLinkedList(head, target); - cout << "The corresponding node object for target node value 3 is " << node << endl; + cout << "Node object corresponding to target node value 3 is " << node << endl; return 0; } diff --git a/en/codes/cpp/chapter_searching/two_sum.cpp b/en/codes/cpp/chapter_searching/two_sum.cpp index 8aa22edc9..99b27f334 100644 --- a/en/codes/cpp/chapter_searching/two_sum.cpp +++ b/en/codes/cpp/chapter_searching/two_sum.cpp @@ -6,10 +6,10 @@ #include "../utils/common.hpp" -/* Method one: Brute force enumeration */ +/* Method 1: Brute force enumeration */ vector twoSumBruteForce(vector &nums, int target) { int size = nums.size(); - // Two-layer loop, time complexity is O(n^2) + // Two nested loops, time complexity is O(n^2) for (int i = 0; i < size - 1; i++) { for (int j = i + 1; j < size; j++) { if (nums[i] + nums[j] == target) @@ -19,12 +19,12 @@ vector twoSumBruteForce(vector &nums, int target) { return {}; } -/* Method two: Auxiliary hash table */ +/* Method 2: Auxiliary hash table */ vector twoSumHashTable(vector &nums, int target) { int size = nums.size(); // Auxiliary hash table, space complexity is O(n) unordered_map dic; - // Single-layer loop, time complexity is O(n) + // Single loop, time complexity is O(n) for (int i = 0; i < size; i++) { if (dic.find(target - nums[i]) != dic.end()) { return {dic[target - nums[i]], i}; @@ -41,13 +41,13 @@ int main() { int target = 13; // ====== Driver Code ====== - // Method one + // Method 1 vector res = twoSumBruteForce(nums, target); - cout << "Method one res = "; + cout << "Method 1 res = "; printVector(res); - // Method two + // Method 2 res = twoSumHashTable(nums, target); - cout << "Method two res = "; + cout << "Method 2 res = "; printVector(res); return 0; diff --git a/en/codes/cpp/chapter_sorting/bubble_sort.cpp b/en/codes/cpp/chapter_sorting/bubble_sort.cpp index 490774b36..6b27f9be3 100644 --- a/en/codes/cpp/chapter_sorting/bubble_sort.cpp +++ b/en/codes/cpp/chapter_sorting/bubble_sort.cpp @@ -10,33 +10,33 @@ void bubbleSort(vector &nums) { // Outer loop: unsorted range is [0, i] for (int i = nums.size() - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] - // Here, the std + // Using std::swap() function here swap(nums[j], nums[j + 1]); } } } } -/* Bubble sort (optimized with flag)*/ +/* Bubble sort (flag optimization)*/ void bubbleSortWithFlag(vector &nums) { // Outer loop: unsorted range is [0, i] for (int i = nums.size() - 1; i > 0; i--) { bool flag = false; // Initialize flag - // Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] - // Here, the std + // Using std::swap() function here swap(nums[j], nums[j + 1]); - flag = true; // Record swapped elements + flag = true; // Record element swap } } if (!flag) - break; // If no elements were swapped in this round of "bubbling", exit + break; // No elements were swapped in this round of "bubbling", exit directly } } @@ -44,12 +44,12 @@ void bubbleSortWithFlag(vector &nums) { int main() { vector nums = {4, 1, 3, 1, 5, 2}; bubbleSort(nums); - cout << "After bubble sort, nums = "; + cout << "After bubble sort completes, nums = "; printVector(nums); vector nums1 = {4, 1, 3, 1, 5, 2}; bubbleSortWithFlag(nums1); - cout << "After bubble sort, nums1 = "; + cout << "After bubble sort completes, nums1 = "; printVector(nums1); return 0; diff --git a/en/codes/cpp/chapter_sorting/bucket_sort.cpp b/en/codes/cpp/chapter_sorting/bucket_sort.cpp index eb1713ddf..aa31c98ef 100644 --- a/en/codes/cpp/chapter_sorting/bucket_sort.cpp +++ b/en/codes/cpp/chapter_sorting/bucket_sort.cpp @@ -15,7 +15,7 @@ void bucketSort(vector &nums) { for (float num : nums) { // Input data range is [0, 1), use num * k to map to index range [0, k-1] int i = num * k; - // Add number to bucket_idx + // Add num to bucket bucket_idx buckets[i].push_back(num); } // 2. Sort each bucket @@ -34,10 +34,10 @@ void bucketSort(vector &nums) { /* Driver Code */ int main() { - // Assume input data is floating point, range [0, 1) + // Assume input data is floating point, interval [0, 1) vector nums = {0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f}; bucketSort(nums); - cout << "After bucket sort, nums = "; + cout << "After bucket sort completes, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_sorting/counting_sort.cpp b/en/codes/cpp/chapter_sorting/counting_sort.cpp index e538f28d0..af964a3bd 100644 --- a/en/codes/cpp/chapter_sorting/counting_sort.cpp +++ b/en/codes/cpp/chapter_sorting/counting_sort.cpp @@ -14,7 +14,7 @@ void countingSortNaive(vector &nums) { for (int num : nums) { m = max(m, num); } - // 2. Count the occurrence of each digit + // 2. Count the occurrence of each number // counter[num] represents the occurrence of num vector counter(m + 1, 0); for (int num : nums) { @@ -37,7 +37,7 @@ void countingSort(vector &nums) { for (int num : nums) { m = max(m, num); } - // 2. Count the occurrence of each digit + // 2. Count the occurrence of each number // counter[num] represents the occurrence of num vector counter(m + 1, 0); for (int num : nums) { @@ -65,12 +65,12 @@ void countingSort(vector &nums) { int main() { vector nums = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4}; countingSortNaive(nums); - cout << "After count sort (unable to sort objects), nums = "; + cout << "After counting sort (cannot sort objects) completes, nums = "; printVector(nums); vector nums1 = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4}; countingSort(nums1); - cout << "After count sort, nums1 = "; + cout << "After counting sort completes, nums1 = "; printVector(nums1); return 0; diff --git a/en/codes/cpp/chapter_sorting/heap_sort.cpp b/en/codes/cpp/chapter_sorting/heap_sort.cpp index d2527db81..42eeca195 100644 --- a/en/codes/cpp/chapter_sorting/heap_sort.cpp +++ b/en/codes/cpp/chapter_sorting/heap_sort.cpp @@ -9,7 +9,7 @@ /* Heap length is n, start heapifying node i, from top to bottom */ void siftDown(vector &nums, int n, int i) { while (true) { - // Determine the largest node among i, l, r, noted as ma + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break int l = 2 * i + 1; int r = 2 * i + 2; int ma = i; @@ -17,7 +17,7 @@ void siftDown(vector &nums, int n, int i) { ma = l; if (r < n && nums[r] > nums[ma]) ma = r; - // If node i is the largest or indices l, r are out of bounds, no further heapification needed, break + // Swap two nodes if (ma == i) { break; } @@ -36,7 +36,7 @@ void heapSort(vector &nums) { } // Extract the largest element from the heap and repeat for n-1 rounds for (int i = nums.size() - 1; i > 0; --i) { - // Swap the root node with the rightmost leaf node (swap the first element with the last element) + // Delete node swap(nums[0], nums[i]); // Start heapifying the root node, from top to bottom siftDown(nums, i, 0); @@ -47,7 +47,7 @@ void heapSort(vector &nums) { int main() { vector nums = {4, 1, 3, 1, 5, 2}; heapSort(nums); - cout << "After heap sort, nums = "; + cout << "After heap sort completes, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_sorting/insertion_sort.cpp b/en/codes/cpp/chapter_sorting/insertion_sort.cpp index 2ad2fe1ab..0041f3c6e 100644 --- a/en/codes/cpp/chapter_sorting/insertion_sort.cpp +++ b/en/codes/cpp/chapter_sorting/insertion_sort.cpp @@ -8,10 +8,10 @@ /* Insertion sort */ void insertionSort(vector &nums) { - // Outer loop: sorted range is [0, i-1] + // Outer loop: sorted interval is [0, i-1] for (int i = 1; i < nums.size(); i++) { int base = nums[i], j = i - 1; - // Inner loop: insert base into the correct position within the sorted range [0, i-1] + // Inner loop: insert base into the correct position within the sorted interval [0, i-1] while (j >= 0 && nums[j] > base) { nums[j + 1] = nums[j]; // Move nums[j] to the right by one position j--; @@ -24,7 +24,7 @@ void insertionSort(vector &nums) { int main() { vector nums = {4, 1, 3, 1, 5, 2}; insertionSort(nums); - cout << "After insertion sort, nums = "; + cout << "After insertion sort completes, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_sorting/merge_sort.cpp b/en/codes/cpp/chapter_sorting/merge_sort.cpp index 0d320c005..86486f49c 100644 --- a/en/codes/cpp/chapter_sorting/merge_sort.cpp +++ b/en/codes/cpp/chapter_sorting/merge_sort.cpp @@ -38,7 +38,7 @@ void mergeSort(vector &nums, int left, int right) { // Termination condition if (left >= right) return; // Terminate recursion when subarray length is 1 - // Partition stage + // Divide and conquer stage int mid = left + (right - left) / 2; // Calculate midpoint mergeSort(nums, left, mid); // Recursively process the left subarray mergeSort(nums, mid + 1, right); // Recursively process the right subarray @@ -51,7 +51,7 @@ int main() { /* Merge sort */ vector nums = {7, 3, 2, 6, 0, 1, 5, 4}; mergeSort(nums, 0, nums.size() - 1); - cout << "After merge sort, nums = "; + cout << "After merge sort completes, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_sorting/quick_sort.cpp b/en/codes/cpp/chapter_sorting/quick_sort.cpp index 0a7be7ec8..fea8f5b11 100644 --- a/en/codes/cpp/chapter_sorting/quick_sort.cpp +++ b/en/codes/cpp/chapter_sorting/quick_sort.cpp @@ -9,26 +9,19 @@ /* Quick sort class */ class QuickSort { private: - /* Swap elements */ - static void swap(vector &nums, int i, int j) { - int tmp = nums[i]; - nums[i] = nums[j]; - nums[j] = tmp; - } - - /* Partition */ + /* Sentinel partition */ static int partition(vector &nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Search from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot - swap(nums, i, j); // Swap these two elements + i++; // Search from left to right for the first element greater than the pivot + swap(nums[i], nums[j]); // Swap these two elements } - swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + swap(nums[i], nums[left]); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot } public: @@ -37,7 +30,7 @@ class QuickSort { // Terminate recursion when subarray length is 1 if (left >= right) return; - // Partition + // Sentinel partition int pivot = partition(nums, left, right); // Recursively process the left subarray and right subarray quickSort(nums, left, pivot - 1); @@ -48,13 +41,6 @@ class QuickSort { /* Quick sort class (median pivot optimization) */ class QuickSortMedian { private: - /* Swap elements */ - static void swap(vector &nums, int i, int j) { - int tmp = nums[i]; - nums[i] = nums[j]; - nums[j] = tmp; - } - /* Select the median of three candidate elements */ static int medianThree(vector &nums, int left, int mid, int right) { int l = nums[left], m = nums[mid], r = nums[right]; @@ -65,23 +51,23 @@ class QuickSortMedian { return right; } - /* Partition (median of three) */ + /* Sentinel partition (median of three) */ static int partition(vector &nums, int left, int right) { // Select the median of three candidate elements int med = medianThree(nums, left, (left + right) / 2, right); // Swap the median to the array's leftmost position - swap(nums, left, med); + swap(nums[left], nums[med]); // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Search from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot - swap(nums, i, j); // Swap these two elements + i++; // Search from left to right for the first element greater than the pivot + swap(nums[i], nums[j]); // Swap these two elements } - swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + swap(nums[i], nums[left]); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot } public: @@ -90,7 +76,7 @@ class QuickSortMedian { // Terminate recursion when subarray length is 1 if (left >= right) return; - // Partition + // Sentinel partition int pivot = partition(nums, left, right); // Recursively process the left subarray and right subarray quickSort(nums, left, pivot - 1); @@ -98,37 +84,30 @@ class QuickSortMedian { } }; -/* Quick sort class (tail recursion optimization) */ +/* Quick sort class (recursion depth optimization) */ class QuickSortTailCall { private: - /* Swap elements */ - static void swap(vector &nums, int i, int j) { - int tmp = nums[i]; - nums[i] = nums[j]; - nums[j] = tmp; - } - - /* Partition */ + /* Sentinel partition */ static int partition(vector &nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // Search from right to left for the first element smaller than the pivot + j--; // Search from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // Search from left to right for the first element greater than the pivot - swap(nums, i, j); // Swap these two elements + i++; // Search from left to right for the first element greater than the pivot + swap(nums[i], nums[j]); // Swap these two elements } - swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays - return i; // Return the index of the pivot + swap(nums[i], nums[left]); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot } public: - /* Quick sort (tail recursion optimization) */ + /* Quick sort (recursion depth optimization) */ static void quickSort(vector &nums, int left, int right) { // Terminate when subarray length is 1 while (left < right) { - // Partition operation + // Sentinel partition operation int pivot = partition(nums, left, right); // Perform quick sort on the shorter of the two subarrays if (pivot - left < right - pivot) { @@ -147,19 +126,19 @@ int main() { /* Quick sort */ vector nums{2, 4, 1, 0, 3, 5}; QuickSort::quickSort(nums, 0, nums.size() - 1); - cout << "After quick sort, nums = "; + cout << "After quick sort completes, nums = "; printVector(nums); - /* Quick sort (median pivot optimization) */ + /* Quick sort (recursion depth optimization) */ vector nums1 = {2, 4, 1, 0, 3, 5}; QuickSortMedian::quickSort(nums1, 0, nums1.size() - 1); - cout << "Quick sort (median pivot optimization) completed, nums = "; + cout << "After quick sort (median pivot optimization), nums = "; printVector(nums1); - /* Quick sort (tail recursion optimization) */ + /* Quick sort (recursion depth optimization) */ vector nums2 = {2, 4, 1, 0, 3, 5}; QuickSortTailCall::quickSort(nums2, 0, nums2.size() - 1); - cout << "Quick sort (tail recursion optimization) completed, nums = "; + cout << "After quick sort (recursion depth optimization), nums = "; printVector(nums2); return 0; diff --git a/en/codes/cpp/chapter_sorting/radix_sort.cpp b/en/codes/cpp/chapter_sorting/radix_sort.cpp index e388f36f1..2f68a4997 100644 --- a/en/codes/cpp/chapter_sorting/radix_sort.cpp +++ b/en/codes/cpp/chapter_sorting/radix_sort.cpp @@ -58,7 +58,7 @@ int main() { vector nums = {10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337, 63832996}; radixSort(nums); - cout << "After radix sort, nums = "; + cout << "After radix sort completes, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_sorting/selection_sort.cpp b/en/codes/cpp/chapter_sorting/selection_sort.cpp index 2504e3a25..c58902457 100644 --- a/en/codes/cpp/chapter_sorting/selection_sort.cpp +++ b/en/codes/cpp/chapter_sorting/selection_sort.cpp @@ -9,15 +9,15 @@ /* Selection sort */ void selectionSort(vector &nums) { int n = nums.size(); - // Outer loop: unsorted range is [i, n-1] + // Outer loop: unsorted interval is [i, n-1] for (int i = 0; i < n - 1; i++) { - // Inner loop: find the smallest element within the unsorted range + // Inner loop: find the smallest element within the unsorted interval int k = i; for (int j = i + 1; j < n; j++) { if (nums[j] < nums[k]) k = j; // Record the index of the smallest element } - // Swap the smallest element with the first element of the unsorted range + // Swap the smallest element with the first element of the unsorted interval swap(nums[i], nums[k]); } } @@ -27,7 +27,7 @@ int main() { vector nums = {4, 1, 3, 1, 5, 2}; selectionSort(nums); - cout << "After selection sort, nums = "; + cout << "After selection sort completes, nums = "; printVector(nums); return 0; diff --git a/en/codes/cpp/chapter_stack_and_queue/array_deque.cpp b/en/codes/cpp/chapter_stack_and_queue/array_deque.cpp index 922cbe842..5af0c8440 100644 --- a/en/codes/cpp/chapter_stack_and_queue/array_deque.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/array_deque.cpp @@ -6,12 +6,12 @@ #include "../utils/common.hpp" -/* Double-ended queue class based on circular array */ +/* Double-ended queue based on circular array implementation */ class ArrayDeque { private: - vector nums; // Array used to store elements of the double-ended queue - int front; // Front pointer, pointing to the front element - int queSize; // Length of the double-ended queue + vector nums; // Array for storing double-ended queue elements + int front; // Front pointer, points to the front of the queue element + int queSize; // Double-ended queue length public: /* Constructor */ @@ -30,81 +30,81 @@ class ArrayDeque { return queSize; } - /* Determine if the double-ended queue is empty */ + /* Check if the double-ended queue is empty */ bool isEmpty() { return queSize == 0; } /* Calculate circular array index */ int index(int i) { - // Implement circular array by modulo operation - // When i exceeds the tail of the array, return to the head - // When i exceeds the head of the array, return to the tail + // Use modulo operation to wrap the array head and tail together + // When i passes the tail of the array, return to the head + // When i passes the head of the array, return to the tail return (i + capacity()) % capacity(); } - /* Front enqueue */ + /* Front of the queue enqueue */ void pushFirst(int num) { if (queSize == capacity()) { cout << "Double-ended queue is full" << endl; return; } - // Move the front pointer one position to the left - // Implement front crossing the head of the array to return to the tail by modulo operation + // Use modulo operation to wrap front around to the tail after passing the head of the array + // Add num to the front of the queue front = index(front - 1); - // Add num to the front + // Add num to front of queue nums[front] = num; queSize++; } - /* Rear enqueue */ + /* Rear of the queue enqueue */ void pushLast(int num) { if (queSize == capacity()) { cout << "Double-ended queue is full" << endl; return; } - // Calculate rear pointer, pointing to rear index + 1 + // Use modulo operation to wrap rear around to the head after passing the tail of the array int rear = index(front + queSize); - // Add num to the rear + // Front pointer moves one position backward nums[rear] = num; queSize++; } - /* Front dequeue */ + /* Rear of the queue dequeue */ int popFirst() { int num = peekFirst(); - // Move front pointer one position backward + // Move front pointer backward by one position front = index(front + 1); queSize--; return num; } - /* Rear dequeue */ + /* Access rear of the queue element */ int popLast() { int num = peekLast(); queSize--; return num; } - /* Access front element */ + /* Return list for printing */ int peekFirst() { if (isEmpty()) - throw out_of_range("Double-ended queue is empty"); + throw out_of_range("Deque is empty"); return nums[front]; } - /* Access rear element */ + /* Driver Code */ int peekLast() { if (isEmpty()) - throw out_of_range("Double-ended queue is empty"); - // Calculate rear element index + throw out_of_range("Deque is empty"); + // Initialize double-ended queue int last = index(front + queSize - 1); return nums[last]; } /* Return array for printing */ vector toVector() { - // Only convert elements within valid length range + // Elements enqueue vector res(queSize); for (int i = 0, j = front; i < queSize; i++, j++) { res[i] = nums[index(j)]; @@ -115,7 +115,7 @@ class ArrayDeque { /* Driver Code */ int main() { - /* Initialize double-ended queue */ + /* Get the length of the double-ended queue */ ArrayDeque *deque = new ArrayDeque(10); deque->pushLast(3); deque->pushLast(2); @@ -123,34 +123,34 @@ int main() { cout << "Double-ended queue deque = "; printVector(deque->toVector()); - /* Access element */ + /* Update element */ int peekFirst = deque->peekFirst(); cout << "Front element peekFirst = " << peekFirst << endl; int peekLast = deque->peekLast(); - cout << "Back element peekLast = " << peekLast << endl; + cout << "Rear element peekLast = " << peekLast << endl; - /* Element enqueue */ + /* Elements enqueue */ deque->pushLast(4); - cout << "Element 4 enqueued at the tail, deque = "; + cout << "After element 4 enqueues at rear, deque = "; printVector(deque->toVector()); deque->pushFirst(1); - cout << "Element 1 enqueued at the head, deque = "; + cout << "After element 1 enqueues at front, deque = "; printVector(deque->toVector()); /* Element dequeue */ int popLast = deque->popLast(); - cout << "Deque tail element = " << popLast << ", after dequeuing from the tail"; + cout << "Rear dequeue element = " << popLast << ", after rear dequeue, deque = "; printVector(deque->toVector()); int popFirst = deque->popFirst(); - cout << "Deque front element = " << popFirst << ", after dequeuing from the front"; + cout << "Front dequeue element = " << popFirst << ", after front dequeue, deque = "; printVector(deque->toVector()); /* Get the length of the double-ended queue */ int size = deque->size(); - cout << "Length of the double-ended queue size = " << size << endl; + cout << "Double-ended queue length size = " << size << endl; - /* Determine if the double-ended queue is empty */ + /* Check if the double-ended queue is empty */ bool isEmpty = deque->isEmpty(); - cout << "Is the double-ended queue empty = " << boolalpha << isEmpty << endl; + cout << "Double-ended queue is empty = " << boolalpha << isEmpty << endl; return 0; } diff --git a/en/codes/cpp/chapter_stack_and_queue/array_queue.cpp b/en/codes/cpp/chapter_stack_and_queue/array_queue.cpp index d43abbb53..50b5b6a1c 100644 --- a/en/codes/cpp/chapter_stack_and_queue/array_queue.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/array_queue.cpp @@ -6,17 +6,17 @@ #include "../utils/common.hpp" -/* Queue class based on circular array */ +/* Queue based on circular array implementation */ class ArrayQueue { private: int *nums; // Array for storing queue elements - int front; // Front pointer, pointing to the front element + int front; // Front pointer, points to the front of the queue element int queSize; // Queue length int queCapacity; // Queue capacity public: ArrayQueue(int capacity) { - // Initialize an array + // Initialize array nums = new int[capacity]; queCapacity = capacity; front = queSize = 0; @@ -36,7 +36,7 @@ class ArrayQueue { return queSize; } - /* Determine if the queue is empty */ + /* Check if the queue is empty */ bool isEmpty() { return size() == 0; } @@ -47,10 +47,10 @@ class ArrayQueue { cout << "Queue is full" << endl; return; } - // Calculate rear pointer, pointing to rear index + 1 - // Use modulo operation to wrap the rear pointer from the end of the array back to the start + // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Add num to the rear of the queue int rear = (front + queSize) % queCapacity; - // Add num to the rear + // Front pointer moves one position backward nums[rear] = num; queSize++; } @@ -58,13 +58,13 @@ class ArrayQueue { /* Dequeue */ int pop() { int num = peek(); - // Move front pointer one position backward, returning to the head of the array if it exceeds the tail + // Move front pointer backward by one position, if it passes the tail, return to array head front = (front + 1) % queCapacity; queSize--; return num; } - /* Access front element */ + /* Return list for printing */ int peek() { if (isEmpty()) throw out_of_range("Queue is empty"); @@ -73,7 +73,7 @@ class ArrayQueue { /* Convert array to Vector and return */ vector toVector() { - // Only convert elements within valid length range + // Elements enqueue vector arr(queSize); for (int i = 0, j = front; i < queSize; i++, j++) { arr[i] = nums[j % queCapacity]; @@ -84,11 +84,11 @@ class ArrayQueue { /* Driver Code */ int main() { - /* Initialize queue */ + /* Access front of the queue element */ int capacity = 10; ArrayQueue *queue = new ArrayQueue(capacity); - /* Element enqueue */ + /* Elements enqueue */ queue->push(1); queue->push(3); queue->push(2); @@ -97,28 +97,28 @@ int main() { cout << "Queue queue = "; printVector(queue->toVector()); - /* Access front element */ + /* Return list for printing */ int peek = queue->peek(); cout << "Front element peek = " << peek << endl; /* Element dequeue */ peek = queue->pop(); - cout << "Element dequeued = " << peek << ", after dequeuing"; + cout << "Dequeue element pop = " << peek << ", after dequeue, queue = "; printVector(queue->toVector()); /* Get the length of the queue */ int size = queue->size(); - cout << "Length of the queue size = " << size << endl; + cout << "Queue length size = " << size << endl; - /* Determine if the queue is empty */ + /* Check if the queue is empty */ bool empty = queue->isEmpty(); - cout << "Is the queue empty = " << empty << endl; + cout << "Queue is empty = " << empty << endl; /* Test circular array */ for (int i = 0; i < 10; i++) { queue->push(i); queue->pop(); - cout << "After the " << i << "th round of enqueueing + dequeuing, queue = "; + cout << "After round " << i << " enqueue + dequeue, queue = "; printVector(queue->toVector()); } diff --git a/en/codes/cpp/chapter_stack_and_queue/array_stack.cpp b/en/codes/cpp/chapter_stack_and_queue/array_stack.cpp index 4f5af36d2..9429467d0 100644 --- a/en/codes/cpp/chapter_stack_and_queue/array_stack.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/array_stack.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Stack class based on array */ +/* Stack based on array implementation */ class ArrayStack { private: vector stack; @@ -17,7 +17,7 @@ class ArrayStack { return stack.size(); } - /* Determine if the stack is empty */ + /* Check if the stack is empty */ bool isEmpty() { return stack.size() == 0; } @@ -34,7 +34,7 @@ class ArrayStack { return num; } - /* Access stack top element */ + /* Return list for printing */ int top() { if (isEmpty()) throw out_of_range("Stack is empty"); @@ -49,10 +49,10 @@ class ArrayStack { /* Driver Code */ int main() { - /* Initialize stack */ + /* Access top of the stack element */ ArrayStack *stack = new ArrayStack(); - /* Element push */ + /* Elements push onto stack */ stack->push(1); stack->push(3); stack->push(2); @@ -61,22 +61,22 @@ int main() { cout << "Stack stack = "; printVector(stack->toVector()); - /* Access stack top element */ + /* Return list for printing */ int top = stack->top(); - cout << "Top element of the stack top = " << top << endl; + cout << "Stack top element top = " << top << endl; - /* Element pop */ + /* Element pop from stack */ top = stack->pop(); - cout << "Element popped from the stack = " << top << ", after popping"; + cout << "Pop element pop = " << top << ", after pop, stack = "; printVector(stack->toVector()); /* Get the length of the stack */ int size = stack->size(); - cout << "Length of the stack size = " << size << endl; + cout << "Stack length size = " << size << endl; - /* Determine if it's empty */ + /* Check if empty */ bool empty = stack->isEmpty(); - cout << "Is the stack empty = " << empty << endl; + cout << "Stack is empty = " << empty << endl; // Free memory delete stack; diff --git a/en/codes/cpp/chapter_stack_and_queue/deque.cpp b/en/codes/cpp/chapter_stack_and_queue/deque.cpp index f24332ee1..c9f7ecc5d 100644 --- a/en/codes/cpp/chapter_stack_and_queue/deque.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/deque.cpp @@ -8,10 +8,10 @@ /* Driver Code */ int main() { - /* Initialize double-ended queue */ + /* Get the length of the double-ended queue */ deque deque; - /* Element enqueue */ + /* Elements enqueue */ deque.push_back(2); deque.push_back(5); deque.push_back(4); @@ -20,27 +20,27 @@ int main() { cout << "Double-ended queue deque = "; printDeque(deque); - /* Access element */ + /* Update element */ int front = deque.front(); - cout << "Front element of the queue front = " << front << endl; + cout << "Front element front = " << front << endl; int back = deque.back(); - cout << "Back element of the queue back = " << back << endl; + cout << "Back element back = " << back << endl; /* Element dequeue */ deque.pop_front(); - cout << "Front element dequeued = " << front << ", after dequeuing from the front"; + cout << "Front dequeue element popFront = " << front << ", after front dequeue, deque = "; printDeque(deque); deque.pop_back(); - cout << "Back element dequeued = " << back << ", after dequeuing from the back"; + cout << "Rear dequeue element popLast = " << back << ", after rear dequeue, deque = "; printDeque(deque); /* Get the length of the double-ended queue */ int size = deque.size(); - cout << "Length of the double-ended queue size = " << size << endl; + cout << "Double-ended queue length size = " << size << endl; - /* Determine if the double-ended queue is empty */ + /* Check if the double-ended queue is empty */ bool empty = deque.empty(); - cout << "Is the double-ended queue empty = " << empty << endl; + cout << "Double-ended queue is empty = " << empty << endl; return 0; } diff --git a/en/codes/cpp/chapter_stack_and_queue/linkedlist_deque.cpp b/en/codes/cpp/chapter_stack_and_queue/linkedlist_deque.cpp index 1bcf74e79..eb3eb59b1 100644 --- a/en/codes/cpp/chapter_stack_and_queue/linkedlist_deque.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/linkedlist_deque.cpp @@ -6,19 +6,19 @@ #include "../utils/common.hpp" -/* Double-linked list node */ +/* Doubly linked list node */ struct DoublyListNode { int val; // Node value - DoublyListNode *next; // Pointer to successor node - DoublyListNode *prev; // Pointer to predecessor node + DoublyListNode *next; // Successor node pointer + DoublyListNode *prev; // Predecessor node pointer DoublyListNode(int val) : val(val), prev(nullptr), next(nullptr) { } }; -/* Double-ended queue class based on double-linked list */ +/* Double-ended queue based on doubly linked list implementation */ class LinkedListDeque { private: - DoublyListNode *front, *rear; // Front node front, back node rear + DoublyListNode *front, *rear; // Head node front, tail node rear int queSize = 0; // Length of the double-ended queue public: @@ -28,7 +28,7 @@ class LinkedListDeque { /* Destructor */ ~LinkedListDeque() { - // Traverse the linked list, remove nodes, free memory + // Traverse linked list to delete nodes and free memory DoublyListNode *pre, *cur = front; while (cur != nullptr) { pre = cur; @@ -42,7 +42,7 @@ class LinkedListDeque { return queSize; } - /* Determine if the double-ended queue is empty */ + /* Check if the double-ended queue is empty */ bool isEmpty() { return size() == 0; } @@ -50,18 +50,18 @@ class LinkedListDeque { /* Enqueue operation */ void push(int num, bool isFront) { DoublyListNode *node = new DoublyListNode(num); - // If the list is empty, make front and rear both point to node + // If the linked list is empty, make both front and rear point to node if (isEmpty()) front = rear = node; - // Front enqueue operation + // Front of the queue enqueue operation else if (isFront) { - // Add node to the head of the list + // Add node to the head of the linked list front->prev = node; node->next = front; front = node; // Update head node - // Rear enqueue operation + // Rear of the queue enqueue operation } else { - // Add node to the tail of the list + // Add node to the tail of the linked list rear->next = node; node->prev = rear; rear = node; // Update tail node @@ -69,12 +69,12 @@ class LinkedListDeque { queSize++; // Update queue length } - /* Front enqueue */ + /* Front of the queue enqueue */ void pushFirst(int num) { push(num, true); } - /* Rear enqueue */ + /* Rear of the queue enqueue */ void pushLast(int num) { push(num, false); } @@ -84,10 +84,10 @@ class LinkedListDeque { if (isEmpty()) throw out_of_range("Queue is empty"); int val; - // Front dequeue operation + // Temporarily store head node value if (isFront) { - val = front->val; // Temporarily store the head node value - // Remove head node + val = front->val; // Delete head node + // Delete head node DoublyListNode *fNext = front->next; if (fNext != nullptr) { fNext->prev = nullptr; @@ -95,10 +95,10 @@ class LinkedListDeque { } delete front; front = fNext; // Update head node - // Rear dequeue operation + // Temporarily store tail node value } else { - val = rear->val; // Temporarily store the tail node value - // Remove tail node + val = rear->val; // Delete tail node + // Update tail node DoublyListNode *rPrev = rear->prev; if (rPrev != nullptr) { rPrev->next = nullptr; @@ -111,27 +111,27 @@ class LinkedListDeque { return val; } - /* Front dequeue */ + /* Rear of the queue dequeue */ int popFirst() { return pop(true); } - /* Rear dequeue */ + /* Access rear of the queue element */ int popLast() { return pop(false); } - /* Access front element */ + /* Return list for printing */ int peekFirst() { if (isEmpty()) - throw out_of_range("Double-ended queue is empty"); + throw out_of_range("Deque is empty"); return front->val; } - /* Access rear element */ + /* Driver Code */ int peekLast() { if (isEmpty()) - throw out_of_range("Double-ended queue is empty"); + throw out_of_range("Deque is empty"); return rear->val; } @@ -149,7 +149,7 @@ class LinkedListDeque { /* Driver Code */ int main() { - /* Initialize double-ended queue */ + /* Get the length of the double-ended queue */ LinkedListDeque *deque = new LinkedListDeque(); deque->pushLast(3); deque->pushLast(2); @@ -157,35 +157,35 @@ int main() { cout << "Double-ended queue deque = "; printVector(deque->toVector()); - /* Access element */ + /* Update element */ int peekFirst = deque->peekFirst(); cout << "Front element peekFirst = " << peekFirst << endl; int peekLast = deque->peekLast(); - cout << "Back element peekLast = " << peekLast << endl; + cout << "Rear element peekLast = " << peekLast << endl; - /* Element enqueue */ + /* Elements enqueue */ deque->pushLast(4); - cout << "Element 4 rear enqueued, deque ="; + cout << "After element 4 enqueues at back, deque ="; printVector(deque->toVector()); deque->pushFirst(1); - cout << "Element 1 enqueued at the head, deque = "; + cout << "After element 1 enqueues at front, deque = "; printVector(deque->toVector()); /* Element dequeue */ int popLast = deque->popLast(); - cout << "Deque tail element = " << popLast << ", after dequeuing from the tail"; + cout << "Rear dequeue element = " << popLast << ", after rear dequeue, deque = "; printVector(deque->toVector()); int popFirst = deque->popFirst(); - cout << "Deque front element = " << popFirst << ", after dequeuing from the front"; + cout << "Front dequeue element = " << popFirst << ", after front dequeue, deque = "; printVector(deque->toVector()); /* Get the length of the double-ended queue */ int size = deque->size(); - cout << "Length of the double-ended queue size = " << size << endl; + cout << "Double-ended queue length size = " << size << endl; - /* Determine if the double-ended queue is empty */ + /* Check if the double-ended queue is empty */ bool isEmpty = deque->isEmpty(); - cout << "Is the double-ended queue empty = " << boolalpha << isEmpty << endl; + cout << "Double-ended queue is empty = " << boolalpha << isEmpty << endl; // Free memory delete deque; diff --git a/en/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp b/en/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp index 7326a9dc4..805861090 100644 --- a/en/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp @@ -6,10 +6,10 @@ #include "../utils/common.hpp" -/* Queue class based on linked list */ +/* Queue based on linked list implementation */ class LinkedListQueue { private: - ListNode *front, *rear; // Front node front, back node rear + ListNode *front, *rear; // Head node front, tail node rear int queSize; public: @@ -20,7 +20,7 @@ class LinkedListQueue { } ~LinkedListQueue() { - // Traverse the linked list, remove nodes, free memory + // Traverse linked list to delete nodes and free memory freeMemoryLinkedList(front); } @@ -29,21 +29,21 @@ class LinkedListQueue { return queSize; } - /* Determine if the queue is empty */ + /* Check if the queue is empty */ bool isEmpty() { return queSize == 0; } /* Enqueue */ void push(int num) { - // Add num behind the tail node + // Add num after the tail node ListNode *node = new ListNode(num); - // If the queue is empty, make the head and tail nodes both point to that node + // If the queue is empty, make both front and rear point to the node if (front == nullptr) { front = node; rear = node; } - // If the queue is not empty, add that node behind the tail node + // If the queue is not empty, add the node after the tail node else { rear->next = node; rear = node; @@ -54,7 +54,7 @@ class LinkedListQueue { /* Dequeue */ int pop() { int num = peek(); - // Remove head node + // Delete head node ListNode *tmp = front; front = front->next; // Free memory @@ -63,14 +63,14 @@ class LinkedListQueue { return num; } - /* Access front element */ + /* Return list for printing */ int peek() { if (size() == 0) throw out_of_range("Queue is empty"); return front->val; } - /* Convert the linked list to Vector and return */ + /* Convert linked list to Vector and return */ vector toVector() { ListNode *node = front; vector res(size()); @@ -84,10 +84,10 @@ class LinkedListQueue { /* Driver Code */ int main() { - /* Initialize queue */ + /* Access front of the queue element */ LinkedListQueue *queue = new LinkedListQueue(); - /* Element enqueue */ + /* Elements enqueue */ queue->push(1); queue->push(3); queue->push(2); @@ -96,22 +96,22 @@ int main() { cout << "Queue queue = "; printVector(queue->toVector()); - /* Access front element */ + /* Return list for printing */ int peek = queue->peek(); cout << "Front element peek = " << peek << endl; /* Element dequeue */ peek = queue->pop(); - cout << "Element dequeued = " << peek << ", after dequeuing"; + cout << "Dequeue element pop = " << peek << ", after dequeue, queue = "; printVector(queue->toVector()); /* Get the length of the queue */ int size = queue->size(); - cout << "Length of the queue size = " << size << endl; + cout << "Queue length size = " << size << endl; - /* Determine if the queue is empty */ + /* Check if the queue is empty */ bool empty = queue->isEmpty(); - cout << "Is the queue empty = " << empty << endl; + cout << "Queue is empty = " << empty << endl; // Free memory delete queue; diff --git a/en/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp b/en/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp index 655f9f02b..fbc791dc9 100644 --- a/en/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp @@ -6,11 +6,11 @@ #include "../utils/common.hpp" -/* Stack class based on linked list */ +/* Stack based on linked list implementation */ class LinkedListStack { private: - ListNode *stackTop; // Use the head node as the top of the stack - int stkSize; // Length of the stack + ListNode *stackTop; // Use head node as stack top + int stkSize; // Stack length public: LinkedListStack() { @@ -19,7 +19,7 @@ class LinkedListStack { } ~LinkedListStack() { - // Traverse the linked list, remove nodes, free memory + // Traverse linked list to delete nodes and free memory freeMemoryLinkedList(stackTop); } @@ -28,7 +28,7 @@ class LinkedListStack { return stkSize; } - /* Determine if the stack is empty */ + /* Check if the stack is empty */ bool isEmpty() { return size() == 0; } @@ -52,14 +52,14 @@ class LinkedListStack { return num; } - /* Access stack top element */ + /* Return list for printing */ int top() { if (isEmpty()) throw out_of_range("Stack is empty"); return stackTop->val; } - /* Convert the List to Array and return */ + /* Convert List to Array and return */ vector toVector() { ListNode *node = stackTop; vector res(size()); @@ -73,10 +73,10 @@ class LinkedListStack { /* Driver Code */ int main() { - /* Initialize stack */ + /* Access top of the stack element */ LinkedListStack *stack = new LinkedListStack(); - /* Element push */ + /* Elements push onto stack */ stack->push(1); stack->push(3); stack->push(2); @@ -85,22 +85,22 @@ int main() { cout << "Stack stack = "; printVector(stack->toVector()); - /* Access stack top element */ + /* Return list for printing */ int top = stack->top(); - cout << "Top element of the stack top = " << top << endl; + cout << "Stack top element top = " << top << endl; - /* Element pop */ + /* Element pop from stack */ top = stack->pop(); - cout << "Element popped from the stack = " << top << ", after popping"; + cout << "Pop element pop = " << top << ", after pop, stack = "; printVector(stack->toVector()); /* Get the length of the stack */ int size = stack->size(); - cout << "Length of the stack size = " << size << endl; + cout << "Stack length size = " << size << endl; - /* Determine if it's empty */ + /* Check if empty */ bool empty = stack->isEmpty(); - cout << "Is the stack empty = " << empty << endl; + cout << "Stack is empty = " << empty << endl; // Free memory delete stack; diff --git a/en/codes/cpp/chapter_stack_and_queue/queue.cpp b/en/codes/cpp/chapter_stack_and_queue/queue.cpp index 6414ef036..7b541b653 100644 --- a/en/codes/cpp/chapter_stack_and_queue/queue.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/queue.cpp @@ -8,10 +8,10 @@ /* Driver Code */ int main() { - /* Initialize queue */ + /* Access front of the queue element */ queue queue; - /* Element enqueue */ + /* Elements enqueue */ queue.push(1); queue.push(3); queue.push(2); @@ -20,22 +20,22 @@ int main() { cout << "Queue queue = "; printQueue(queue); - /* Access front element */ + /* Return list for printing */ int front = queue.front(); - cout << "Front element of the queue front = " << front << endl; + cout << "Front element front = " << front << endl; /* Element dequeue */ queue.pop(); - cout << "Element dequeued = " << front << ", after dequeuing"; + cout << "Dequeue element front = " << front << ", after dequeue, queue = "; printQueue(queue); /* Get the length of the queue */ int size = queue.size(); - cout << "Length of the queue size = " << size << endl; + cout << "Queue length size = " << size << endl; - /* Determine if the queue is empty */ + /* Check if the queue is empty */ bool empty = queue.empty(); - cout << "Is the queue empty = " << empty << endl; + cout << "Queue is empty = " << empty << endl; return 0; } diff --git a/en/codes/cpp/chapter_stack_and_queue/stack.cpp b/en/codes/cpp/chapter_stack_and_queue/stack.cpp index 6b78e18d7..1d20521b2 100644 --- a/en/codes/cpp/chapter_stack_and_queue/stack.cpp +++ b/en/codes/cpp/chapter_stack_and_queue/stack.cpp @@ -8,10 +8,10 @@ /* Driver Code */ int main() { - /* Initialize stack */ + /* Access top of the stack element */ stack stack; - /* Element push */ + /* Elements push onto stack */ stack.push(1); stack.push(3); stack.push(2); @@ -20,22 +20,22 @@ int main() { cout << "Stack stack = "; printStack(stack); - /* Access stack top element */ + /* Return list for printing */ int top = stack.top(); - cout << "Top element of the stack top = " << top << endl; + cout << "Stack top element top = " << top << endl; - /* Element pop */ + /* Element pop from stack */ stack.pop(); // No return value - cout << "Element popped from the stack = " << top << ", after popping"; + cout << "Pop element pop = " << top << ", after pop, stack = "; printStack(stack); /* Get the length of the stack */ int size = stack.size(); - cout << "Length of the stack size = " << size << endl; + cout << "Stack length size = " << size << endl; - /* Determine if it's empty */ + /* Check if empty */ bool empty = stack.empty(); - cout << "Is the stack empty = " << empty << endl; + cout << "Stack is empty = " << empty << endl; return 0; } diff --git a/en/codes/cpp/chapter_tree/array_binary_tree.cpp b/en/codes/cpp/chapter_tree/array_binary_tree.cpp index 458fa2062..727a94fcc 100644 --- a/en/codes/cpp/chapter_tree/array_binary_tree.cpp +++ b/en/codes/cpp/chapter_tree/array_binary_tree.cpp @@ -6,7 +6,7 @@ #include "../utils/common.hpp" -/* Array-based binary tree class */ +/* Binary tree class represented by array */ class ArrayBinaryTree { public: /* Constructor */ @@ -19,25 +19,25 @@ class ArrayBinaryTree { return tree.size(); } - /* Get the value of the node at index i */ + /* Get value of node at index i */ int val(int i) { - // If index is out of bounds, return INT_MAX, representing a null + // Return INT_MAX if index out of bounds, representing empty position if (i < 0 || i >= size()) return INT_MAX; return tree[i]; } - /* Get the index of the left child of the node at index i */ + /* Get index of left child node of node at index i */ int left(int i) { return 2 * i + 1; } - /* Get the index of the right child of the node at index i */ + /* Get index of right child node of node at index i */ int right(int i) { return 2 * i + 2; } - /* Get the index of the parent of the node at index i */ + /* Get index of parent node of node at index i */ int parent(int i) { return (i - 1) / 2; } @@ -45,7 +45,7 @@ class ArrayBinaryTree { /* Level-order traversal */ vector levelOrder() { vector res; - // Traverse array + // Traverse array directly for (int i = 0; i < size(); i++) { if (val(i) != INT_MAX) res.push_back(val(i)); @@ -53,21 +53,21 @@ class ArrayBinaryTree { return res; } - /* Pre-order traversal */ + /* Preorder traversal */ vector preOrder() { vector res; dfs(0, "pre", res); return res; } - /* In-order traversal */ + /* Inorder traversal */ vector inOrder() { vector res; dfs(0, "in", res); return res; } - /* Post-order traversal */ + /* Postorder traversal */ vector postOrder() { vector res; dfs(0, "post", res); @@ -79,18 +79,18 @@ class ArrayBinaryTree { /* Depth-first traversal */ void dfs(int i, string order, vector &res) { - // If it is an empty spot, return + // If empty position, return if (val(i) == INT_MAX) return; - // Pre-order traversal + // Preorder traversal if (order == "pre") res.push_back(val(i)); dfs(left(i), order, res); - // In-order traversal + // Inorder traversal if (order == "in") res.push_back(val(i)); dfs(right(i), order, res); - // Post-order traversal + // Postorder traversal if (order == "post") res.push_back(val(i)); } @@ -99,38 +99,38 @@ class ArrayBinaryTree { /* Driver Code */ int main() { // Initialize binary tree - // Use INT_MAX to represent an empty spot nullptr + // Use INT_MAX to represent empty position nullptr vector arr = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; TreeNode *root = vectorToTree(arr); cout << "\nInitialize binary tree\n"; - cout << "Binary tree in array representation:\n"; + cout << "Array representation of binary tree:\n"; printVector(arr); - cout << "Binary tree in linked list representation:\n"; + cout << "Linked list representation of binary tree:\n"; printTree(root); - // Array-based binary tree class + // Binary tree class represented by array ArrayBinaryTree abt(arr); // Access node int i = 1; int l = abt.left(i), r = abt.right(i), p = abt.parent(i); - cout << "\nCurrent node's index is " << i << ", value = " << abt.val(i) << "\n"; - cout << "Its left child's index is " << l << ", value = " << (l != INT_MAX ? to_string(abt.val(l)) : "nullptr") << "\n"; - cout << "Its right child's index is " << r << ", value = " << (r != INT_MAX ? to_string(abt.val(r)) : "nullptr") << "\n"; - cout << "Its parent's index is " << p << ", value = " << (p != INT_MAX ? to_string(abt.val(p)) : "nullptr") << "\n"; + cout << "\nCurrent node index is " << i << ", value is " << abt.val(i) << "\n"; + cout << "Its left child node index is " << l << ", value is " << (abt.val(l) != INT_MAX ? to_string(abt.val(l)) : "nullptr") << "\n"; + cout << "Its right child node index is " << r << ", value is " << (abt.val(r) != INT_MAX ? to_string(abt.val(r)) : "nullptr") << "\n"; + cout << "Its parent node index is " << p << ", value is " << (abt.val(p) != INT_MAX ? to_string(abt.val(p)) : "nullptr") << "\n"; // Traverse tree vector res = abt.levelOrder(); - cout << "\nLevel-order traversal is:"; + cout << "\nLevel-order traversal: "; printVector(res); res = abt.preOrder(); - cout << "Pre-order traversal is:"; + cout << "Pre-order traversal: "; printVector(res); res = abt.inOrder(); - cout << "In-order traversal is:"; + cout << "In-order traversal: "; printVector(res); res = abt.postOrder(); - cout << "Post-order traversal is:"; + cout << "Post-order traversal: "; printVector(res); return 0; diff --git a/en/codes/cpp/chapter_tree/avl_tree.cpp b/en/codes/cpp/chapter_tree/avl_tree.cpp index ac3fbeec9..5047bba3a 100644 --- a/en/codes/cpp/chapter_tree/avl_tree.cpp +++ b/en/codes/cpp/chapter_tree/avl_tree.cpp @@ -19,13 +19,13 @@ class AVLTree { TreeNode *rightRotate(TreeNode *node) { TreeNode *child = node->left; TreeNode *grandChild = child->right; - // Rotate node to the right around child + // Using child as pivot, rotate node to the right child->right = node; node->left = grandChild; // Update node height updateHeight(node); updateHeight(child); - // Return the root of the subtree after rotation + // Return root node of subtree after rotation return child; } @@ -33,19 +33,19 @@ class AVLTree { TreeNode *leftRotate(TreeNode *node) { TreeNode *child = node->right; TreeNode *grandChild = child->left; - // Rotate node to the left around child + // Using child as pivot, rotate node to the left child->left = node; node->right = grandChild; // Update node height updateHeight(node); updateHeight(child); - // Return the root of the subtree after rotation + // Return root node of subtree after rotation return child; } - /* Perform rotation operation to restore balance to the subtree */ + /* Perform rotation operation to restore balance to this subtree */ TreeNode *rotate(TreeNode *node) { - // Get the balance factor of node + // Get balance factor of node int _balanceFactor = balanceFactor(node); // Left-leaning tree if (_balanceFactor > 1) { @@ -69,7 +69,7 @@ class AVLTree { return leftRotate(node); } } - // Balanced tree, no rotation needed, return + // Balanced tree, no rotation needed, return directly return node; } @@ -83,19 +83,19 @@ class AVLTree { else if (val > node->val) node->right = insertHelper(node->right, val); else - return node; // Do not insert duplicate nodes, return + return node; // Duplicate node not inserted, return directly updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to the subtree */ + /* 2. Perform rotation operation to restore balance to this subtree */ node = rotate(node); - // Return the root node of the subtree + // Return root node of subtree return node; } - /* Recursively remove node (helper method) */ + /* Recursively delete node (helper method) */ TreeNode *removeHelper(TreeNode *node, int val) { if (node == nullptr) return nullptr; - /* 1. Find and remove the node */ + /* 1. Find node and delete */ if (val < node->val) node->left = removeHelper(node->left, val); else if (val > node->val) @@ -103,18 +103,18 @@ class AVLTree { else { if (node->left == nullptr || node->right == nullptr) { TreeNode *child = node->left != nullptr ? node->left : node->right; - // Number of child nodes = 0, remove node and return + // Number of child nodes = 0, delete node directly and return if (child == nullptr) { delete node; return nullptr; } - // Number of child nodes = 1, remove node + // Number of child nodes = 1, delete node directly else { delete node; node = child; } } else { - // Number of child nodes = 2, remove the next node in in-order traversal and replace the current node with it + // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it TreeNode *temp = node->right; while (temp->left != nullptr) { temp = temp->left; @@ -125,9 +125,9 @@ class AVLTree { } } updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to the subtree */ + /* 2. Perform rotation operation to restore balance to this subtree */ node = rotate(node); - // Return the root node of the subtree + // Return root node of subtree return node; } @@ -162,7 +162,7 @@ class AVLTree { /* Search node */ TreeNode *search(int val) { TreeNode *cur = root; - // Loop find, break after passing leaf nodes + // Loop search, exit after passing leaf node while (cur != nullptr) { // Target node is in cur's right subtree if (cur->val < val) @@ -170,7 +170,7 @@ class AVLTree { // Target node is in cur's left subtree else if (cur->val > val) cur = cur->left; - // Found target node, break loop + // Found target node, exit loop else break; } @@ -190,23 +190,23 @@ class AVLTree { void testInsert(AVLTree &tree, int val) { tree.insert(val); - cout << "\nAfter inserting node " << val << ", the AVL tree is" << endl; + cout << "\nAfter inserting node " << val << ", AVL tree is" << endl; printTree(tree.root); } void testRemove(AVLTree &tree, int val) { tree.remove(val); - cout << "\nAfter removing node " << val << ", the AVL tree is" << endl; + cout << "\nAfter removing node " << val << ", AVL tree is" << endl; printTree(tree.root); } /* Driver Code */ int main() { - /* Initialize empty AVL tree */ + /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ AVLTree avlTree; /* Insert node */ - // Notice how the AVL tree maintains balance after inserting nodes + // Delete nodes testInsert(avlTree, 1); testInsert(avlTree, 2); testInsert(avlTree, 3); @@ -218,16 +218,16 @@ int main() { testInsert(avlTree, 10); testInsert(avlTree, 6); - /* Insert duplicate node */ + /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ testInsert(avlTree, 7); /* Remove node */ - // Notice how the AVL tree maintains balance after removing nodes - testRemove(avlTree, 8); // Remove node with degree 0 + // Delete node with degree 1 + testRemove(avlTree, 8); // Delete node with degree 2 testRemove(avlTree, 5); // Remove node with degree 1 testRemove(avlTree, 4); // Remove node with degree 2 /* Search node */ TreeNode *node = avlTree.search(7); - cout << "\nThe found node object is " << node << ", node value =" << node->val << endl; + cout << "\nFound node object is " << node << ", node value = " << node->val << endl; } diff --git a/en/codes/cpp/chapter_tree/binary_search_tree.cpp b/en/codes/cpp/chapter_tree/binary_search_tree.cpp index 2f4d7895f..0a2888b4a 100644 --- a/en/codes/cpp/chapter_tree/binary_search_tree.cpp +++ b/en/codes/cpp/chapter_tree/binary_search_tree.cpp @@ -31,7 +31,7 @@ class BinarySearchTree { /* Search node */ TreeNode *search(int num) { TreeNode *cur = root; - // Loop find, break after passing leaf nodes + // Loop search, exit after passing leaf node while (cur != nullptr) { // Target node is in cur's right subtree if (cur->val < num) @@ -39,7 +39,7 @@ class BinarySearchTree { // Target node is in cur's left subtree else if (cur->val > num) cur = cur->left; - // Found target node, break loop + // Found target node, exit loop else break; } @@ -55,9 +55,9 @@ class BinarySearchTree { return; } TreeNode *cur = root, *pre = nullptr; - // Loop find, break after passing leaf nodes + // Loop search, exit after passing leaf node while (cur != nullptr) { - // Found duplicate node, thus return + // Found duplicate node, return directly if (cur->val == num) return; pre = cur; @@ -78,38 +78,38 @@ class BinarySearchTree { /* Remove node */ void remove(int num) { - // If tree is empty, return + // If tree is empty, return directly if (root == nullptr) return; TreeNode *cur = root, *pre = nullptr; - // Loop find, break after passing leaf nodes + // Loop search, exit after passing leaf node while (cur != nullptr) { - // Found node to be removed, break loop + // Found node to delete, exit loop if (cur->val == num) break; pre = cur; - // Node to be removed is in cur's right subtree + // Node to delete is in cur's right subtree if (cur->val < num) cur = cur->right; - // Node to be removed is in cur's left subtree + // Node to delete is in cur's left subtree else cur = cur->left; } - // If no node to be removed, return + // If no node to delete, return directly if (cur == nullptr) return; // Number of child nodes = 0 or 1 if (cur->left == nullptr || cur->right == nullptr) { - // When the number of child nodes = 0 / 1, child = nullptr / that child node + // When number of child nodes = 0 / 1, child = nullptr / that child node TreeNode *child = cur->left != nullptr ? cur->left : cur->right; - // Remove node cur + // Delete node cur if (cur != root) { if (pre->left == cur) pre->left = child; else pre->right = child; } else { - // If the removed node is the root, reassign the root + // If deleted node is root node, reassign root node root = child; } // Free memory @@ -117,13 +117,13 @@ class BinarySearchTree { } // Number of child nodes = 2 else { - // Get the next node in in-order traversal of cur + // Get next node of cur in inorder traversal TreeNode *tmp = cur->right; while (tmp->left != nullptr) { tmp = tmp->left; } int tmpVal = tmp->val; - // Recursively remove node tmp + // Recursively delete node tmp remove(tmp->val); // Replace cur with tmp cur->val = tmpVal; @@ -135,32 +135,32 @@ class BinarySearchTree { int main() { /* Initialize binary search tree */ BinarySearchTree *bst = new BinarySearchTree(); - // Note that different insertion orders can result in various tree structures. This particular sequence creates a perfect binary tree + // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree vector nums = {8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15}; for (int num : nums) { bst->insert(num); } - cout << endl << "The initialized binary tree is\n" << endl; + cout << endl << "Initialized binary tree is\n" << endl; printTree(bst->getRoot()); /* Search node */ TreeNode *node = bst->search(7); - cout << endl << "The found node object is " << node << ", node value =" << node->val << endl; + cout << endl << "Found node object is " << node << ", node value = " << node->val << endl; /* Insert node */ bst->insert(16); - cout << endl << "After inserting node 16, the binary tree is\n" << endl; + cout << endl << "After inserting node 16, binary tree is\n" << endl; printTree(bst->getRoot()); /* Remove node */ bst->remove(1); - cout << endl << "After removing node 1, the binary tree is\n" << endl; + cout << endl << "After removing node 1, binary tree is\n" << endl; printTree(bst->getRoot()); bst->remove(2); - cout << endl << "After removing node 2, the binary tree is\n" << endl; + cout << endl << "After removing node 2, binary tree is\n" << endl; printTree(bst->getRoot()); bst->remove(4); - cout << endl << "After removing node 4, the binary tree is\n" << endl; + cout << endl << "After removing node 4, binary tree is\n" << endl; printTree(bst->getRoot()); // Free memory diff --git a/en/codes/cpp/chapter_tree/binary_tree.cpp b/en/codes/cpp/chapter_tree/binary_tree.cpp index f1b13b230..fe24118e1 100644 --- a/en/codes/cpp/chapter_tree/binary_tree.cpp +++ b/en/codes/cpp/chapter_tree/binary_tree.cpp @@ -9,13 +9,13 @@ /* Driver Code */ int main() { /* Initialize binary tree */ - // Initialize node + // Initialize nodes TreeNode *n1 = new TreeNode(1); TreeNode *n2 = new TreeNode(2); TreeNode *n3 = new TreeNode(3); TreeNode *n4 = new TreeNode(4); TreeNode *n5 = new TreeNode(5); - // Construct node references (pointers) + // Build references (pointers) between nodes n1->left = n2; n1->right = n3; n2->left = n4; @@ -23,9 +23,9 @@ int main() { cout << endl << "Initialize binary tree\n" << endl; printTree(n1); - /* Insert and remove nodes */ + /* Insert node P between n1 -> n2 */ TreeNode *P = new TreeNode(0); - // Insert node P between n1 -> n2 + // Delete node n1->left = P; P->left = n2; cout << endl << "After inserting node P\n" << endl; diff --git a/en/codes/cpp/chapter_tree/binary_tree_bfs.cpp b/en/codes/cpp/chapter_tree/binary_tree_bfs.cpp index 80e094f19..0b383a7cf 100644 --- a/en/codes/cpp/chapter_tree/binary_tree_bfs.cpp +++ b/en/codes/cpp/chapter_tree/binary_tree_bfs.cpp @@ -11,16 +11,16 @@ vector levelOrder(TreeNode *root) { // Initialize queue, add root node queue queue; queue.push(root); - // Initialize a list to store the traversal sequence + // Initialize a list to save the traversal sequence vector vec; while (!queue.empty()) { TreeNode *node = queue.front(); - queue.pop(); // Queue dequeues + queue.pop(); // Dequeue vec.push_back(node->val); // Save node value if (node->left != nullptr) - queue.push(node->left); // Left child node enqueues + queue.push(node->left); // Left child node enqueue if (node->right != nullptr) - queue.push(node->right); // Right child node enqueues + queue.push(node->right); // Right child node enqueue } return vec; } @@ -28,14 +28,14 @@ vector levelOrder(TreeNode *root) { /* Driver Code */ int main() { /* Initialize binary tree */ - // Use a specific function to convert an array into a binary tree + // Here we use a function to generate a binary tree directly from an array TreeNode *root = vectorToTree(vector{1, 2, 3, 4, 5, 6, 7}); cout << endl << "Initialize binary tree\n" << endl; printTree(root); /* Level-order traversal */ vector vec = levelOrder(root); - cout << endl << "Sequence of nodes in level-order traversal = "; + cout << endl << "Level-order traversal node print sequence = "; printVector(vec); return 0; diff --git a/en/codes/cpp/chapter_tree/binary_tree_dfs.cpp b/en/codes/cpp/chapter_tree/binary_tree_dfs.cpp index c74ede2fe..68053275e 100644 --- a/en/codes/cpp/chapter_tree/binary_tree_dfs.cpp +++ b/en/codes/cpp/chapter_tree/binary_tree_dfs.cpp @@ -6,10 +6,10 @@ #include "../utils/common.hpp" -// Initialize the list for storing traversal sequences +// Initialize list for storing traversal sequence vector vec; -/* Pre-order traversal */ +/* Preorder traversal */ void preOrder(TreeNode *root) { if (root == nullptr) return; @@ -19,7 +19,7 @@ void preOrder(TreeNode *root) { preOrder(root->right); } -/* In-order traversal */ +/* Inorder traversal */ void inOrder(TreeNode *root) { if (root == nullptr) return; @@ -29,7 +29,7 @@ void inOrder(TreeNode *root) { inOrder(root->right); } -/* Post-order traversal */ +/* Postorder traversal */ void postOrder(TreeNode *root) { if (root == nullptr) return; @@ -42,27 +42,27 @@ void postOrder(TreeNode *root) { /* Driver Code */ int main() { /* Initialize binary tree */ - // Use a specific function to convert an array into a binary tree + // Here we use a function to generate a binary tree directly from an array TreeNode *root = vectorToTree(vector{1, 2, 3, 4, 5, 6, 7}); cout << endl << "Initialize binary tree\n" << endl; printTree(root); - /* Pre-order traversal */ + /* Preorder traversal */ vec.clear(); preOrder(root); - cout << endl << "Sequence of nodes in pre-order traversal = "; + cout << endl << "Pre-order traversal node print sequence = "; printVector(vec); - /* In-order traversal */ + /* Inorder traversal */ vec.clear(); inOrder(root); - cout << endl << "Sequence of nodes in in-order traversal = "; + cout << endl << "In-order traversal node print sequence = "; printVector(vec); - /* Post-order traversal */ + /* Postorder traversal */ vec.clear(); postOrder(root); - cout << endl << "Sequence of nodes in post-order traversal = "; + cout << endl << "Post-order traversal node print sequence = "; printVector(vec); return 0; diff --git a/en/codes/cpp/utils/list_node.hpp b/en/codes/cpp/utils/list_node.hpp index b774a5610..33eadf4aa 100644 --- a/en/codes/cpp/utils/list_node.hpp +++ b/en/codes/cpp/utils/list_node.hpp @@ -30,7 +30,7 @@ ListNode *vecToLinkedList(vector list) { return dum->next; } -/* Free memory allocated to the linked list */ +/* Free memory allocated to linked list */ void freeMemoryLinkedList(ListNode *cur) { // Free memory ListNode *pre; diff --git a/en/codes/cpp/utils/print_utils.hpp b/en/codes/cpp/utils/print_utils.hpp index 08420244f..c29a99ad9 100644 --- a/en/codes/cpp/utils/print_utils.hpp +++ b/en/codes/cpp/utils/print_utils.hpp @@ -199,7 +199,7 @@ template void printDeque(deque deque) { } /* Print hash table */ -// Define template parameters TKey and TValue to specify the types of key-value pairs +// Define template parameters TKey and TValue to specify key-value pair types template void printHashMap(unordered_map map) { for (auto kv : map) { cout << kv.first << " -> " << kv.second << '\n'; @@ -216,12 +216,12 @@ template S &Container(priority_queue void printHeap(priority_queue &heap) { vector vec = Container(heap); - cout << "Array representation of the heap:"; + cout << "Heap array representation:"; printVector(vec); - cout << "Tree representation of the heap:" << endl; + cout << "Heap tree representation:" << endl; TreeNode *root = vectorToTree(vec); printTree(root); freeMemoryTree(root); diff --git a/en/codes/cpp/utils/tree_node.hpp b/en/codes/cpp/utils/tree_node.hpp index b4c2b6fc7..acd385dde 100644 --- a/en/codes/cpp/utils/tree_node.hpp +++ b/en/codes/cpp/utils/tree_node.hpp @@ -23,11 +23,11 @@ struct TreeNode { } }; -// For serialization encoding rules, refer to: +// For the serialization encoding rules, please refer to: // https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ -// Array representation of the binary tree: +// Array representation of binary tree: // [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] -// Linked list representation of the binary tree: +// Linked list representation of binary tree: // /——— 15 // /——— 7 // /——— 3 @@ -39,7 +39,7 @@ struct TreeNode { // \——— 4 // \——— 8 -/* Deserialize a list into a binary tree: Recursively */ +/* Deserialize a list into a binary tree: recursion */ TreeNode *vectorToTreeDFS(vector &arr, int i) { if (i < 0 || i >= arr.size() || arr[i] == INT_MAX) { return nullptr; @@ -55,7 +55,7 @@ TreeNode *vectorToTree(vector arr) { return vectorToTreeDFS(arr, 0); } -/* Serialize a binary tree into a list: Recursively */ +/* Serialize a binary tree into a list: recursion */ void treeToVecorDFS(TreeNode *root, int i, vector &res) { if (root == nullptr) return; @@ -74,7 +74,7 @@ vector treeToVecor(TreeNode *root) { return res; } -/* Free memory allocated to the binary tree */ +/* Free binary tree memory */ void freeMemoryTree(TreeNode *root) { if (root == nullptr) return; diff --git a/en/codes/cpp/utils/vertex.hpp b/en/codes/cpp/utils/vertex.hpp index 63d9a8146..c8c21eafd 100644 --- a/en/codes/cpp/utils/vertex.hpp +++ b/en/codes/cpp/utils/vertex.hpp @@ -17,7 +17,7 @@ struct Vertex { } }; -/* Input a list of values vals, return a list of vertices vets */ +/* Input value list vals, return vertex list vets */ vector valsToVets(vector vals) { vector vets; for (int val : vals) { @@ -26,7 +26,7 @@ vector valsToVets(vector vals) { return vets; } -/* Input a list of vertices vets, return a list of values vals */ +/* Input vertex list vets, return value list vals */ vector vetsToVals(vector vets) { vector vals; for (Vertex *vet : vets) { diff --git a/en/codes/csharp/.editorconfig b/en/codes/csharp/.editorconfig new file mode 100644 index 000000000..0a2c1df58 --- /dev/null +++ b/en/codes/csharp/.editorconfig @@ -0,0 +1,88 @@ +# CSharp formatting rules +[*.cs] +csharp_new_line_before_open_brace = none +csharp_new_line_before_else = false +csharp_new_line_before_catch = false +csharp_new_line_before_finally = false +csharp_indent_labels = one_less_than_current +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent + +# CS8981: The type name only contains lower-cased ascii characters. Such names may become reserved for the language. +dotnet_diagnostic.CS8981.severity = silent + +# IDE1006: Naming Styles +dotnet_diagnostic.IDE1006.severity = silent + +# CA1822: Mark members as static +dotnet_diagnostic.CA1822.severity = silent + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf + +# IDE0040: Add accessibility modifiers +dotnet_diagnostic.IDE0040.severity = silent + +# IDE0044: Add readonly modifier +dotnet_diagnostic.IDE0044.severity = silent diff --git a/en/codes/csharp/.gitignore b/en/codes/csharp/.gitignore new file mode 100644 index 000000000..a4b66a94a --- /dev/null +++ b/en/codes/csharp/.gitignore @@ -0,0 +1,5 @@ +.idea/ +.vs/ +obj/ +.Debug +bin/ diff --git a/en/codes/csharp/GlobalUsing.cs b/en/codes/csharp/GlobalUsing.cs new file mode 100644 index 000000000..402066ff4 --- /dev/null +++ b/en/codes/csharp/GlobalUsing.cs @@ -0,0 +1,3 @@ +global using NUnit.Framework; +global using hello_algo.utils; +global using System.Text; \ No newline at end of file diff --git a/en/codes/csharp/chapter_array_and_linkedlist/array.cs b/en/codes/csharp/chapter_array_and_linkedlist/array.cs new file mode 100644 index 000000000..a20a16f23 --- /dev/null +++ b/en/codes/csharp/chapter_array_and_linkedlist/array.cs @@ -0,0 +1,107 @@ +// File: array.cs +// Created Time: 2022-12-14 +// Author: mingXta (1195669834@qq.com) + +namespace hello_algo.chapter_array_and_linkedlist; + +public class array { + /* Random access to element */ + int RandomAccess(int[] nums) { + Random random = new(); + // Randomly select a number in interval [0, nums.Length) + int randomIndex = random.Next(nums.Length); + // Retrieve and return the random element + int randomNum = nums[randomIndex]; + return randomNum; + } + + /* Extend array length */ + int[] Extend(int[] nums, int enlarge) { + // Initialize an array with extended length + int[] res = new int[nums.Length + enlarge]; + // Copy all elements from the original array to the new array + for (int i = 0; i < nums.Length; i++) { + res[i] = nums[i]; + } + // Return the extended new array + return res; + } + + /* Insert element num at index index in the array */ + void Insert(int[] nums, int num, int index) { + // Move all elements at and after index index backward by one position + for (int i = nums.Length - 1; i > index; i--) { + nums[i] = nums[i - 1]; + } + // Assign num to the element at index index + nums[index] = num; + } + + /* Remove the element at index index */ + void Remove(int[] nums, int index) { + // Move all elements after index index forward by one position + for (int i = index; i < nums.Length - 1; i++) { + nums[i] = nums[i + 1]; + } + } + + /* Traverse array */ + void Traverse(int[] nums) { + int count = 0; + // Traverse array by index + for (int i = 0; i < nums.Length; i++) { + count += nums[i]; + } + // Direct traversal of array elements + foreach (int num in nums) { + count += num; + } + } + + /* Find the specified element in the array */ + int Find(int[] nums, int target) { + for (int i = 0; i < nums.Length; i++) { + if (nums[i] == target) + return i; + } + return -1; + } + + /* Helper function, convert array to string */ + string ToString(int[] nums) { + return string.Join(",", nums); + } + + + [Test] + public void Test() { + // Initialize array + int[] arr = new int[5]; + Console.WriteLine("Array arr = " + ToString(arr)); + int[] nums = [1, 3, 2, 5, 4]; + Console.WriteLine("Array nums = " + ToString(nums)); + + // Insert element + int randomNum = RandomAccess(nums); + Console.WriteLine("Get random element in nums " + randomNum); + + // Traverse array + nums = Extend(nums, 3); + Console.WriteLine("Extend array length to 8, resulting in nums = " + ToString(nums)); + + // Insert element + Insert(nums, 6, 3); + Console.WriteLine("Insert number 6 at index 3, resulting in nums = " + ToString(nums)); + + // Remove element + Remove(nums, 2); + Console.WriteLine("Remove element at index 2, resulting in nums = " + ToString(nums)); + + // Traverse array + Traverse(nums); + + // Find element + int index = Find(nums, 3); + Console.WriteLine("Find element 3 in nums, get index = " + index); + } +} diff --git a/en/codes/csharp/chapter_array_and_linkedlist/linked_list.cs b/en/codes/csharp/chapter_array_and_linkedlist/linked_list.cs new file mode 100644 index 000000000..4ffa92dc9 --- /dev/null +++ b/en/codes/csharp/chapter_array_and_linkedlist/linked_list.cs @@ -0,0 +1,80 @@ +// File: linked_list.cs +// Created Time: 2022-12-16 +// Author: mingXta (1195669834@qq.com) + +namespace hello_algo.chapter_array_and_linkedlist; + +public class linked_list { + /* Insert node P after node n0 in the linked list */ + void Insert(ListNode n0, ListNode P) { + ListNode? n1 = n0.next; + P.next = n1; + n0.next = P; + } + + /* Remove the first node after node n0 in the linked list */ + void Remove(ListNode n0) { + if (n0.next == null) + return; + // n0 -> P -> n1 + ListNode P = n0.next; + ListNode? n1 = P.next; + n0.next = n1; + } + + /* Access the node at index index in the linked list */ + ListNode? Access(ListNode? head, int index) { + for (int i = 0; i < index; i++) { + if (head == null) + return null; + head = head.next; + } + return head; + } + + /* Find the first node with value target in the linked list */ + int Find(ListNode? head, int target) { + int index = 0; + while (head != null) { + if (head.val == target) + return index; + head = head.next; + index++; + } + return -1; + } + + + [Test] + public void Test() { + // Initialize linked list + // Initialize each node + ListNode n0 = new(1); + ListNode n1 = new(3); + ListNode n2 = new(2); + ListNode n3 = new(5); + ListNode n4 = new(4); + // Build references between nodes + n0.next = n1; + n1.next = n2; + n2.next = n3; + n3.next = n4; + Console.WriteLine($"Initialized linked list is{n0}"); + + // Insert node + Insert(n0, new ListNode(0)); + Console.WriteLine($"Linked list after node insertion is{n0}"); + + // Remove node + Remove(n0); + Console.WriteLine($"Linked list after node deletion is{n0}"); + + // Access node + ListNode? node = Access(n0, 3); + Console.WriteLine($"Value of node at index 3 in linked list = {node?.val}"); + + // Search node + int index = Find(n0, 2); + Console.WriteLine($"Index of node with value 2 in linked list = {index}"); + } +} diff --git a/en/codes/csharp/chapter_array_and_linkedlist/list.cs b/en/codes/csharp/chapter_array_and_linkedlist/list.cs new file mode 100644 index 000000000..2d441c3ed --- /dev/null +++ b/en/codes/csharp/chapter_array_and_linkedlist/list.cs @@ -0,0 +1,66 @@ +/** + * File: list.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_array_and_linkedlist; + +public class list { + [Test] + public void Test() { + + /* Initialize list */ + int[] numbers = [1, 3, 2, 5, 4]; + List nums = [.. numbers]; + Console.WriteLine("List nums = " + string.Join(",", nums)); + + /* Update element */ + int num = nums[1]; + Console.WriteLine("Access element at index 1, get num = " + num); + + /* Add elements at the end */ + nums[1] = 0; + Console.WriteLine("Update element at index 1 to 0, resulting in nums = " + string.Join(",", nums)); + + /* Remove element */ + nums.Clear(); + Console.WriteLine("After clearing list, nums = " + string.Join(",", nums)); + + /* Direct traversal of list elements */ + nums.Add(1); + nums.Add(3); + nums.Add(2); + nums.Add(5); + nums.Add(4); + Console.WriteLine("After adding elements, nums = " + string.Join(",", nums)); + + /* Sort list */ + nums.Insert(3, 6); + Console.WriteLine("Insert number 6 at index 3, resulting in nums = " + string.Join(",", nums)); + + /* Remove element */ + nums.RemoveAt(3); + Console.WriteLine("Remove element at index 3, resulting in nums = " + string.Join(",", nums)); + + /* Traverse list by index */ + int count = 0; + for (int i = 0; i < nums.Count; i++) { + count += nums[i]; + } + /* Directly traverse list elements */ + count = 0; + foreach (int x in nums) { + count += x; + } + + /* Concatenate two lists */ + List nums1 = [6, 8, 7, 10, 9]; + nums.AddRange(nums1); + Console.WriteLine("Concatenate list nums1 to nums, resulting in nums = " + string.Join(",", nums)); + + /* Sort list */ + nums.Sort(); // After sorting, list elements are arranged from smallest to largest + Console.WriteLine("After sorting list, nums = " + string.Join(",", nums)); + } +} diff --git a/en/codes/csharp/chapter_array_and_linkedlist/my_list.cs b/en/codes/csharp/chapter_array_and_linkedlist/my_list.cs new file mode 100644 index 000000000..2c9892361 --- /dev/null +++ b/en/codes/csharp/chapter_array_and_linkedlist/my_list.cs @@ -0,0 +1,144 @@ +/** + * File: my_list.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_array_and_linkedlist; + +/* List class */ +class MyList { + private int[] arr; // Array (stores list elements) + private int arrCapacity = 10; // List capacity + private int arrSize = 0; // List length (current number of elements) + private readonly int extendRatio = 2; // Multiple by which the list capacity is extended each time + + /* Constructor */ + public MyList() { + arr = new int[arrCapacity]; + } + + /* Get list length (current number of elements) */ + public int Size() { + return arrSize; + } + + /* Get list capacity */ + public int Capacity() { + return arrCapacity; + } + + /* Update element */ + public int Get(int index) { + // If the index is out of bounds, throw an exception, as below + if (index < 0 || index >= arrSize) + throw new IndexOutOfRangeException("Index out of bounds"); + return arr[index]; + } + + /* Add elements at the end */ + public void Set(int index, int num) { + if (index < 0 || index >= arrSize) + throw new IndexOutOfRangeException("Index out of bounds"); + arr[index] = num; + } + + /* Direct traversal of list elements */ + public void Add(int num) { + // When the number of elements exceeds capacity, trigger the extension mechanism + if (arrSize == arrCapacity) + ExtendCapacity(); + arr[arrSize] = num; + // Update the number of elements + arrSize++; + } + + /* Sort list */ + public void Insert(int index, int num) { + if (index < 0 || index >= arrSize) + throw new IndexOutOfRangeException("Index out of bounds"); + // When the number of elements exceeds capacity, trigger the extension mechanism + if (arrSize == arrCapacity) + ExtendCapacity(); + // Move all elements after index index forward by one position + for (int j = arrSize - 1; j >= index; j--) { + arr[j + 1] = arr[j]; + } + arr[index] = num; + // Update the number of elements + arrSize++; + } + + /* Remove element */ + public int Remove(int index) { + if (index < 0 || index >= arrSize) + throw new IndexOutOfRangeException("Index out of bounds"); + int num = arr[index]; + // Move all elements after index forward by one position + for (int j = index; j < arrSize - 1; j++) { + arr[j] = arr[j + 1]; + } + // Update the number of elements + arrSize--; + // Return the removed element + return num; + } + + /* Driver Code */ + public void ExtendCapacity() { + // Create new array of length arrCapacity * extendRatio and copy original array to new array + Array.Resize(ref arr, arrCapacity * extendRatio); + // Add elements at the end + arrCapacity = arr.Length; + } + + /* Convert list to array */ + public int[] ToArray() { + // Elements enqueue + int[] arr = new int[arrSize]; + for (int i = 0; i < arrSize; i++) { + arr[i] = Get(i); + } + return arr; + } +} + +public class my_list { + [Test] + public void Test() { + /* Initialize list */ + MyList nums = new(); + /* Direct traversal of list elements */ + nums.Add(1); + nums.Add(3); + nums.Add(2); + nums.Add(5); + nums.Add(4); + Console.WriteLine("List nums = " + string.Join(",", nums.ToArray()) + + ", capacity = " + nums.Capacity() + ", length = " + nums.Size()); + + /* Sort list */ + nums.Insert(3, 6); + Console.WriteLine("Insert number 6 at index 3, resulting in nums = " + string.Join(",", nums.ToArray())); + + /* Remove element */ + nums.Remove(3); + Console.WriteLine("Remove element at index 3, resulting in nums = " + string.Join(",", nums.ToArray())); + + /* Update element */ + int num = nums.Get(1); + Console.WriteLine("Access element at index 1, get num = " + num); + + /* Add elements at the end */ + nums.Set(1, 0); + Console.WriteLine("Update element at index 1 to 0, resulting in nums = " + string.Join(",", nums.ToArray())); + + /* Test capacity expansion mechanism */ + for (int i = 0; i < 10; i++) { + // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + nums.Add(i); + } + Console.WriteLine("List nums after expansion = " + string.Join(",", nums.ToArray()) + + ", capacity = " + nums.Capacity() + ", length = " + nums.Size()); + } +} diff --git a/en/codes/csharp/chapter_backtracking/n_queens.cs b/en/codes/csharp/chapter_backtracking/n_queens.cs new file mode 100644 index 000000000..762758177 --- /dev/null +++ b/en/codes/csharp/chapter_backtracking/n_queens.cs @@ -0,0 +1,76 @@ +/** + * File: n_queens.cs + * Created Time: 2023-05-04 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_backtracking; + +public class n_queens { + /* Backtracking algorithm: N queens */ + void Backtrack(int row, int n, List> state, List>> res, + bool[] cols, bool[] diags1, bool[] diags2) { + // When all rows are placed, record the solution + if (row == n) { + List> copyState = []; + foreach (List sRow in state) { + copyState.Add(new List(sRow)); + } + res.Add(copyState); + return; + } + // Traverse all columns + for (int col = 0; col < n; col++) { + // Calculate the main diagonal and anti-diagonal corresponding to this cell + int diag1 = row - col + n - 1; + int diag2 = row + col; + // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { + // Attempt: place the queen in this cell + state[row][col] = "Q"; + cols[col] = diags1[diag1] = diags2[diag2] = true; + // Place the next row + Backtrack(row + 1, n, state, res, cols, diags1, diags2); + // Backtrack: restore this cell to an empty cell + state[row][col] = "#"; + cols[col] = diags1[diag1] = diags2[diag2] = false; + } + } + } + + /* Solve N queens */ + List>> NQueens(int n) { + // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + List> state = []; + for (int i = 0; i < n; i++) { + List row = []; + for (int j = 0; j < n; j++) { + row.Add("#"); + } + state.Add(row); + } + bool[] cols = new bool[n]; // Record whether there is a queen in the column + bool[] diags1 = new bool[2 * n - 1]; // Record whether there is a queen on the main diagonal + bool[] diags2 = new bool[2 * n - 1]; // Record whether there is a queen on the anti-diagonal + List>> res = []; + + Backtrack(0, n, state, res, cols, diags1, diags2); + + return res; + } + + [Test] + public void Test() { + int n = 4; + List>> res = NQueens(n); + + Console.WriteLine("Input board size is " + n); + Console.WriteLine("Total queen placement solutions: " + res.Count + " solutions"); + foreach (List> state in res) { + Console.WriteLine("--------------------"); + foreach (List row in state) { + PrintUtil.PrintList(row); + } + } + } +} diff --git a/en/codes/csharp/chapter_backtracking/permutations_i.cs b/en/codes/csharp/chapter_backtracking/permutations_i.cs new file mode 100644 index 000000000..99393f96e --- /dev/null +++ b/en/codes/csharp/chapter_backtracking/permutations_i.cs @@ -0,0 +1,53 @@ +/** + * File: permutations_i.cs + * Created Time: 2023-04-24 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_backtracking; + +public class permutations_i { + /* Backtracking algorithm: Permutations I */ + void Backtrack(List state, int[] choices, bool[] selected, List> res) { + // When the state length equals the number of elements, record the solution + if (state.Count == choices.Length) { + res.Add(new List(state)); + return; + } + // Traverse all choices + for (int i = 0; i < choices.Length; i++) { + int choice = choices[i]; + // Pruning: do not allow repeated selection of elements + if (!selected[i]) { + // Attempt: make choice, update state + selected[i] = true; + state.Add(choice); + // Proceed to the next round of selection + Backtrack(state, choices, selected, res); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + state.RemoveAt(state.Count - 1); + } + } + } + + /* Permutations I */ + List> PermutationsI(int[] nums) { + List> res = []; + Backtrack([], nums, new bool[nums.Length], res); + return res; + } + + [Test] + public void Test() { + int[] nums = [1, 2, 3]; + + List> res = PermutationsI(nums); + + Console.WriteLine("Input array nums = " + string.Join(", ", nums)); + Console.WriteLine("All permutations res = "); + foreach (List permutation in res) { + PrintUtil.PrintList(permutation); + } + } +} diff --git a/en/codes/csharp/chapter_backtracking/permutations_ii.cs b/en/codes/csharp/chapter_backtracking/permutations_ii.cs new file mode 100644 index 000000000..3b955071e --- /dev/null +++ b/en/codes/csharp/chapter_backtracking/permutations_ii.cs @@ -0,0 +1,55 @@ +/** + * File: permutations_ii.cs + * Created Time: 2023-04-24 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_backtracking; + +public class permutations_ii { + /* Backtracking algorithm: Permutations II */ + void Backtrack(List state, int[] choices, bool[] selected, List> res) { + // When the state length equals the number of elements, record the solution + if (state.Count == choices.Length) { + res.Add(new List(state)); + return; + } + // Traverse all choices + HashSet duplicated = []; + for (int i = 0; i < choices.Length; i++) { + int choice = choices[i]; + // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if (!selected[i] && !duplicated.Contains(choice)) { + // Attempt: make choice, update state + duplicated.Add(choice); // Record the selected element value + selected[i] = true; + state.Add(choice); + // Proceed to the next round of selection + Backtrack(state, choices, selected, res); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + state.RemoveAt(state.Count - 1); + } + } + } + + /* Permutations II */ + List> PermutationsII(int[] nums) { + List> res = []; + Backtrack([], nums, new bool[nums.Length], res); + return res; + } + + [Test] + public void Test() { + int[] nums = [1, 2, 2]; + + List> res = PermutationsII(nums); + + Console.WriteLine("Input array nums = " + string.Join(", ", nums)); + Console.WriteLine("All permutations res = "); + foreach (List permutation in res) { + PrintUtil.PrintList(permutation); + } + } +} diff --git a/en/codes/csharp/chapter_backtracking/preorder_traversal_i_compact.cs b/en/codes/csharp/chapter_backtracking/preorder_traversal_i_compact.cs new file mode 100644 index 000000000..02f21d533 --- /dev/null +++ b/en/codes/csharp/chapter_backtracking/preorder_traversal_i_compact.cs @@ -0,0 +1,37 @@ +/** + * File: preorder_traversal_i_compact.cs + * Created Time: 2023-04-17 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_backtracking; + +public class preorder_traversal_i_compact { + List res = []; + + /* Preorder traversal: Example 1 */ + void PreOrder(TreeNode? root) { + if (root == null) { + return; + } + if (root.val == 7) { + // Record solution + res.Add(root); + } + PreOrder(root.left); + PreOrder(root.right); + } + + [Test] + public void Test() { + TreeNode? root = TreeNode.ListToTree([1, 7, 3, 4, 5, 6, 7]); + Console.WriteLine("\nInitialize binary tree"); + PrintUtil.PrintTree(root); + + // Preorder traversal + PreOrder(root); + + Console.WriteLine("\nOutput all nodes with value 7"); + PrintUtil.PrintList(res.Select(p => p.val).ToList()); + } +} diff --git a/en/codes/csharp/chapter_backtracking/preorder_traversal_ii_compact.cs b/en/codes/csharp/chapter_backtracking/preorder_traversal_ii_compact.cs new file mode 100644 index 000000000..56b138210 --- /dev/null +++ b/en/codes/csharp/chapter_backtracking/preorder_traversal_ii_compact.cs @@ -0,0 +1,44 @@ +/** + * File: preorder_traversal_ii_compact.cs + * Created Time: 2023-04-17 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_backtracking; + +public class preorder_traversal_ii_compact { + List path = []; + List> res = []; + + /* Preorder traversal: Example 2 */ + void PreOrder(TreeNode? root) { + if (root == null) { + return; + } + // Attempt + path.Add(root); + if (root.val == 7) { + // Record solution + res.Add(new List(path)); + } + PreOrder(root.left); + PreOrder(root.right); + // Backtrack + path.RemoveAt(path.Count - 1); + } + + [Test] + public void Test() { + TreeNode? root = TreeNode.ListToTree([1, 7, 3, 4, 5, 6, 7]); + Console.WriteLine("\nInitialize binary tree"); + PrintUtil.PrintTree(root); + + // Preorder traversal + PreOrder(root); + + Console.WriteLine("\nOutput all paths from root node to node 7"); + foreach (List path in res) { + PrintUtil.PrintList(path.Select(p => p.val).ToList()); + } + } +} diff --git a/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_compact.cs b/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_compact.cs new file mode 100644 index 000000000..6b0317348 --- /dev/null +++ b/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_compact.cs @@ -0,0 +1,45 @@ +/** + * File: preorder_traversal_iii_compact.cs + * Created Time: 2023-04-17 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_backtracking; + +public class preorder_traversal_iii_compact { + List path = []; + List> res = []; + + /* Preorder traversal: Example 3 */ + void PreOrder(TreeNode? root) { + // Pruning + if (root == null || root.val == 3) { + return; + } + // Attempt + path.Add(root); + if (root.val == 7) { + // Record solution + res.Add(new List(path)); + } + PreOrder(root.left); + PreOrder(root.right); + // Backtrack + path.RemoveAt(path.Count - 1); + } + + [Test] + public void Test() { + TreeNode? root = TreeNode.ListToTree([1, 7, 3, 4, 5, 6, 7]); + Console.WriteLine("\nInitialize binary tree"); + PrintUtil.PrintTree(root); + + // Preorder traversal + PreOrder(root); + + Console.WriteLine("\nOutput all paths from root node to node 7, paths do not include nodes with value 3"); + foreach (List path in res) { + PrintUtil.PrintList(path.Select(p => p.val).ToList()); + } + } +} diff --git a/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_template.cs b/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_template.cs new file mode 100644 index 000000000..c25e67775 --- /dev/null +++ b/en/codes/csharp/chapter_backtracking/preorder_traversal_iii_template.cs @@ -0,0 +1,72 @@ +/** + * File: preorder_traversal_iii_template.cs + * Created Time: 2023-04-17 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_backtracking; + +public class preorder_traversal_iii_template { + /* Check if the current state is a solution */ + bool IsSolution(List state) { + return state.Count != 0 && state[^1].val == 7; + } + + /* Record solution */ + void RecordSolution(List state, List> res) { + res.Add(new List(state)); + } + + /* Check if the choice is valid under the current state */ + bool IsValid(List state, TreeNode choice) { + return choice != null && choice.val != 3; + } + + /* Update state */ + void MakeChoice(List state, TreeNode choice) { + state.Add(choice); + } + + /* Restore state */ + void UndoChoice(List state, TreeNode choice) { + state.RemoveAt(state.Count - 1); + } + + /* Backtracking algorithm: Example 3 */ + void Backtrack(List state, List choices, List> res) { + // Check if it is a solution + if (IsSolution(state)) { + // Record solution + RecordSolution(state, res); + } + // Traverse all choices + foreach (TreeNode choice in choices) { + // Pruning: check if the choice is valid + if (IsValid(state, choice)) { + // Attempt: make choice, update state + MakeChoice(state, choice); + // Proceed to the next round of selection + Backtrack(state, [choice.left!, choice.right!], res); + // Backtrack: undo choice, restore to previous state + UndoChoice(state, choice); + } + } + } + + [Test] + public void Test() { + TreeNode? root = TreeNode.ListToTree([1, 7, 3, 4, 5, 6, 7]); + Console.WriteLine("\nInitialize binary tree"); + PrintUtil.PrintTree(root); + + // Backtracking algorithm + List> res = []; + List choices = [root!]; + Backtrack([], choices, res); + + Console.WriteLine("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3"); + foreach (List path in res) { + PrintUtil.PrintList(path.Select(p => p.val).ToList()); + } + } +} diff --git a/en/codes/csharp/chapter_backtracking/subset_sum_i.cs b/en/codes/csharp/chapter_backtracking/subset_sum_i.cs new file mode 100644 index 000000000..86ff301df --- /dev/null +++ b/en/codes/csharp/chapter_backtracking/subset_sum_i.cs @@ -0,0 +1,55 @@ +/** +* File: subset_sum_i.cs +* Created Time: 2023-06-25 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_backtracking; + +public class subset_sum_i { + /* Backtracking algorithm: Subset sum I */ + void Backtrack(List state, int target, int[] choices, int start, List> res) { + // When the subset sum equals target, record the solution + if (target == 0) { + res.Add(new List(state)); + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + for (int i = start; i < choices.Length; i++) { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if (target - choices[i] < 0) { + break; + } + // Attempt: make choice, update target, start + state.Add(choices[i]); + // Proceed to the next round of selection + Backtrack(state, target - choices[i], choices, i, res); + // Backtrack: undo choice, restore to previous state + state.RemoveAt(state.Count - 1); + } + } + + /* Solve subset sum I */ + List> SubsetSumI(int[] nums, int target) { + List state = []; // State (subset) + Array.Sort(nums); // Sort nums + int start = 0; // Start point for traversal + List> res = []; // Result list (subset list) + Backtrack(state, target, nums, start, res); + return res; + } + + [Test] + public void Test() { + int[] nums = [3, 4, 5]; + int target = 9; + List> res = SubsetSumI(nums, target); + Console.WriteLine("Input array nums = " + string.Join(", ", nums) + ", target = " + target); + Console.WriteLine("All subsets with sum equal to " + target + " are res = "); + foreach (var subset in res) { + PrintUtil.PrintList(subset); + } + } +} diff --git a/en/codes/csharp/chapter_backtracking/subset_sum_i_naive.cs b/en/codes/csharp/chapter_backtracking/subset_sum_i_naive.cs new file mode 100644 index 000000000..6fc11a833 --- /dev/null +++ b/en/codes/csharp/chapter_backtracking/subset_sum_i_naive.cs @@ -0,0 +1,53 @@ +/** +* File: subset_sum_i_naive.cs +* Created Time: 2023-06-25 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_backtracking; + +public class subset_sum_i_naive { + /* Backtracking algorithm: Subset sum I */ + void Backtrack(List state, int target, int total, int[] choices, List> res) { + // When the subset sum equals target, record the solution + if (total == target) { + res.Add(new List(state)); + return; + } + // Traverse all choices + for (int i = 0; i < choices.Length; i++) { + // Pruning: if the subset sum exceeds target, skip this choice + if (total + choices[i] > target) { + continue; + } + // Attempt: make choice, update element sum total + state.Add(choices[i]); + // Proceed to the next round of selection + Backtrack(state, target, total + choices[i], choices, res); + // Backtrack: undo choice, restore to previous state + state.RemoveAt(state.Count - 1); + } + } + + /* Solve subset sum I (including duplicate subsets) */ + List> SubsetSumINaive(int[] nums, int target) { + List state = []; // State (subset) + int total = 0; // Subset sum + List> res = []; // Result list (subset list) + Backtrack(state, target, total, nums, res); + return res; + } + + [Test] + public void Test() { + int[] nums = [3, 4, 5]; + int target = 9; + List> res = SubsetSumINaive(nums, target); + Console.WriteLine("Input array nums = " + string.Join(", ", nums) + ", target = " + target); + Console.WriteLine("All subsets with sum equal to " + target + " are res = "); + foreach (var subset in res) { + PrintUtil.PrintList(subset); + } + Console.WriteLine("Please note that this method outputs results containing duplicate sets"); + } +} diff --git a/en/codes/csharp/chapter_backtracking/subset_sum_ii.cs b/en/codes/csharp/chapter_backtracking/subset_sum_ii.cs new file mode 100644 index 000000000..05e4eeaf0 --- /dev/null +++ b/en/codes/csharp/chapter_backtracking/subset_sum_ii.cs @@ -0,0 +1,60 @@ +/** +* File: subset_sum_ii.cs +* Created Time: 2023-06-25 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_backtracking; + +public class subset_sum_ii { + /* Backtracking algorithm: Subset sum II */ + void Backtrack(List state, int target, int[] choices, int start, List> res) { + // When the subset sum equals target, record the solution + if (target == 0) { + res.Add(new List(state)); + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + for (int i = start; i < choices.Length; i++) { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if (target - choices[i] < 0) { + break; + } + // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + if (i > start && choices[i] == choices[i - 1]) { + continue; + } + // Attempt: make choice, update target, start + state.Add(choices[i]); + // Proceed to the next round of selection + Backtrack(state, target - choices[i], choices, i + 1, res); + // Backtrack: undo choice, restore to previous state + state.RemoveAt(state.Count - 1); + } + } + + /* Solve subset sum II */ + List> SubsetSumII(int[] nums, int target) { + List state = []; // State (subset) + Array.Sort(nums); // Sort nums + int start = 0; // Start point for traversal + List> res = []; // Result list (subset list) + Backtrack(state, target, nums, start, res); + return res; + } + + [Test] + public void Test() { + int[] nums = [4, 4, 5]; + int target = 9; + List> res = SubsetSumII(nums, target); + Console.WriteLine("Input array nums = " + string.Join(", ", nums) + ", target = " + target); + Console.WriteLine("All subsets with sum equal to " + target + " are res = "); + foreach (var subset in res) { + PrintUtil.PrintList(subset); + } + } +} diff --git a/en/codes/csharp/chapter_computational_complexity/iteration.cs b/en/codes/csharp/chapter_computational_complexity/iteration.cs new file mode 100644 index 000000000..18e37e584 --- /dev/null +++ b/en/codes/csharp/chapter_computational_complexity/iteration.cs @@ -0,0 +1,77 @@ +/** +* File: iteration.cs +* Created Time: 2023-08-28 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_computational_complexity; + +public class iteration { + /* for loop */ + int ForLoop(int n) { + int res = 0; + // Sum 1, 2, ..., n-1, n + for (int i = 1; i <= n; i++) { + res += i; + } + return res; + } + + /* while loop */ + int WhileLoop(int n) { + int res = 0; + int i = 1; // Initialize condition variable + // Sum 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i += 1; // Update condition variable + } + return res; + } + + /* while loop (two updates) */ + int WhileLoopII(int n) { + int res = 0; + int i = 1; // Initialize condition variable + // Sum 1, 4, 10, ... + while (i <= n) { + res += i; + // Update condition variable + i += 1; + i *= 2; + } + return res; + } + + /* Nested for loop */ + string NestedForLoop(int n) { + StringBuilder res = new(); + // Loop i = 1, 2, ..., n-1, n + for (int i = 1; i <= n; i++) { + // Loop j = 1, 2, ..., n-1, n + for (int j = 1; j <= n; j++) { + res.Append($"({i}, {j}), "); + } + } + return res.ToString(); + } + + /* Driver Code */ + [Test] + public void Test() { + int n = 5; + int res; + + res = ForLoop(n); + Console.WriteLine("\nfor loop sum result res = " + res); + + res = WhileLoop(n); + Console.WriteLine("\nwhile loop sum result res = " + res); + + res = WhileLoopII(n); + Console.WriteLine("\nwhile loop (two updates) sum result res = " + res); + + string resStr = NestedForLoop(n); + Console.WriteLine("\nDouble for loop traversal result " + resStr); + } +} diff --git a/en/codes/csharp/chapter_computational_complexity/recursion.cs b/en/codes/csharp/chapter_computational_complexity/recursion.cs new file mode 100644 index 000000000..80e4fed24 --- /dev/null +++ b/en/codes/csharp/chapter_computational_complexity/recursion.cs @@ -0,0 +1,78 @@ +/** +* File: recursion.cs +* Created Time: 2023-08-28 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_computational_complexity; + +public class recursion { + /* Recursion */ + int Recur(int n) { + // Termination condition + if (n == 1) + return 1; + // Recurse: recursive call + int res = Recur(n - 1); + // Return: return result + return n + res; + } + + /* Simulate recursion using iteration */ + int ForLoopRecur(int n) { + // Use an explicit stack to simulate the system call stack + Stack stack = new(); + int res = 0; + // Recurse: recursive call + for (int i = n; i > 0; i--) { + // Simulate "recurse" with "push" + stack.Push(i); + } + // Return: return result + while (stack.Count > 0) { + // Simulate "return" with "pop" + res += stack.Pop(); + } + // res = 1+2+3+...+n + return res; + } + + /* Tail recursion */ + int TailRecur(int n, int res) { + // Termination condition + if (n == 0) + return res; + // Tail recursive call + return TailRecur(n - 1, res + n); + } + + /* Fibonacci sequence: recursion */ + int Fib(int n) { + // Termination condition f(1) = 0, f(2) = 1 + if (n == 1 || n == 2) + return n - 1; + // Recursive call f(n) = f(n-1) + f(n-2) + int res = Fib(n - 1) + Fib(n - 2); + // Return result f(n) + return res; + } + + /* Driver Code */ + [Test] + public void Test() { + int n = 5; + int res; + + res = Recur(n); + Console.WriteLine("\nRecursive function sum result res = " + res); + + res = ForLoopRecur(n); + Console.WriteLine("\nUsing iteration to simulate recursive sum result res = " + res); + + res = TailRecur(n, 0); + Console.WriteLine("\nTail recursive function sum result res = " + res); + + res = Fib(n); + Console.WriteLine("\nThe " + n + "th term of the Fibonacci sequence is " + res); + } +} diff --git a/en/codes/csharp/chapter_computational_complexity/space_complexity.cs b/en/codes/csharp/chapter_computational_complexity/space_complexity.cs new file mode 100644 index 000000000..c2da20ea7 --- /dev/null +++ b/en/codes/csharp/chapter_computational_complexity/space_complexity.cs @@ -0,0 +1,104 @@ +/** + * File: space_complexity.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_computational_complexity; + +public class space_complexity { + /* Function */ + int Function() { + // Perform some operations + return 0; + } + + /* Constant order */ + void Constant(int n) { + // Constants, variables, objects occupy O(1) space + int a = 0; + int b = 0; + int[] nums = new int[10000]; + ListNode node = new(0); + // Variables in the loop occupy O(1) space + for (int i = 0; i < n; i++) { + int c = 0; + } + // Functions in the loop occupy O(1) space + for (int i = 0; i < n; i++) { + Function(); + } + } + + /* Linear order */ + void Linear(int n) { + // Array of length n uses O(n) space + int[] nums = new int[n]; + // A list of length n occupies O(n) space + List nodes = []; + for (int i = 0; i < n; i++) { + nodes.Add(new ListNode(i)); + } + // A hash table of length n occupies O(n) space + Dictionary map = []; + for (int i = 0; i < n; i++) { + map.Add(i, i.ToString()); + } + } + + /* Linear order (recursive implementation) */ + void LinearRecur(int n) { + Console.WriteLine("Recursion n = " + n); + if (n == 1) return; + LinearRecur(n - 1); + } + + /* Exponential order */ + void Quadratic(int n) { + // Matrix uses O(n^2) space + int[,] numMatrix = new int[n, n]; + // 2D list uses O(n^2) space + List> numList = []; + for (int i = 0; i < n; i++) { + List tmp = []; + for (int j = 0; j < n; j++) { + tmp.Add(0); + } + numList.Add(tmp); + } + } + + /* Quadratic order (recursive implementation) */ + int QuadraticRecur(int n) { + if (n <= 0) return 0; + int[] nums = new int[n]; + Console.WriteLine("Recursion n = " + n + ", nums length = " + nums.Length); + return QuadraticRecur(n - 1); + } + + /* Driver Code */ + TreeNode? BuildTree(int n) { + if (n == 0) return null; + TreeNode root = new(0) { + left = BuildTree(n - 1), + right = BuildTree(n - 1) + }; + return root; + } + + [Test] + public void Test() { + int n = 5; + // Constant order + Constant(n); + // Linear order + Linear(n); + LinearRecur(n); + // Exponential order + Quadratic(n); + QuadraticRecur(n); + // Exponential order + TreeNode? root = BuildTree(n); + PrintUtil.PrintTree(root); + } +} diff --git a/en/codes/csharp/chapter_computational_complexity/time_complexity.cs b/en/codes/csharp/chapter_computational_complexity/time_complexity.cs new file mode 100644 index 000000000..f26c5d051 --- /dev/null +++ b/en/codes/csharp/chapter_computational_complexity/time_complexity.cs @@ -0,0 +1,195 @@ +/** + * File: time_complexity.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_computational_complexity; + +public class time_complexity { + void Algorithm(int n) { + int a = 1; // +0 (technique 1) + a += n; // +0 (technique 1) + // +n (technique 2) + for (int i = 0; i < 5 * n + 1; i++) { + Console.WriteLine(0); + } + // +n*n (technique 3) + for (int i = 0; i < 2 * n; i++) { + for (int j = 0; j < n + 1; j++) { + Console.WriteLine(0); + } + } + } + + // Algorithm A time complexity: constant + void AlgorithmA(int n) { + Console.WriteLine(0); + } + + // Algorithm B time complexity: linear + void AlgorithmB(int n) { + for (int i = 0; i < n; i++) { + Console.WriteLine(0); + } + } + + // Algorithm C time complexity: constant + void AlgorithmC(int n) { + for (int i = 0; i < 1000000; i++) { + Console.WriteLine(0); + } + } + + /* Constant order */ + int Constant(int n) { + int count = 0; + int size = 100000; + for (int i = 0; i < size; i++) + count++; + return count; + } + + /* Linear order */ + int Linear(int n) { + int count = 0; + for (int i = 0; i < n; i++) + count++; + return count; + } + + /* Linear order (traversing array) */ + int ArrayTraversal(int[] nums) { + int count = 0; + // Number of iterations is proportional to the array length + foreach (int num in nums) { + count++; + } + return count; + } + + /* Exponential order */ + int Quadratic(int n) { + int count = 0; + // Number of iterations is quadratically related to the data size n + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; + } + + /* Quadratic order (bubble sort) */ + int BubbleSort(int[] nums) { + int count = 0; // Counter + // Outer loop: unsorted range is [0, i] + for (int i = nums.Length - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]); + count += 3; // Element swap includes 3 unit operations + } + } + } + return count; + } + + /* Exponential order (loop implementation) */ + int Exponential(int n) { + int count = 0, bas = 1; + // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + for (int i = 0; i < n; i++) { + for (int j = 0; j < bas; j++) { + count++; + } + bas *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; + } + + /* Exponential order (recursive implementation) */ + int ExpRecur(int n) { + if (n == 1) return 1; + return ExpRecur(n - 1) + ExpRecur(n - 1) + 1; + } + + /* Logarithmic order (loop implementation) */ + int Logarithmic(int n) { + int count = 0; + while (n > 1) { + n /= 2; + count++; + } + return count; + } + + /* Logarithmic order (recursive implementation) */ + int LogRecur(int n) { + if (n <= 1) return 0; + return LogRecur(n / 2) + 1; + } + + /* Linearithmic order */ + int LinearLogRecur(int n) { + if (n <= 1) return 1; + int count = LinearLogRecur(n / 2) + LinearLogRecur(n / 2); + for (int i = 0; i < n; i++) { + count++; + } + return count; + } + + /* Factorial order (recursive implementation) */ + int FactorialRecur(int n) { + if (n == 0) return 1; + int count = 0; + // Split from 1 into n + for (int i = 0; i < n; i++) { + count += FactorialRecur(n - 1); + } + return count; + } + + [Test] + public void Test() { + // You can modify n to run and observe the trend of the number of operations for various complexities + int n = 8; + Console.WriteLine("Input data size n = " + n); + + int count = Constant(n); + Console.WriteLine("Constant order operation count = " + count); + + count = Linear(n); + Console.WriteLine("Linear order operation count = " + count); + count = ArrayTraversal(new int[n]); + Console.WriteLine("Linear order (array traversal) operation count = " + count); + + count = Quadratic(n); + Console.WriteLine("Quadratic order operation count = " + count); + int[] nums = new int[n]; + for (int i = 0; i < n; i++) + nums[i] = n - i; // [n,n-1,...,2,1] + count = BubbleSort(nums); + Console.WriteLine("Quadratic order (bubble sort) operation count = " + count); + + count = Exponential(n); + Console.WriteLine("Exponential order (loop implementation) operation count = " + count); + count = ExpRecur(n); + Console.WriteLine("Exponential order (recursive implementation) operation count = " + count); + + count = Logarithmic(n); + Console.WriteLine("Logarithmic order (loop implementation) operation count = " + count); + count = LogRecur(n); + Console.WriteLine("Logarithmic order (recursive implementation) operation count = " + count); + + count = LinearLogRecur(n); + Console.WriteLine("Linearithmic order (recursive implementation) operation count = " + count); + + count = FactorialRecur(n); + Console.WriteLine("Factorial order (recursive implementation) operation count = " + count); + } +} diff --git a/en/codes/csharp/chapter_computational_complexity/worst_best_time_complexity.cs b/en/codes/csharp/chapter_computational_complexity/worst_best_time_complexity.cs new file mode 100644 index 000000000..0cc737b73 --- /dev/null +++ b/en/codes/csharp/chapter_computational_complexity/worst_best_time_complexity.cs @@ -0,0 +1,49 @@ +/** + * File: worst_best_time_complexity.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_computational_complexity; + +public class worst_best_time_complexity { + /* Generate an array with elements { 1, 2, ..., n }, order shuffled */ + int[] RandomNumbers(int n) { + int[] nums = new int[n]; + // Generate array nums = { 1, 2, 3, ..., n } + for (int i = 0; i < n; i++) { + nums[i] = i + 1; + } + + // Randomly shuffle array elements + for (int i = 0; i < nums.Length; i++) { + int index = new Random().Next(i, nums.Length); + (nums[i], nums[index]) = (nums[index], nums[i]); + } + return nums; + } + + /* Find the index of number 1 in array nums */ + int FindOne(int[] nums) { + for (int i = 0; i < nums.Length; i++) { + // When element 1 is at the head of the array, best time complexity O(1) is achieved + // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + if (nums[i] == 1) + return i; + } + return -1; + } + + + /* Driver Code */ + [Test] + public void Test() { + for (int i = 0; i < 10; i++) { + int n = 100; + int[] nums = RandomNumbers(n); + int index = FindOne(nums); + Console.WriteLine("\nArray [ 1, 2, ..., n ] after shuffling = " + string.Join(",", nums)); + Console.WriteLine("Index of number 1 is " + index); + } + } +} diff --git a/en/codes/csharp/chapter_divide_and_conquer/binary_search_recur.cs b/en/codes/csharp/chapter_divide_and_conquer/binary_search_recur.cs new file mode 100644 index 000000000..bf97c0886 --- /dev/null +++ b/en/codes/csharp/chapter_divide_and_conquer/binary_search_recur.cs @@ -0,0 +1,46 @@ +/** +* File: binary_search_recur.cs +* Created Time: 2023-07-18 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_divide_and_conquer; + +public class binary_search_recur { + /* Binary search: problem f(i, j) */ + int DFS(int[] nums, int target, int i, int j) { + // If the interval is empty, it means there is no target element, return -1 + if (i > j) { + return -1; + } + // Calculate the midpoint index m + int m = (i + j) / 2; + if (nums[m] < target) { + // Recursion subproblem f(m+1, j) + return DFS(nums, target, m + 1, j); + } else if (nums[m] > target) { + // Recursion subproblem f(i, m-1) + return DFS(nums, target, i, m - 1); + } else { + // Found the target element, return its index + return m; + } + } + + /* Binary search */ + int BinarySearch(int[] nums, int target) { + int n = nums.Length; + // Solve the problem f(0, n-1) + return DFS(nums, target, 0, n - 1); + } + + [Test] + public void Test() { + int target = 6; + int[] nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; + + // Binary search (closed interval on both sides) + int index = BinarySearch(nums, target); + Console.WriteLine("Index of target element 6 = " + index); + } +} diff --git a/en/codes/csharp/chapter_divide_and_conquer/build_tree.cs b/en/codes/csharp/chapter_divide_and_conquer/build_tree.cs new file mode 100644 index 000000000..ee06ced60 --- /dev/null +++ b/en/codes/csharp/chapter_divide_and_conquer/build_tree.cs @@ -0,0 +1,49 @@ +/** +* File: build_tree.cs +* Created Time: 2023-07-18 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_divide_and_conquer; + +public class build_tree { + /* Build binary tree: divide and conquer */ + TreeNode? DFS(int[] preorder, Dictionary inorderMap, int i, int l, int r) { + // Terminate when the subtree interval is empty + if (r - l < 0) + return null; + // Initialize the root node + TreeNode root = new(preorder[i]); + // Query m to divide the left and right subtrees + int m = inorderMap[preorder[i]]; + // Subproblem: build the left subtree + root.left = DFS(preorder, inorderMap, i + 1, l, m - 1); + // Subproblem: build the right subtree + root.right = DFS(preorder, inorderMap, i + 1 + m - l, m + 1, r); + // Return the root node + return root; + } + + /* Build binary tree */ + TreeNode? BuildTree(int[] preorder, int[] inorder) { + // Initialize hash map, storing the mapping from inorder elements to indices + Dictionary inorderMap = []; + for (int i = 0; i < inorder.Length; i++) { + inorderMap.TryAdd(inorder[i], i); + } + TreeNode? root = DFS(preorder, inorderMap, 0, 0, inorder.Length - 1); + return root; + } + + [Test] + public void Test() { + int[] preorder = [3, 9, 2, 1, 7]; + int[] inorder = [9, 3, 1, 2, 7]; + Console.WriteLine("Preorder traversal = " + string.Join(", ", preorder)); + Console.WriteLine("Inorder traversal = " + string.Join(", ", inorder)); + + TreeNode? root = BuildTree(preorder, inorder); + Console.WriteLine("The constructed binary tree is:"); + PrintUtil.PrintTree(root); + } +} diff --git a/en/codes/csharp/chapter_divide_and_conquer/hanota.cs b/en/codes/csharp/chapter_divide_and_conquer/hanota.cs new file mode 100644 index 000000000..a6a1cd29e --- /dev/null +++ b/en/codes/csharp/chapter_divide_and_conquer/hanota.cs @@ -0,0 +1,59 @@ +/** +* File: hanota.cs +* Created Time: 2023-07-18 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_divide_and_conquer; + +public class hanota { + /* Move a disk */ + void Move(List src, List tar) { + // Take out a disk from the top of src + int pan = src[^1]; + src.RemoveAt(src.Count - 1); + // Place the disk on top of tar + tar.Add(pan); + } + + /* Solve the Tower of Hanoi problem f(i) */ + void DFS(int i, List src, List buf, List tar) { + // If there is only one disk left in src, move it directly to tar + if (i == 1) { + Move(src, tar); + return; + } + // Subproblem f(i-1): move the top i-1 disks from src to buf using tar + DFS(i - 1, src, tar, buf); + // Subproblem f(1): move the remaining disk from src to tar + Move(src, tar); + // Subproblem f(i-1): move the top i-1 disks from buf to tar using src + DFS(i - 1, buf, src, tar); + } + + /* Solve the Tower of Hanoi problem */ + void SolveHanota(List A, List B, List C) { + int n = A.Count; + // Move the top n disks from A to C using B + DFS(n, A, B, C); + } + + [Test] + public void Test() { + // The tail of the list is the top of the rod + List A = [5, 4, 3, 2, 1]; + List B = []; + List C = []; + Console.WriteLine("In initial state:"); + Console.WriteLine("A = " + string.Join(", ", A)); + Console.WriteLine("B = " + string.Join(", ", B)); + Console.WriteLine("C = " + string.Join(", ", C)); + + SolveHanota(A, B, C); + + Console.WriteLine("After disk movement is complete:"); + Console.WriteLine("A = " + string.Join(", ", A)); + Console.WriteLine("B = " + string.Join(", ", B)); + Console.WriteLine("C = " + string.Join(", ", C)); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_backtrack.cs b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_backtrack.cs new file mode 100644 index 000000000..8e2b4fbe7 --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_backtrack.cs @@ -0,0 +1,41 @@ +/** +* File: climbing_stairs_backtrack.cs +* Created Time: 2023-06-30 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class climbing_stairs_backtrack { + /* Backtracking */ + void Backtrack(List choices, int state, int n, List res) { + // When climbing to the n-th stair, add 1 to the solution count + if (state == n) + res[0]++; + // Traverse all choices + foreach (int choice in choices) { + // Pruning: not allowed to go beyond the n-th stair + if (state + choice > n) + continue; + // Attempt: make choice, update state + Backtrack(choices, state + choice, n, res); + // Backtrack + } + } + + /* Climbing stairs: Backtracking */ + int ClimbingStairsBacktrack(int n) { + List choices = [1, 2]; // Can choose to climb up 1 or 2 stairs + int state = 0; // Start climbing from the 0-th stair + List res = [0]; // Use res[0] to record the solution count + Backtrack(choices, state, n, res); + return res[0]; + } + + [Test] + public void Test() { + int n = 9; + int res = ClimbingStairsBacktrack(n); + Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cs b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cs new file mode 100644 index 000000000..18ad6a88b --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cs @@ -0,0 +1,36 @@ +/** +* File: climbing_stairs_constraint_dp.cs +* Created Time: 2023-07-03 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class climbing_stairs_constraint_dp { + /* Climbing stairs with constraint: Dynamic programming */ + int ClimbingStairsConstraintDP(int n) { + if (n == 1 || n == 2) { + return 1; + } + // Initialize dp table, used to store solutions to subproblems + int[,] dp = new int[n + 1, 3]; + // Initial state: preset the solution to the smallest subproblem + dp[1, 1] = 1; + dp[1, 2] = 0; + dp[2, 1] = 0; + dp[2, 2] = 1; + // State transition: gradually solve larger subproblems from smaller ones + for (int i = 3; i <= n; i++) { + dp[i, 1] = dp[i - 1, 2]; + dp[i, 2] = dp[i - 2, 1] + dp[i - 2, 2]; + } + return dp[n, 1] + dp[n, 2]; + } + + [Test] + public void Test() { + int n = 9; + int res = ClimbingStairsConstraintDP(n); + Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs.cs b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs.cs new file mode 100644 index 000000000..017bbca79 --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs.cs @@ -0,0 +1,31 @@ +/** +* File: climbing_stairs_dfs.cs +* Created Time: 2023-06-30 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class climbing_stairs_dfs { + /* Search */ + int DFS(int i) { + // Known dp[1] and dp[2], return them + if (i == 1 || i == 2) + return i; + // dp[i] = dp[i-1] + dp[i-2] + int count = DFS(i - 1) + DFS(i - 2); + return count; + } + + /* Climbing stairs: Search */ + int ClimbingStairsDFS(int n) { + return DFS(n); + } + + [Test] + public void Test() { + int n = 9; + int res = ClimbingStairsDFS(n); + Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cs b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cs new file mode 100644 index 000000000..06dc5c9c7 --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cs @@ -0,0 +1,39 @@ +/** +* File: climbing_stairs_dfs_mem.cs +* Created Time: 2023-06-30 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class climbing_stairs_dfs_mem { + /* Memoization search */ + int DFS(int i, int[] mem) { + // Known dp[1] and dp[2], return them + if (i == 1 || i == 2) + return i; + // If record dp[i] exists, return it directly + if (mem[i] != -1) + return mem[i]; + // dp[i] = dp[i-1] + dp[i-2] + int count = DFS(i - 1, mem) + DFS(i - 2, mem); + // Record dp[i] + mem[i] = count; + return count; + } + + /* Climbing stairs: Memoization search */ + int ClimbingStairsDFSMem(int n) { + // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + int[] mem = new int[n + 1]; + Array.Fill(mem, -1); + return DFS(n, mem); + } + + [Test] + public void Test() { + int n = 9; + int res = ClimbingStairsDFSMem(n); + Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dp.cs b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dp.cs new file mode 100644 index 000000000..57489392a --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/climbing_stairs_dp.cs @@ -0,0 +1,49 @@ +/** +* File: climbing_stairs_dp.cs +* Created Time: 2023-06-30 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class climbing_stairs_dp { + /* Climbing stairs: Dynamic programming */ + int ClimbingStairsDP(int n) { + if (n == 1 || n == 2) + return n; + // Initialize dp table, used to store solutions to subproblems + int[] dp = new int[n + 1]; + // Initial state: preset the solution to the smallest subproblem + dp[1] = 1; + dp[2] = 2; + // State transition: gradually solve larger subproblems from smaller ones + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; + } + + /* Climbing stairs: Space-optimized dynamic programming */ + int ClimbingStairsDPComp(int n) { + if (n == 1 || n == 2) + return n; + int a = 1, b = 2; + for (int i = 3; i <= n; i++) { + int tmp = b; + b = a + b; + a = tmp; + } + return b; + } + + [Test] + public void Test() { + int n = 9; + + int res = ClimbingStairsDP(n); + Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + + res = ClimbingStairsDPComp(n); + Console.WriteLine($"Climbing {n} stairs has {res} solutions"); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/coin_change.cs b/en/codes/csharp/chapter_dynamic_programming/coin_change.cs new file mode 100644 index 000000000..13570b358 --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/coin_change.cs @@ -0,0 +1,71 @@ +/** +* File: coin_change.cs +* Created Time: 2023-07-12 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class coin_change { + /* Coin change: Dynamic programming */ + int CoinChangeDP(int[] coins, int amt) { + int n = coins.Length; + int MAX = amt + 1; + // Initialize dp table + int[,] dp = new int[n + 1, amt + 1]; + // State transition: first row and first column + for (int a = 1; a <= amt; a++) { + dp[0, a] = MAX; + } + // State transition: rest of the rows and columns + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i, a] = dp[i - 1, a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[i, a] = Math.Min(dp[i - 1, a], dp[i, a - coins[i - 1]] + 1); + } + } + } + return dp[n, amt] != MAX ? dp[n, amt] : -1; + } + + /* Coin change: Space-optimized dynamic programming */ + int CoinChangeDPComp(int[] coins, int amt) { + int n = coins.Length; + int MAX = amt + 1; + // Initialize dp table + int[] dp = new int[amt + 1]; + Array.Fill(dp, MAX); + dp[0] = 0; + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[a] = Math.Min(dp[a], dp[a - coins[i - 1]] + 1); + } + } + } + return dp[amt] != MAX ? dp[amt] : -1; + } + + [Test] + public void Test() { + int[] coins = [1, 2, 5]; + int amt = 4; + + // Dynamic programming + int res = CoinChangeDP(coins, amt); + Console.WriteLine("Minimum number of coins needed to make target amount is " + res); + + // Space-optimized dynamic programming + res = CoinChangeDPComp(coins, amt); + Console.WriteLine("Minimum number of coins needed to make target amount is " + res); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/coin_change_ii.cs b/en/codes/csharp/chapter_dynamic_programming/coin_change_ii.cs new file mode 100644 index 000000000..1dc91adce --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/coin_change_ii.cs @@ -0,0 +1,68 @@ +/** +* File: coin_change_ii.cs +* Created Time: 2023-07-12 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class coin_change_ii { + /* Coin change II: Dynamic programming */ + int CoinChangeIIDP(int[] coins, int amt) { + int n = coins.Length; + // Initialize dp table + int[,] dp = new int[n + 1, amt + 1]; + // Initialize first column + for (int i = 0; i <= n; i++) { + dp[i, 0] = 1; + } + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i, a] = dp[i - 1, a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[i, a] = dp[i - 1, a] + dp[i, a - coins[i - 1]]; + } + } + } + return dp[n, amt]; + } + + /* Coin change II: Space-optimized dynamic programming */ + int CoinChangeIIDPComp(int[] coins, int amt) { + int n = coins.Length; + // Initialize dp table + int[] dp = new int[amt + 1]; + dp[0] = 1; + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[a] = dp[a] + dp[a - coins[i - 1]]; + } + } + } + return dp[amt]; + } + + [Test] + public void Test() { + int[] coins = [1, 2, 5]; + int amt = 5; + + // Dynamic programming + int res = CoinChangeIIDP(coins, amt); + Console.WriteLine("Number of coin combinations to make target amount is " + res); + + // Space-optimized dynamic programming + res = CoinChangeIIDPComp(coins, amt); + Console.WriteLine("Number of coin combinations to make target amount is " + res); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/edit_distance.cs b/en/codes/csharp/chapter_dynamic_programming/edit_distance.cs new file mode 100644 index 000000000..3a8a2a4b4 --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/edit_distance.cs @@ -0,0 +1,141 @@ +/** +* File: edit_distance.cs +* Created Time: 2023-07-14 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class edit_distance { + /* Edit distance: Brute-force search */ + int EditDistanceDFS(string s, string t, int i, int j) { + // If both s and t are empty, return 0 + if (i == 0 && j == 0) + return 0; + // If s is empty, return length of t + if (i == 0) + return j; + // If t is empty, return length of s + if (j == 0) + return i; + // If two characters are equal, skip both characters + if (s[i - 1] == t[j - 1]) + return EditDistanceDFS(s, t, i - 1, j - 1); + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + int insert = EditDistanceDFS(s, t, i, j - 1); + int delete = EditDistanceDFS(s, t, i - 1, j); + int replace = EditDistanceDFS(s, t, i - 1, j - 1); + // Return minimum edit steps + return Math.Min(Math.Min(insert, delete), replace) + 1; + } + + /* Edit distance: Memoization search */ + int EditDistanceDFSMem(string s, string t, int[][] mem, int i, int j) { + // If both s and t are empty, return 0 + if (i == 0 && j == 0) + return 0; + // If s is empty, return length of t + if (i == 0) + return j; + // If t is empty, return length of s + if (j == 0) + return i; + // If there's a record, return it directly + if (mem[i][j] != -1) + return mem[i][j]; + // If two characters are equal, skip both characters + if (s[i - 1] == t[j - 1]) + return EditDistanceDFSMem(s, t, mem, i - 1, j - 1); + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + int insert = EditDistanceDFSMem(s, t, mem, i, j - 1); + int delete = EditDistanceDFSMem(s, t, mem, i - 1, j); + int replace = EditDistanceDFSMem(s, t, mem, i - 1, j - 1); + // Record and return minimum edit steps + mem[i][j] = Math.Min(Math.Min(insert, delete), replace) + 1; + return mem[i][j]; + } + + /* Edit distance: Dynamic programming */ + int EditDistanceDP(string s, string t) { + int n = s.Length, m = t.Length; + int[,] dp = new int[n + 1, m + 1]; + // State transition: first row and first column + for (int i = 1; i <= n; i++) { + dp[i, 0] = i; + } + for (int j = 1; j <= m; j++) { + dp[0, j] = j; + } + // State transition: rest of the rows and columns + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + if (s[i - 1] == t[j - 1]) { + // If two characters are equal, skip both characters + dp[i, j] = dp[i - 1, j - 1]; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[i, j] = Math.Min(Math.Min(dp[i, j - 1], dp[i - 1, j]), dp[i - 1, j - 1]) + 1; + } + } + } + return dp[n, m]; + } + + /* Edit distance: Space-optimized dynamic programming */ + int EditDistanceDPComp(string s, string t) { + int n = s.Length, m = t.Length; + int[] dp = new int[m + 1]; + // State transition: first row + for (int j = 1; j <= m; j++) { + dp[j] = j; + } + // State transition: rest of the rows + for (int i = 1; i <= n; i++) { + // State transition: first column + int leftup = dp[0]; // Temporarily store dp[i-1, j-1] + dp[0] = i; + // State transition: rest of the columns + for (int j = 1; j <= m; j++) { + int temp = dp[j]; + if (s[i - 1] == t[j - 1]) { + // If two characters are equal, skip both characters + dp[j] = leftup; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[j] = Math.Min(Math.Min(dp[j - 1], dp[j]), leftup) + 1; + } + leftup = temp; // Update for next round's dp[i-1, j-1] + } + } + return dp[m]; + } + + [Test] + public void Test() { + string s = "bag"; + string t = "pack"; + int n = s.Length, m = t.Length; + + // Brute-force search + int res = EditDistanceDFS(s, t, n, m); + Console.WriteLine("Change " + s + " to " + t + " requires a minimum of " + res + " edits"); + + // Memoization search + int[][] mem = new int[n + 1][]; + for (int i = 0; i <= n; i++) { + mem[i] = new int[m + 1]; + Array.Fill(mem[i], -1); + } + + res = EditDistanceDFSMem(s, t, mem, n, m); + Console.WriteLine("Change " + s + " to " + t + " requires a minimum of " + res + " edits"); + + // Dynamic programming + res = EditDistanceDP(s, t); + Console.WriteLine("Change " + s + " to " + t + " requires a minimum of " + res + " edits"); + + // Space-optimized dynamic programming + res = EditDistanceDPComp(s, t); + Console.WriteLine("Change " + s + " to " + t + " requires a minimum of " + res + " edits"); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/knapsack.cs b/en/codes/csharp/chapter_dynamic_programming/knapsack.cs new file mode 100644 index 000000000..d830861a5 --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/knapsack.cs @@ -0,0 +1,118 @@ +/** +* File: knapsack.cs +* Created Time: 2023-07-07 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class knapsack { + /* 0-1 knapsack: Brute-force search */ + int KnapsackDFS(int[] weight, int[] val, int i, int c) { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i == 0 || c == 0) { + return 0; + } + // If exceeds knapsack capacity, can only choose not to put it in + if (weight[i - 1] > c) { + return KnapsackDFS(weight, val, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + int no = KnapsackDFS(weight, val, i - 1, c); + int yes = KnapsackDFS(weight, val, i - 1, c - weight[i - 1]) + val[i - 1]; + // Return the larger value of the two options + return Math.Max(no, yes); + } + + /* 0-1 knapsack: Memoization search */ + int KnapsackDFSMem(int[] weight, int[] val, int[][] mem, int i, int c) { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i == 0 || c == 0) { + return 0; + } + // If there's a record, return it directly + if (mem[i][c] != -1) { + return mem[i][c]; + } + // If exceeds knapsack capacity, can only choose not to put it in + if (weight[i - 1] > c) { + return KnapsackDFSMem(weight, val, mem, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + int no = KnapsackDFSMem(weight, val, mem, i - 1, c); + int yes = KnapsackDFSMem(weight, val, mem, i - 1, c - weight[i - 1]) + val[i - 1]; + // Record and return the larger value of the two options + mem[i][c] = Math.Max(no, yes); + return mem[i][c]; + } + + /* 0-1 knapsack: Dynamic programming */ + int KnapsackDP(int[] weight, int[] val, int cap) { + int n = weight.Length; + // Initialize dp table + int[,] dp = new int[n + 1, cap + 1]; + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (weight[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[i, c] = dp[i - 1, c]; + } else { + // The larger value between not selecting and selecting item i + dp[i, c] = Math.Max(dp[i - 1, c - weight[i - 1]] + val[i - 1], dp[i - 1, c]); + } + } + } + return dp[n, cap]; + } + + /* 0-1 knapsack: Space-optimized dynamic programming */ + int KnapsackDPComp(int[] weight, int[] val, int cap) { + int n = weight.Length; + // Initialize dp table + int[] dp = new int[cap + 1]; + // State transition + for (int i = 1; i <= n; i++) { + // Traverse in reverse order + for (int c = cap; c > 0; c--) { + if (weight[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[c] = dp[c]; + } else { + // The larger value between not selecting and selecting item i + dp[c] = Math.Max(dp[c], dp[c - weight[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; + } + + [Test] + public void Test() { + int[] weight = [10, 20, 30, 40, 50]; + int[] val = [50, 120, 150, 210, 240]; + int cap = 50; + int n = weight.Length; + + // Brute-force search + int res = KnapsackDFS(weight, val, n, cap); + Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + + // Memoization search + int[][] mem = new int[n + 1][]; + for (int i = 0; i <= n; i++) { + mem[i] = new int[cap + 1]; + Array.Fill(mem[i], -1); + } + res = KnapsackDFSMem(weight, val, mem, n, cap); + Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + + // Dynamic programming + res = KnapsackDP(weight, val, cap); + Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + + // Space-optimized dynamic programming + res = KnapsackDPComp(weight, val, cap); + Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cs b/en/codes/csharp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cs new file mode 100644 index 000000000..f824d23db --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cs @@ -0,0 +1,53 @@ +/** +* File: min_cost_climbing_stairs_dp.cs +* Created Time: 2023-06-30 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class min_cost_climbing_stairs_dp { + /* Minimum cost climbing stairs: Dynamic programming */ + int MinCostClimbingStairsDP(int[] cost) { + int n = cost.Length - 1; + if (n == 1 || n == 2) + return cost[n]; + // Initialize dp table, used to store solutions to subproblems + int[] dp = new int[n + 1]; + // Initial state: preset the solution to the smallest subproblem + dp[1] = cost[1]; + dp[2] = cost[2]; + // State transition: gradually solve larger subproblems from smaller ones + for (int i = 3; i <= n; i++) { + dp[i] = Math.Min(dp[i - 1], dp[i - 2]) + cost[i]; + } + return dp[n]; + } + + /* Minimum cost climbing stairs: Space-optimized dynamic programming */ + int MinCostClimbingStairsDPComp(int[] cost) { + int n = cost.Length - 1; + if (n == 1 || n == 2) + return cost[n]; + int a = cost[1], b = cost[2]; + for (int i = 3; i <= n; i++) { + int tmp = b; + b = Math.Min(a, tmp) + cost[i]; + a = tmp; + } + return b; + } + + [Test] + public void Test() { + int[] cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]; + Console.WriteLine("Input stair cost list is"); + PrintUtil.PrintList(cost); + + int res = MinCostClimbingStairsDP(cost); + Console.WriteLine($"Minimum cost to climb stairs is {res}"); + + res = MinCostClimbingStairsDPComp(cost); + Console.WriteLine($"Minimum cost to climb stairs is {res}"); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/min_path_sum.cs b/en/codes/csharp/chapter_dynamic_programming/min_path_sum.cs new file mode 100644 index 000000000..9740f092a --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/min_path_sum.cs @@ -0,0 +1,127 @@ +/** +* File: min_path_sum.cs +* Created Time: 2023-07-10 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class min_path_sum { + /* Minimum path sum: Brute-force search */ + int MinPathSumDFS(int[][] grid, int i, int j) { + // If it's the top-left cell, terminate the search + if (i == 0 && j == 0) { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + return int.MaxValue; + } + // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + int up = MinPathSumDFS(grid, i - 1, j); + int left = MinPathSumDFS(grid, i, j - 1); + // Return the minimum path cost from top-left to (i, j) + return Math.Min(left, up) + grid[i][j]; + } + + /* Minimum path sum: Memoization search */ + int MinPathSumDFSMem(int[][] grid, int[][] mem, int i, int j) { + // If it's the top-left cell, terminate the search + if (i == 0 && j == 0) { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + return int.MaxValue; + } + // If there's a record, return it directly + if (mem[i][j] != -1) { + return mem[i][j]; + } + // Minimum path cost for left and upper cells + int up = MinPathSumDFSMem(grid, mem, i - 1, j); + int left = MinPathSumDFSMem(grid, mem, i, j - 1); + // Record and return the minimum path cost from top-left to (i, j) + mem[i][j] = Math.Min(left, up) + grid[i][j]; + return mem[i][j]; + } + + /* Minimum path sum: Dynamic programming */ + int MinPathSumDP(int[][] grid) { + int n = grid.Length, m = grid[0].Length; + // Initialize dp table + int[,] dp = new int[n, m]; + dp[0, 0] = grid[0][0]; + // State transition: first row + for (int j = 1; j < m; j++) { + dp[0, j] = dp[0, j - 1] + grid[0][j]; + } + // State transition: first column + for (int i = 1; i < n; i++) { + dp[i, 0] = dp[i - 1, 0] + grid[i][0]; + } + // State transition: rest of the rows and columns + for (int i = 1; i < n; i++) { + for (int j = 1; j < m; j++) { + dp[i, j] = Math.Min(dp[i, j - 1], dp[i - 1, j]) + grid[i][j]; + } + } + return dp[n - 1, m - 1]; + } + + /* Minimum path sum: Space-optimized dynamic programming */ + int MinPathSumDPComp(int[][] grid) { + int n = grid.Length, m = grid[0].Length; + // Initialize dp table + int[] dp = new int[m]; + dp[0] = grid[0][0]; + // State transition: first row + for (int j = 1; j < m; j++) { + dp[j] = dp[j - 1] + grid[0][j]; + } + // State transition: rest of the rows + for (int i = 1; i < n; i++) { + // State transition: first column + dp[0] = dp[0] + grid[i][0]; + // State transition: rest of the columns + for (int j = 1; j < m; j++) { + dp[j] = Math.Min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + return dp[m - 1]; + } + + [Test] + public void Test() { + int[][] grid = + [ + [1, 3, 1, 5], + [2, 2, 4, 2], + [5, 3, 2, 1], + [4, 3, 5, 2] + ]; + + int n = grid.Length, m = grid[0].Length; + + // Brute-force search + int res = MinPathSumDFS(grid, n - 1, m - 1); + Console.WriteLine("Minimum path sum from top-left to bottom-right is " + res); + + // Memoization search + int[][] mem = new int[n][]; + for (int i = 0; i < n; i++) { + mem[i] = new int[m]; + Array.Fill(mem[i], -1); + } + res = MinPathSumDFSMem(grid, mem, n - 1, m - 1); + Console.WriteLine("Minimum path sum from top-left to bottom-right is " + res); + + // Dynamic programming + res = MinPathSumDP(grid); + Console.WriteLine("Minimum path sum from top-left to bottom-right is " + res); + + // Space-optimized dynamic programming + res = MinPathSumDPComp(grid); + Console.WriteLine("Minimum path sum from top-left to bottom-right is " + res); + } +} diff --git a/en/codes/csharp/chapter_dynamic_programming/unbounded_knapsack.cs b/en/codes/csharp/chapter_dynamic_programming/unbounded_knapsack.cs new file mode 100644 index 000000000..a16442f04 --- /dev/null +++ b/en/codes/csharp/chapter_dynamic_programming/unbounded_knapsack.cs @@ -0,0 +1,64 @@ +/** +* File: unbounded_knapsack.cs +* Created Time: 2023-07-12 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_dynamic_programming; + +public class unbounded_knapsack { + /* Unbounded knapsack: Dynamic programming */ + int UnboundedKnapsackDP(int[] wgt, int[] val, int cap) { + int n = wgt.Length; + // Initialize dp table + int[,] dp = new int[n + 1, cap + 1]; + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[i, c] = dp[i - 1, c]; + } else { + // The larger value between not selecting and selecting item i + dp[i, c] = Math.Max(dp[i - 1, c], dp[i, c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[n, cap]; + } + + /* Unbounded knapsack: Space-optimized dynamic programming */ + int UnboundedKnapsackDPComp(int[] wgt, int[] val, int cap) { + int n = wgt.Length; + // Initialize dp table + int[] dp = new int[cap + 1]; + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[c] = dp[c]; + } else { + // The larger value between not selecting and selecting item i + dp[c] = Math.Max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; + } + + [Test] + public void Test() { + int[] wgt = [1, 2, 3]; + int[] val = [5, 11, 15]; + int cap = 4; + + // Dynamic programming + int res = UnboundedKnapsackDP(wgt, val, cap); + Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + + // Space-optimized dynamic programming + res = UnboundedKnapsackDPComp(wgt, val, cap); + Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + } +} diff --git a/en/codes/csharp/chapter_graph/graph_adjacency_list.cs b/en/codes/csharp/chapter_graph/graph_adjacency_list.cs new file mode 100644 index 000000000..15349cd3c --- /dev/null +++ b/en/codes/csharp/chapter_graph/graph_adjacency_list.cs @@ -0,0 +1,122 @@ +/** + * File: graph_adjacency_list.cs + * Created Time: 2023-02-06 + * Author: zjkung1123 (zjkung1123@gmail.com) + */ + +namespace hello_algo.chapter_graph; + +/* Undirected graph class based on adjacency list */ +public class GraphAdjList { + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + public Dictionary> adjList; + + /* Constructor */ + public GraphAdjList(Vertex[][] edges) { + adjList = []; + // Add all vertices and edges + foreach (Vertex[] edge in edges) { + AddVertex(edge[0]); + AddVertex(edge[1]); + AddEdge(edge[0], edge[1]); + } + } + + /* Get the number of vertices */ + int Size() { + return adjList.Count; + } + + /* Add edge */ + public void AddEdge(Vertex vet1, Vertex vet2) { + if (!adjList.ContainsKey(vet1) || !adjList.ContainsKey(vet2) || vet1 == vet2) + throw new InvalidOperationException(); + // Add edge vet1 - vet2 + adjList[vet1].Add(vet2); + adjList[vet2].Add(vet1); + } + + /* Remove edge */ + public void RemoveEdge(Vertex vet1, Vertex vet2) { + if (!adjList.ContainsKey(vet1) || !adjList.ContainsKey(vet2) || vet1 == vet2) + throw new InvalidOperationException(); + // Remove edge vet1 - vet2 + adjList[vet1].Remove(vet2); + adjList[vet2].Remove(vet1); + } + + /* Add vertex */ + public void AddVertex(Vertex vet) { + if (adjList.ContainsKey(vet)) + return; + // Add a new linked list in the adjacency list + adjList.Add(vet, []); + } + + /* Remove vertex */ + public void RemoveVertex(Vertex vet) { + if (!adjList.ContainsKey(vet)) + throw new InvalidOperationException(); + // Remove the linked list corresponding to vertex vet in the adjacency list + adjList.Remove(vet); + // Traverse the linked lists of other vertices and remove all edges containing vet + foreach (List list in adjList.Values) { + list.Remove(vet); + } + } + + /* Print adjacency list */ + public void Print() { + Console.WriteLine("Adjacency list ="); + foreach (KeyValuePair> pair in adjList) { + List tmp = []; + foreach (Vertex vertex in pair.Value) + tmp.Add(vertex.val); + Console.WriteLine(pair.Key.val + ": [" + string.Join(", ", tmp) + "],"); + } + } +} + +public class graph_adjacency_list { + [Test] + public void Test() { + /* Add edge */ + Vertex[] v = Vertex.ValsToVets([1, 3, 2, 5, 4]); + Vertex[][] edges = + [ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[2], v[3]], + [v[2], v[4]], + [v[3], v[4]] + ]; + GraphAdjList graph = new(edges); + Console.WriteLine("\nAfter initialization, graph is"); + graph.Print(); + + /* Add edge */ + // Vertices 1, 3 are v[0], v[1] + graph.AddEdge(v[0], v[2]); + Console.WriteLine("\nAfter adding edge 1-2, graph is"); + graph.Print(); + + /* Remove edge */ + // Vertex 3 is v[1] + graph.RemoveEdge(v[0], v[1]); + Console.WriteLine("\nAfter removing edge 1-3, graph is"); + graph.Print(); + + /* Add vertex */ + Vertex v5 = new(6); + graph.AddVertex(v5); + Console.WriteLine("\nAfter adding vertex 6, graph is"); + graph.Print(); + + /* Remove vertex */ + // Vertex 3 is v[1] + graph.RemoveVertex(v[1]); + Console.WriteLine("\nAfter removing vertex 3, graph is"); + graph.Print(); + } +} diff --git a/en/codes/csharp/chapter_graph/graph_adjacency_matrix.cs b/en/codes/csharp/chapter_graph/graph_adjacency_matrix.cs new file mode 100644 index 000000000..029797e25 --- /dev/null +++ b/en/codes/csharp/chapter_graph/graph_adjacency_matrix.cs @@ -0,0 +1,137 @@ +/** + * File: graph_adjacency_matrix.cs + * Created Time: 2023-02-06 + * Author: zjkung1123 (zjkung1123@gmail.com) + */ + +namespace hello_algo.chapter_graph; + +/* Undirected graph class based on adjacency matrix */ +class GraphAdjMat { + List vertices; // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + List> adjMat; // Adjacency matrix, where the row and column indices correspond to the "vertex index" + + /* Constructor */ + public GraphAdjMat(int[] vertices, int[][] edges) { + this.vertices = []; + this.adjMat = []; + // Add vertex + foreach (int val in vertices) { + AddVertex(val); + } + // Add edge + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + foreach (int[] e in edges) { + AddEdge(e[0], e[1]); + } + } + + /* Get the number of vertices */ + int Size() { + return vertices.Count; + } + + /* Add vertex */ + public void AddVertex(int val) { + int n = Size(); + // Add the value of the new vertex to the vertex list + vertices.Add(val); + // Add a row to the adjacency matrix + List newRow = new(n); + for (int j = 0; j < n; j++) { + newRow.Add(0); + } + adjMat.Add(newRow); + // Add a column to the adjacency matrix + foreach (List row in adjMat) { + row.Add(0); + } + } + + /* Remove vertex */ + public void RemoveVertex(int index) { + if (index >= Size()) + throw new IndexOutOfRangeException(); + // Remove the vertex at index from the vertex list + vertices.RemoveAt(index); + // Remove the row at index from the adjacency matrix + adjMat.RemoveAt(index); + // Remove the column at index from the adjacency matrix + foreach (List row in adjMat) { + row.RemoveAt(index); + } + } + + /* Add edge */ + // Parameters i, j correspond to the vertices element indices + public void AddEdge(int i, int j) { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= Size() || j >= Size() || i == j) + throw new IndexOutOfRangeException(); + // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + adjMat[i][j] = 1; + adjMat[j][i] = 1; + } + + /* Remove edge */ + // Parameters i, j correspond to the vertices element indices + public void RemoveEdge(int i, int j) { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= Size() || j >= Size() || i == j) + throw new IndexOutOfRangeException(); + adjMat[i][j] = 0; + adjMat[j][i] = 0; + } + + /* Print adjacency matrix */ + public void Print() { + Console.Write("Vertex list = "); + PrintUtil.PrintList(vertices); + Console.WriteLine("Adjacency matrix ="); + PrintUtil.PrintMatrix(adjMat); + } +} + +public class graph_adjacency_matrix { + [Test] + public void Test() { + /* Add edge */ + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + int[] vertices = [1, 3, 2, 5, 4]; + int[][] edges = + [ + [0, 1], + [0, 3], + [1, 2], + [2, 3], + [2, 4], + [3, 4] + ]; + GraphAdjMat graph = new(vertices, edges); + Console.WriteLine("\nAfter initialization, graph is"); + graph.Print(); + + /* Add edge */ + // Add vertex + graph.AddEdge(0, 2); + Console.WriteLine("\nAfter adding edge 1-2, graph is"); + graph.Print(); + + /* Remove edge */ + // Vertices 1, 3 have indices 0, 1 respectively + graph.RemoveEdge(0, 1); + Console.WriteLine("\nAfter removing edge 1-3, graph is"); + graph.Print(); + + /* Add vertex */ + graph.AddVertex(6); + Console.WriteLine("\nAfter adding vertex 6, graph is"); + graph.Print(); + + /* Remove vertex */ + // Vertex 3 has index 1 + graph.RemoveVertex(1); + Console.WriteLine("\nAfter removing vertex 3, graph is"); + graph.Print(); + } +} diff --git a/en/codes/csharp/chapter_graph/graph_bfs.cs b/en/codes/csharp/chapter_graph/graph_bfs.cs new file mode 100644 index 000000000..0ca8886c2 --- /dev/null +++ b/en/codes/csharp/chapter_graph/graph_bfs.cs @@ -0,0 +1,58 @@ +/** + * File: graph_bfs.cs + * Created Time: 2023-03-08 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_graph; + +public class graph_bfs { + /* Breadth-first traversal */ + // Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + List GraphBFS(GraphAdjList graph, Vertex startVet) { + // Vertex traversal sequence + List res = []; + // Hash set for recording vertices that have been visited + HashSet visited = [startVet]; + // Queue used to implement BFS + Queue que = new(); + que.Enqueue(startVet); + // Starting from vertex vet, loop until all vertices are visited + while (que.Count > 0) { + Vertex vet = que.Dequeue(); // Dequeue the front vertex + res.Add(vet); // Record visited vertex + foreach (Vertex adjVet in graph.adjList[vet]) { + if (visited.Contains(adjVet)) { + continue; // Skip vertices that have been visited + } + que.Enqueue(adjVet); // Only enqueue unvisited vertices + visited.Add(adjVet); // Mark this vertex as visited + } + } + + // Return vertex traversal sequence + return res; + } + + [Test] + public void Test() { + /* Add edge */ + Vertex[] v = Vertex.ValsToVets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + Vertex[][] edges = + [ + [v[0], v[1]], [v[0], v[3]], [v[1], v[2]], + [v[1], v[4]], [v[2], v[5]], [v[3], v[4]], + [v[3], v[6]], [v[4], v[5]], [v[4], v[7]], + [v[5], v[8]], [v[6], v[7]], [v[7], v[8]] + ]; + + GraphAdjList graph = new(edges); + Console.WriteLine("\nAfter initialization, graph is"); + graph.Print(); + + /* Breadth-first traversal */ + List res = GraphBFS(graph, v[0]); + Console.WriteLine("\nBreadth-first traversal (BFS) vertex sequence is"); + Console.WriteLine(string.Join(" ", Vertex.VetsToVals(res))); + } +} diff --git a/en/codes/csharp/chapter_graph/graph_dfs.cs b/en/codes/csharp/chapter_graph/graph_dfs.cs new file mode 100644 index 000000000..f1adbc3eb --- /dev/null +++ b/en/codes/csharp/chapter_graph/graph_dfs.cs @@ -0,0 +1,54 @@ +/** + * File: graph_dfs.cs + * Created Time: 2023-03-08 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_graph; + +public class graph_dfs { + /* Depth-first traversal helper function */ + void DFS(GraphAdjList graph, HashSet visited, List res, Vertex vet) { + res.Add(vet); // Record visited vertex + visited.Add(vet); // Mark this vertex as visited + // Traverse all adjacent vertices of this vertex + foreach (Vertex adjVet in graph.adjList[vet]) { + if (visited.Contains(adjVet)) { + continue; // Skip vertices that have been visited + } + // Recursively visit adjacent vertices + DFS(graph, visited, res, adjVet); + } + } + + /* Depth-first traversal */ + // Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + List GraphDFS(GraphAdjList graph, Vertex startVet) { + // Vertex traversal sequence + List res = []; + // Hash set for recording vertices that have been visited + HashSet visited = []; + DFS(graph, visited, res, startVet); + return res; + } + + [Test] + public void Test() { + /* Add edge */ + Vertex[] v = Vertex.ValsToVets([0, 1, 2, 3, 4, 5, 6]); + Vertex[][] edges = + [ + [v[0], v[1]], [v[0], v[3]], [v[1], v[2]], + [v[2], v[5]], [v[4], v[5]], [v[5], v[6]], + ]; + + GraphAdjList graph = new(edges); + Console.WriteLine("\nAfter initialization, graph is"); + graph.Print(); + + /* Depth-first traversal */ + List res = GraphDFS(graph, v[0]); + Console.WriteLine("\nDepth-first traversal (DFS) vertex sequence is"); + Console.WriteLine(string.Join(" ", Vertex.VetsToVals(res))); + } +} diff --git a/en/codes/csharp/chapter_greedy/coin_change_greedy.cs b/en/codes/csharp/chapter_greedy/coin_change_greedy.cs new file mode 100644 index 000000000..0e7cdb5a0 --- /dev/null +++ b/en/codes/csharp/chapter_greedy/coin_change_greedy.cs @@ -0,0 +1,54 @@ +/** +* File: coin_change_greedy.cs +* Created Time: 2023-07-21 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_greedy; + +public class coin_change_greedy { + /* Coin change: Greedy algorithm */ + int CoinChangeGreedy(int[] coins, int amt) { + // Assume coins list is sorted + int i = coins.Length - 1; + int count = 0; + // Loop to make greedy choices until no remaining amount + while (amt > 0) { + // Find the coin that is less than and closest to the remaining amount + while (i > 0 && coins[i] > amt) { + i--; + } + // Choose coins[i] + amt -= coins[i]; + count++; + } + // If no feasible solution is found, return -1 + return amt == 0 ? count : -1; + } + + [Test] + public void Test() { + // Greedy algorithm: Can guarantee finding the global optimal solution + int[] coins = [1, 5, 10, 20, 50, 100]; + int amt = 186; + int res = CoinChangeGreedy(coins, amt); + Console.WriteLine("\ncoins = " + coins.PrintList() + ", amt = " + amt); + Console.WriteLine("To make " + amt + ", minimum number of coins needed is " + res); + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = [1, 20, 50]; + amt = 60; + res = CoinChangeGreedy(coins, amt); + Console.WriteLine("\ncoins = " + coins.PrintList() + ", amt = " + amt); + Console.WriteLine("To make " + amt + ", minimum number of coins needed is " + res); + Console.WriteLine("Actually the minimum number needed is 3, i.e., 20 + 20 + 20"); + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = [1, 49, 50]; + amt = 98; + res = CoinChangeGreedy(coins, amt); + Console.WriteLine("\ncoins = " + coins.PrintList() + ", amt = " + amt); + Console.WriteLine("To make " + amt + ", minimum number of coins needed is " + res); + Console.WriteLine("Actually the minimum number needed is 2, i.e., 49 + 49"); + } +} \ No newline at end of file diff --git a/en/codes/csharp/chapter_greedy/fractional_knapsack.cs b/en/codes/csharp/chapter_greedy/fractional_knapsack.cs new file mode 100644 index 000000000..940baa57d --- /dev/null +++ b/en/codes/csharp/chapter_greedy/fractional_knapsack.cs @@ -0,0 +1,52 @@ +/** +* File: fractional_knapsack.cs +* Created Time: 2023-07-21 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_greedy; + +/* Item */ +class Item(int w, int v) { + public int w = w; // Item weight + public int v = v; // Item value +} + +public class fractional_knapsack { + /* Fractional knapsack: Greedy algorithm */ + double FractionalKnapsack(int[] wgt, int[] val, int cap) { + // Create item list with two attributes: weight, value + Item[] items = new Item[wgt.Length]; + for (int i = 0; i < wgt.Length; i++) { + items[i] = new Item(wgt[i], val[i]); + } + // Sort by unit value item.v / item.w from high to low + Array.Sort(items, (x, y) => (y.v / y.w).CompareTo(x.v / x.w)); + // Loop for greedy selection + double res = 0; + foreach (Item item in items) { + if (item.w <= cap) { + // If remaining capacity is sufficient, put the entire current item into the knapsack + res += item.v; + cap -= item.w; + } else { + // If remaining capacity is insufficient, put part of the current item into the knapsack + res += (double)item.v / item.w * cap; + // No remaining capacity, so break out of the loop + break; + } + } + return res; + } + + [Test] + public void Test() { + int[] wgt = [10, 20, 30, 40, 50]; + int[] val = [50, 120, 150, 210, 240]; + int cap = 50; + + // Greedy algorithm + double res = FractionalKnapsack(wgt, val, cap); + Console.WriteLine("Maximum item value not exceeding knapsack capacity is " + res); + } +} \ No newline at end of file diff --git a/en/codes/csharp/chapter_greedy/max_capacity.cs b/en/codes/csharp/chapter_greedy/max_capacity.cs new file mode 100644 index 000000000..4bb373cb9 --- /dev/null +++ b/en/codes/csharp/chapter_greedy/max_capacity.cs @@ -0,0 +1,39 @@ +/** +* File: max_capacity.cs +* Created Time: 2023-07-21 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_greedy; + +public class max_capacity { + /* Max capacity: Greedy algorithm */ + int MaxCapacity(int[] ht) { + // Initialize i, j to be at both ends of the array + int i = 0, j = ht.Length - 1; + // Initial max capacity is 0 + int res = 0; + // Loop for greedy selection until the two boards meet + while (i < j) { + // Update max capacity + int cap = Math.Min(ht[i], ht[j]) * (j - i); + res = Math.Max(res, cap); + // Move the shorter board inward + if (ht[i] < ht[j]) { + i++; + } else { + j--; + } + } + return res; + } + + [Test] + public void Test() { + int[] ht = [3, 8, 5, 2, 7, 7, 3, 4]; + + // Greedy algorithm + int res = MaxCapacity(ht); + Console.WriteLine("Maximum capacity is " + res); + } +} \ No newline at end of file diff --git a/en/codes/csharp/chapter_greedy/max_product_cutting.cs b/en/codes/csharp/chapter_greedy/max_product_cutting.cs new file mode 100644 index 000000000..7626c9aa1 --- /dev/null +++ b/en/codes/csharp/chapter_greedy/max_product_cutting.cs @@ -0,0 +1,39 @@ +/** +* File: max_product_cutting.cs +* Created Time: 2023-07-21 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_greedy; + +public class max_product_cutting { + /* Max product cutting: Greedy algorithm */ + int MaxProductCutting(int n) { + // When n <= 3, must cut out a 1 + if (n <= 3) { + return 1 * (n - 1); + } + // Greedily cut out 3, a is the number of 3s, b is the remainder + int a = n / 3; + int b = n % 3; + if (b == 1) { + // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + return (int)Math.Pow(3, a - 1) * 2 * 2; + } + if (b == 2) { + // When the remainder is 2, do nothing + return (int)Math.Pow(3, a) * 2; + } + // When the remainder is 0, do nothing + return (int)Math.Pow(3, a); + } + + [Test] + public void Test() { + int n = 58; + + // Greedy algorithm + int res = MaxProductCutting(n); + Console.WriteLine("Maximum cutting product is" + res); + } +} \ No newline at end of file diff --git a/en/codes/csharp/chapter_hashing/array_hash_map.cs b/en/codes/csharp/chapter_hashing/array_hash_map.cs new file mode 100644 index 000000000..cfa6cd0ae --- /dev/null +++ b/en/codes/csharp/chapter_hashing/array_hash_map.cs @@ -0,0 +1,134 @@ +/** + * File: array_hash_map.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_hashing; + +/* Key-value pair int->string */ +class Pair(int key, string val) { + public int key = key; + public string val = val; +} + +/* Hash table based on array implementation */ +class ArrayHashMap { + List buckets; + public ArrayHashMap() { + // Initialize array with 100 buckets + buckets = []; + for (int i = 0; i < 100; i++) { + buckets.Add(null); + } + } + + /* Hash function */ + int HashFunc(int key) { + int index = key % 100; + return index; + } + + /* Query operation */ + public string? Get(int key) { + int index = HashFunc(key); + Pair? pair = buckets[index]; + if (pair == null) return null; + return pair.val; + } + + /* Add operation */ + public void Put(int key, string val) { + Pair pair = new(key, val); + int index = HashFunc(key); + buckets[index] = pair; + } + + /* Remove operation */ + public void Remove(int key) { + int index = HashFunc(key); + // Set to null to represent deletion + buckets[index] = null; + } + + /* Get all key-value pairs */ + public List PairSet() { + List pairSet = []; + foreach (Pair? pair in buckets) { + if (pair != null) + pairSet.Add(pair); + } + return pairSet; + } + + /* Get all keys */ + public List KeySet() { + List keySet = []; + foreach (Pair? pair in buckets) { + if (pair != null) + keySet.Add(pair.key); + } + return keySet; + } + + /* Get all values */ + public List ValueSet() { + List valueSet = []; + foreach (Pair? pair in buckets) { + if (pair != null) + valueSet.Add(pair.val); + } + return valueSet; + } + + /* Print hash table */ + public void Print() { + foreach (Pair kv in PairSet()) { + Console.WriteLine(kv.key + " -> " + kv.val); + } + } +} + + +public class array_hash_map { + [Test] + public void Test() { + /* Initialize hash table */ + ArrayHashMap map = new(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.Put(12836, "Xiao Ha"); + map.Put(15937, "Xiao Luo"); + map.Put(16750, "Xiao Suan"); + map.Put(13276, "Xiao Fa"); + map.Put(10583, "Xiao Ya"); + Console.WriteLine("\nAfter adding is complete, hash table is\nKey -> Value"); + map.Print(); + + /* Query operation */ + // Input key into hash table to get value + string? name = map.Get(15937); + Console.WriteLine("\nInput student ID 15937, query name " + name); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.Remove(10583); + Console.WriteLine("\nAfter removing 10583, hash table is\nKey -> Value"); + map.Print(); + + /* Traverse hash table */ + Console.WriteLine("\nTraverse key-value pairs Key->Value"); + foreach (Pair kv in map.PairSet()) { + Console.WriteLine(kv.key + " -> " + kv.val); + } + Console.WriteLine("\nTraverse keys only Key"); + foreach (int key in map.KeySet()) { + Console.WriteLine(key); + } + Console.WriteLine("\nTraverse values only Value"); + foreach (string val in map.ValueSet()) { + Console.WriteLine(val); + } + } +} diff --git a/en/codes/csharp/chapter_hashing/built_in_hash.cs b/en/codes/csharp/chapter_hashing/built_in_hash.cs new file mode 100644 index 000000000..3a866444e --- /dev/null +++ b/en/codes/csharp/chapter_hashing/built_in_hash.cs @@ -0,0 +1,36 @@ +/** +* File: built_in_hash.cs +* Created Time: 2023-06-26 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_hashing; + +public class built_in_hash { + [Test] + public void Test() { + int num = 3; + int hashNum = num.GetHashCode(); + Console.WriteLine("Integer " + num + " has hash value " + hashNum); + + bool bol = true; + int hashBol = bol.GetHashCode(); + Console.WriteLine("Boolean " + bol + " has hash value " + hashBol); + + double dec = 3.14159; + int hashDec = dec.GetHashCode(); + Console.WriteLine("Decimal " + dec + " has hash value " + hashDec); + + string str = "Hello Algo"; + int hashStr = str.GetHashCode(); + Console.WriteLine("String " + str + " has hash value " + hashStr); + + object[] arr = [12836, "Xiao Ha"]; + int hashTup = arr.GetHashCode(); + Console.WriteLine("Array [" + string.Join(", ", arr) + "] hash value is " + hashTup); + + ListNode obj = new(0); + int hashObj = obj.GetHashCode(); + Console.WriteLine("Node object " + obj + " has hash value " + hashObj); + } +} diff --git a/en/codes/csharp/chapter_hashing/hash_map.cs b/en/codes/csharp/chapter_hashing/hash_map.cs new file mode 100644 index 000000000..d7ad8fbfa --- /dev/null +++ b/en/codes/csharp/chapter_hashing/hash_map.cs @@ -0,0 +1,51 @@ + +/** + * File: hash_map.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_hashing; + +public class hash_map { + [Test] + public void Test() { + /* Initialize hash table */ + Dictionary map = new() { + /* Add operation */ + // Add key-value pair (key, value) to the hash table + { 12836, "Xiao Ha" }, + { 15937, "Xiao Luo" }, + { 16750, "Xiao Suan" }, + { 13276, "Xiao Fa" }, + { 10583, "Xiao Ya" } + }; + Console.WriteLine("\nAfter adding is complete, hash table is\nKey -> Value"); + PrintUtil.PrintHashMap(map); + + /* Query operation */ + // Input key into hash table to get value + string name = map[15937]; + Console.WriteLine("\nInput student ID 15937, query name " + name); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.Remove(10583); + Console.WriteLine("\nAfter removing 10583, hash table is\nKey -> Value"); + PrintUtil.PrintHashMap(map); + + /* Traverse hash table */ + Console.WriteLine("\nTraverse key-value pairs Key->Value"); + foreach (var kv in map) { + Console.WriteLine(kv.Key + " -> " + kv.Value); + } + Console.WriteLine("\nTraverse keys only Key"); + foreach (int key in map.Keys) { + Console.WriteLine(key); + } + Console.WriteLine("\nTraverse values only Value"); + foreach (string val in map.Values) { + Console.WriteLine(val); + } + } +} diff --git a/en/codes/csharp/chapter_hashing/hash_map_chaining.cs b/en/codes/csharp/chapter_hashing/hash_map_chaining.cs new file mode 100644 index 000000000..d8600f6c1 --- /dev/null +++ b/en/codes/csharp/chapter_hashing/hash_map_chaining.cs @@ -0,0 +1,144 @@ +/** +* File: hash_map_chaining.cs +* Created Time: 2023-06-26 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_hashing; + +/* Hash table with separate chaining */ +class HashMapChaining { + int size; // Number of key-value pairs + int capacity; // Hash table capacity + double loadThres; // Load factor threshold for triggering expansion + int extendRatio; // Expansion multiplier + List> buckets; // Bucket array + + /* Constructor */ + public HashMapChaining() { + size = 0; + capacity = 4; + loadThres = 2.0 / 3.0; + extendRatio = 2; + buckets = new List>(capacity); + for (int i = 0; i < capacity; i++) { + buckets.Add([]); + } + } + + /* Hash function */ + int HashFunc(int key) { + return key % capacity; + } + + /* Load factor */ + double LoadFactor() { + return (double)size / capacity; + } + + /* Query operation */ + public string? Get(int key) { + int index = HashFunc(key); + // Traverse bucket, if key is found, return corresponding val + foreach (Pair pair in buckets[index]) { + if (pair.key == key) { + return pair.val; + } + } + // If key is not found, return null + return null; + } + + /* Add operation */ + public void Put(int key, string val) { + // When load factor exceeds threshold, perform expansion + if (LoadFactor() > loadThres) { + Extend(); + } + int index = HashFunc(key); + // Traverse bucket, if specified key is encountered, update corresponding val and return + foreach (Pair pair in buckets[index]) { + if (pair.key == key) { + pair.val = val; + return; + } + } + // If key does not exist, append key-value pair to the end + buckets[index].Add(new Pair(key, val)); + size++; + } + + /* Remove operation */ + public void Remove(int key) { + int index = HashFunc(key); + // Traverse bucket and remove key-value pair from it + foreach (Pair pair in buckets[index].ToList()) { + if (pair.key == key) { + buckets[index].Remove(pair); + size--; + break; + } + } + } + + /* Expand hash table */ + void Extend() { + // Temporarily store the original hash table + List> bucketsTmp = buckets; + // Initialize expanded new hash table + capacity *= extendRatio; + buckets = new List>(capacity); + for (int i = 0; i < capacity; i++) { + buckets.Add([]); + } + size = 0; + // Move key-value pairs from original hash table to new hash table + foreach (List bucket in bucketsTmp) { + foreach (Pair pair in bucket) { + Put(pair.key, pair.val); + } + } + } + + /* Print hash table */ + public void Print() { + foreach (List bucket in buckets) { + List res = []; + foreach (Pair pair in bucket) { + res.Add(pair.key + " -> " + pair.val); + } + foreach (string kv in res) { + Console.WriteLine(kv); + } + } + } +} + +public class hash_map_chaining { + [Test] + public void Test() { + /* Initialize hash table */ + HashMapChaining map = new(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.Put(12836, "Xiao Ha"); + map.Put(15937, "Xiao Luo"); + map.Put(16750, "Xiao Suan"); + map.Put(13276, "Xiao Fa"); + map.Put(10583, "Xiao Ya"); + Console.WriteLine("\nAfter adding is complete, hash table is\nKey -> Value"); + map.Print(); + + /* Query operation */ + // Input key into hash table to get value + string? name = map.Get(13276); + Console.WriteLine("\nInput student ID 13276, query name " + name); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.Remove(12836); + Console.WriteLine("\nAfter removing 12836, hash table is\nKey -> Value"); + map.Print(); + } +} \ No newline at end of file diff --git a/en/codes/csharp/chapter_hashing/hash_map_open_addressing.cs b/en/codes/csharp/chapter_hashing/hash_map_open_addressing.cs new file mode 100644 index 000000000..e3cdd1591 --- /dev/null +++ b/en/codes/csharp/chapter_hashing/hash_map_open_addressing.cs @@ -0,0 +1,159 @@ +/** +* File: hash_map_open_addressing.cs +* Created Time: 2023-06-26 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_hashing; + +/* Hash table with open addressing */ +class HashMapOpenAddressing { + int size; // Number of key-value pairs + int capacity = 4; // Hash table capacity + double loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion + int extendRatio = 2; // Expansion multiplier + Pair[] buckets; // Bucket array + Pair TOMBSTONE = new(-1, "-1"); // Removal marker + + /* Constructor */ + public HashMapOpenAddressing() { + size = 0; + buckets = new Pair[capacity]; + } + + /* Hash function */ + int HashFunc(int key) { + return key % capacity; + } + + /* Load factor */ + double LoadFactor() { + return (double)size / capacity; + } + + /* Search for bucket index corresponding to key */ + int FindBucket(int key) { + int index = HashFunc(key); + int firstTombstone = -1; + // Linear probing, break when encountering an empty bucket + while (buckets[index] != null) { + // If key is encountered, return the corresponding bucket index + if (buckets[index].key == key) { + // If a removal marker was encountered before, move the key-value pair to that index + if (firstTombstone != -1) { + buckets[firstTombstone] = buckets[index]; + buckets[index] = TOMBSTONE; + return firstTombstone; // Return the moved bucket index + } + return index; // Return bucket index + } + // Record the first removal marker encountered + if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { + firstTombstone = index; + } + // Calculate bucket index, wrap around to the head if past the tail + index = (index + 1) % capacity; + } + // If key does not exist, return the index for insertion + return firstTombstone == -1 ? index : firstTombstone; + } + + /* Query operation */ + public string? Get(int key) { + // Search for bucket index corresponding to key + int index = FindBucket(key); + // If key-value pair is found, return corresponding val + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + return buckets[index].val; + } + // If key-value pair does not exist, return null + return null; + } + + /* Add operation */ + public void Put(int key, string val) { + // When load factor exceeds threshold, perform expansion + if (LoadFactor() > loadThres) { + Extend(); + } + // Search for bucket index corresponding to key + int index = FindBucket(key); + // If key-value pair is found, overwrite val and return + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + buckets[index].val = val; + return; + } + // If key-value pair does not exist, add the key-value pair + buckets[index] = new Pair(key, val); + size++; + } + + /* Remove operation */ + public void Remove(int key) { + // Search for bucket index corresponding to key + int index = FindBucket(key); + // If key-value pair is found, overwrite it with removal marker + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + buckets[index] = TOMBSTONE; + size--; + } + } + + /* Expand hash table */ + void Extend() { + // Temporarily store the original hash table + Pair[] bucketsTmp = buckets; + // Initialize expanded new hash table + capacity *= extendRatio; + buckets = new Pair[capacity]; + size = 0; + // Move key-value pairs from original hash table to new hash table + foreach (Pair pair in bucketsTmp) { + if (pair != null && pair != TOMBSTONE) { + Put(pair.key, pair.val); + } + } + } + + /* Print hash table */ + public void Print() { + foreach (Pair pair in buckets) { + if (pair == null) { + Console.WriteLine("null"); + } else if (pair == TOMBSTONE) { + Console.WriteLine("TOMBSTONE"); + } else { + Console.WriteLine(pair.key + " -> " + pair.val); + } + } + } +} + +public class hash_map_open_addressing { + [Test] + public void Test() { + /* Initialize hash table */ + HashMapOpenAddressing map = new(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.Put(12836, "Xiao Ha"); + map.Put(15937, "Xiao Luo"); + map.Put(16750, "Xiao Suan"); + map.Put(13276, "Xiao Fa"); + map.Put(10583, "Xiao Ya"); + Console.WriteLine("\nAfter adding is complete, hash table is\nKey -> Value"); + map.Print(); + + /* Query operation */ + // Input key into hash table to get value + string? name = map.Get(13276); + Console.WriteLine("\nInput student ID 13276, query name " + name); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.Remove(16750); + Console.WriteLine("\nAfter removing 16750, hash table is\nKey -> Value"); + map.Print(); + } +} diff --git a/en/codes/csharp/chapter_hashing/simple_hash.cs b/en/codes/csharp/chapter_hashing/simple_hash.cs new file mode 100644 index 000000000..b18e2294d --- /dev/null +++ b/en/codes/csharp/chapter_hashing/simple_hash.cs @@ -0,0 +1,66 @@ +/** +* File: simple_hash.cs +* Created Time: 2023-06-26 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_hashing; + +public class simple_hash { + /* Additive hash */ + int AddHash(string key) { + long hash = 0; + const int MODULUS = 1000000007; + foreach (char c in key) { + hash = (hash + c) % MODULUS; + } + return (int)hash; + } + + /* Multiplicative hash */ + int MulHash(string key) { + long hash = 0; + const int MODULUS = 1000000007; + foreach (char c in key) { + hash = (31 * hash + c) % MODULUS; + } + return (int)hash; + } + + /* XOR hash */ + int XorHash(string key) { + int hash = 0; + const int MODULUS = 1000000007; + foreach (char c in key) { + hash ^= c; + } + return hash & MODULUS; + } + + /* Rotational hash */ + int RotHash(string key) { + long hash = 0; + const int MODULUS = 1000000007; + foreach (char c in key) { + hash = ((hash << 4) ^ (hash >> 28) ^ c) % MODULUS; + } + return (int)hash; + } + + [Test] + public void Test() { + string key = "Hello Algo"; + + int hash = AddHash(key); + Console.WriteLine("Additive hash value is " + hash); + + hash = MulHash(key); + Console.WriteLine("Multiplicative hash value is " + hash); + + hash = XorHash(key); + Console.WriteLine("XOR hash value is " + hash); + + hash = RotHash(key); + Console.WriteLine("Rotational hash value is " + hash); + } +} diff --git a/en/codes/csharp/chapter_heap/heap.cs b/en/codes/csharp/chapter_heap/heap.cs new file mode 100644 index 000000000..00001f14f --- /dev/null +++ b/en/codes/csharp/chapter_heap/heap.cs @@ -0,0 +1,64 @@ +/** + * File: heap.cs + * Created Time: 2023-02-06 + * Author: zjkung1123 (zjkung1123@gmail.com) + */ + +namespace hello_algo.chapter_heap; + +public class heap { + void TestPush(PriorityQueue heap, int val) { + heap.Enqueue(val, val); // Element enters heap + Console.WriteLine($"\nAfter element {val} pushes to heap\n"); + PrintUtil.PrintHeap(heap); + } + + void TestPop(PriorityQueue heap) { + int val = heap.Dequeue(); // Time complexity is O(n), not O(nlogn) + Console.WriteLine($"\nAfter heap top element {val} pops from heap\n"); + PrintUtil.PrintHeap(heap); + } + + [Test] + public void Test() { + /* Initialize heap */ + // Python's heapq module implements min heap by default + PriorityQueue minHeap = new(); + // Initialize max heap (modify Comparer using lambda expression) + PriorityQueue maxHeap = new(Comparer.Create((x, y) => y.CompareTo(x))); + Console.WriteLine("Following test cases are for max heap"); + + /* Element enters heap */ + TestPush(maxHeap, 1); + TestPush(maxHeap, 3); + TestPush(maxHeap, 2); + TestPush(maxHeap, 5); + TestPush(maxHeap, 4); + + /* Check if heap is empty */ + int peek = maxHeap.Peek(); + Console.WriteLine($"Heap top element is {peek}"); + + /* Time complexity is O(n), not O(nlogn) */ + // Dequeued elements form a descending sequence + TestPop(maxHeap); + TestPop(maxHeap); + TestPop(maxHeap); + TestPop(maxHeap); + TestPop(maxHeap); + + /* Get heap size */ + int size = maxHeap.Count; + Console.WriteLine($"Heap size is {size}"); + + /* Check if heap is empty */ + bool isEmpty = maxHeap.Count == 0; + Console.WriteLine($"Is heap empty {isEmpty}"); + + /* Input list and build heap */ + var list = new int[] { 1, 3, 2, 5, 4 }; + minHeap = new PriorityQueue(list.Select(x => (x, x))); + Console.WriteLine("After input list and building min heap"); + PrintUtil.PrintHeap(minHeap); + } +} diff --git a/en/codes/csharp/chapter_heap/my_heap.cs b/en/codes/csharp/chapter_heap/my_heap.cs new file mode 100644 index 000000000..bd397d813 --- /dev/null +++ b/en/codes/csharp/chapter_heap/my_heap.cs @@ -0,0 +1,160 @@ +/** + * File: my_heap.cs + * Created Time: 2023-02-06 + * Author: zjkung1123 (zjkung1123@gmail.com) + */ + +namespace hello_algo.chapter_heap; + +/* Max heap */ +class MaxHeap { + // Use list instead of array, no need to consider capacity expansion + List maxHeap; + + /* Constructor, build empty heap */ + public MaxHeap() { + maxHeap = []; + } + + /* Constructor, build heap from input list */ + public MaxHeap(IEnumerable nums) { + // Add list elements to heap as is + maxHeap = new List(nums); + // Heapify all nodes except leaf nodes + var size = Parent(this.Size() - 1); + for (int i = size; i >= 0; i--) { + SiftDown(i); + } + } + + /* Get index of left child node */ + int Left(int i) { + return 2 * i + 1; + } + + /* Get index of right child node */ + int Right(int i) { + return 2 * i + 2; + } + + /* Get index of parent node */ + int Parent(int i) { + return (i - 1) / 2; // Floor division + } + + /* Access top element */ + public int Peek() { + return maxHeap[0]; + } + + /* Element enters heap */ + public void Push(int val) { + // Add node + maxHeap.Add(val); + // Heapify from bottom to top + SiftUp(Size() - 1); + } + + /* Get heap size */ + public int Size() { + return maxHeap.Count; + } + + /* Check if heap is empty */ + public bool IsEmpty() { + return Size() == 0; + } + + /* Starting from node i, heapify from bottom to top */ + void SiftUp(int i) { + while (true) { + // Get parent node of node i + int p = Parent(i); + // If 'past root node' or 'node needs no repair', end heapify + if (p < 0 || maxHeap[i] <= maxHeap[p]) + break; + // Swap two nodes + Swap(i, p); + // Loop upward heapify + i = p; + } + } + + /* Element exits heap */ + public int Pop() { + // Handle empty case + if (IsEmpty()) + throw new IndexOutOfRangeException(); + // Delete node + Swap(0, Size() - 1); + // Remove node + int val = maxHeap.Last(); + maxHeap.RemoveAt(Size() - 1); + // Return top element + SiftDown(0); + // Return heap top element + return val; + } + + /* Starting from node i, heapify from top to bottom */ + void SiftDown(int i) { + while (true) { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + int l = Left(i), r = Right(i), ma = i; + if (l < Size() && maxHeap[l] > maxHeap[ma]) + ma = l; + if (r < Size() && maxHeap[r] > maxHeap[ma]) + ma = r; + // If 'node i is largest' or 'past leaf node', end heapify + if (ma == i) break; + // Swap two nodes + Swap(i, ma); + // Loop downwards heapification + i = ma; + } + } + + /* Swap elements */ + void Swap(int i, int p) { + (maxHeap[i], maxHeap[p]) = (maxHeap[p], maxHeap[i]); + } + + /* Driver Code */ + public void Print() { + var queue = new Queue(maxHeap); + PrintUtil.PrintHeap(queue); + } +} + +public class my_heap { + [Test] + public void Test() { + /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + MaxHeap maxHeap = new([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); + Console.WriteLine("\nAfter inputting list and building heap"); + maxHeap.Print(); + + /* Check if heap is empty */ + int peek = maxHeap.Peek(); + Console.WriteLine($"Heap top element is {peek}"); + + /* Element enters heap */ + int val = 7; + maxHeap.Push(val); + Console.WriteLine($"After element {val} pushes to heap"); + maxHeap.Print(); + + /* Time complexity is O(n), not O(nlogn) */ + peek = maxHeap.Pop(); + Console.WriteLine($"After heap top element {peek} pops from heap"); + maxHeap.Print(); + + /* Get heap size */ + int size = maxHeap.Size(); + Console.WriteLine($"Heap size is {size}"); + + /* Check if heap is empty */ + bool isEmpty = maxHeap.IsEmpty(); + Console.WriteLine($"Is heap empty {isEmpty}"); + } +} diff --git a/en/codes/csharp/chapter_heap/top_k.cs b/en/codes/csharp/chapter_heap/top_k.cs new file mode 100644 index 000000000..187af84bc --- /dev/null +++ b/en/codes/csharp/chapter_heap/top_k.cs @@ -0,0 +1,37 @@ +/** +* File: top_k.cs +* Created Time: 2023-06-14 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_heap; + +public class top_k { + /* Find the largest k elements in array based on heap */ + PriorityQueue TopKHeap(int[] nums, int k) { + // Python's heapq module implements min heap by default + PriorityQueue heap = new(); + // Enter the first k elements of array into heap + for (int i = 0; i < k; i++) { + heap.Enqueue(nums[i], nums[i]); + } + // Starting from the (k+1)th element, maintain heap length as k + for (int i = k; i < nums.Length; i++) { + // If current element is greater than top element, top element exits heap, current element enters heap + if (nums[i] > heap.Peek()) { + heap.Dequeue(); + heap.Enqueue(nums[i], nums[i]); + } + } + return heap; + } + + [Test] + public void Test() { + int[] nums = [1, 7, 6, 3, 2]; + int k = 3; + PriorityQueue res = TopKHeap(nums, k); + Console.WriteLine("The largest " + k + " elements are"); + PrintUtil.PrintHeap(res); + } +} diff --git a/en/codes/csharp/chapter_searching/binary_search.cs b/en/codes/csharp/chapter_searching/binary_search.cs new file mode 100644 index 000000000..1f4973d2e --- /dev/null +++ b/en/codes/csharp/chapter_searching/binary_search.cs @@ -0,0 +1,59 @@ +/** + * File: binary_search.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_searching; + +public class binary_search { + /* Binary search (closed interval on both sides) */ + int BinarySearch(int[] nums, int target) { + // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + int i = 0, j = nums.Length - 1; + // Loop, exit when the search interval is empty (empty when i > j) + while (i <= j) { + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) // This means target is in the interval [m+1, j] + i = m + 1; + else if (nums[m] > target) // This means target is in the interval [i, m-1] + j = m - 1; + else // Found the target element, return its index + return m; + } + // Target element not found, return -1 + return -1; + } + + /* Binary search (left-closed right-open interval) */ + int BinarySearchLCRO(int[] nums, int target) { + // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + int i = 0, j = nums.Length; + // Loop, exit when the search interval is empty (empty when i = j) + while (i < j) { + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) // This means target is in the interval [m+1, j) + i = m + 1; + else if (nums[m] > target) // This means target is in the interval [i, m) + j = m; + else // Found the target element, return its index + return m; + } + // Target element not found, return -1 + return -1; + } + + [Test] + public void Test() { + int target = 6; + int[] nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; + + /* Binary search (closed interval on both sides) */ + int index = BinarySearch(nums, target); + Console.WriteLine("Index of target element 6 = " + index); + + /* Binary search (left-closed right-open interval) */ + index = BinarySearchLCRO(nums, target); + Console.WriteLine("Index of target element 6 = " + index); + } +} diff --git a/en/codes/csharp/chapter_searching/binary_search_edge.cs b/en/codes/csharp/chapter_searching/binary_search_edge.cs new file mode 100644 index 000000000..70584c60f --- /dev/null +++ b/en/codes/csharp/chapter_searching/binary_search_edge.cs @@ -0,0 +1,50 @@ +/** +* File: binary_search_edge.cs +* Created Time: 2023-08-06 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_searching; + +public class binary_search_edge { + /* Binary search for the leftmost target */ + int BinarySearchLeftEdge(int[] nums, int target) { + // Equivalent to finding the insertion point of target + int i = binary_search_insertion.BinarySearchInsertion(nums, target); + // Target not found, return -1 + if (i == nums.Length || nums[i] != target) { + return -1; + } + // Found target, return index i + return i; + } + + /* Binary search for the rightmost target */ + int BinarySearchRightEdge(int[] nums, int target) { + // Convert to finding the leftmost target + 1 + int i = binary_search_insertion.BinarySearchInsertion(nums, target + 1); + // j points to the rightmost target, i points to the first element greater than target + int j = i - 1; + // Target not found, return -1 + if (j == -1 || nums[j] != target) { + return -1; + } + // Found target, return index j + return j; + } + + [Test] + public void Test() { + // Array with duplicate elements + int[] nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; + Console.WriteLine("\nArray nums = " + nums.PrintList()); + + // Binary search left and right boundaries + foreach (int target in new int[] { 6, 7 }) { + int index = BinarySearchLeftEdge(nums, target); + Console.WriteLine("Leftmost element " + target + " has index " + index); + index = BinarySearchRightEdge(nums, target); + Console.WriteLine("Rightmost element " + target + " has index " + index); + } + } +} diff --git a/en/codes/csharp/chapter_searching/binary_search_insertion.cs b/en/codes/csharp/chapter_searching/binary_search_insertion.cs new file mode 100644 index 000000000..3c5519550 --- /dev/null +++ b/en/codes/csharp/chapter_searching/binary_search_insertion.cs @@ -0,0 +1,64 @@ +/** +* File: binary_search_insertion.cs +* Created Time: 2023-08-06 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_searching; + +public class binary_search_insertion { + /* Binary search for insertion point (no duplicate elements) */ + public static int BinarySearchInsertionSimple(int[] nums, int target) { + int i = 0, j = nums.Length - 1; // Initialize closed interval [0, n-1] + while (i <= j) { + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) { + i = m + 1; // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // target is in the interval [i, m-1] + } else { + return m; // Found target, return insertion point m + } + } + // Target not found, return insertion point i + return i; + } + + /* Binary search for insertion point (with duplicate elements) */ + public static int BinarySearchInsertion(int[] nums, int target) { + int i = 0, j = nums.Length - 1; // Initialize closed interval [0, n-1] + while (i <= j) { + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) { + i = m + 1; // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // target is in the interval [i, m-1] + } else { + j = m - 1; // The first element less than target is in the interval [i, m-1] + } + } + // Return insertion point i + return i; + } + + [Test] + public void Test() { + // Array without duplicate elements + int[] nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; + Console.WriteLine("\nArray nums = " + nums.PrintList()); + // Binary search for insertion point + foreach (int target in new int[] { 6, 9 }) { + int index = BinarySearchInsertionSimple(nums, target); + Console.WriteLine("Element " + target + "'s insertion point index is " + index); + } + + // Array with duplicate elements + nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; + Console.WriteLine("\nArray nums = " + nums.PrintList()); + // Binary search for insertion point + foreach (int target in new int[] { 2, 6, 20 }) { + int index = BinarySearchInsertion(nums, target); + Console.WriteLine("Element " + target + "'s insertion point index is " + index); + } + } +} diff --git a/en/codes/csharp/chapter_searching/hashing_search.cs b/en/codes/csharp/chapter_searching/hashing_search.cs new file mode 100644 index 000000000..7f6bc8c76 --- /dev/null +++ b/en/codes/csharp/chapter_searching/hashing_search.cs @@ -0,0 +1,50 @@ +/** + * File: hashing_search.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_searching; + +public class hashing_search { + /* Hash search (array) */ + int HashingSearchArray(Dictionary map, int target) { + // Hash table's key: target element, value: index + // If this key does not exist in the hash table, return -1 + return map.GetValueOrDefault(target, -1); + } + + /* Hash search (linked list) */ + ListNode? HashingSearchLinkedList(Dictionary map, int target) { + + // Hash table key: target node value, value: node object + // If key is not in hash table, return null + return map.GetValueOrDefault(target); + } + + [Test] + public void Test() { + int target = 3; + + /* Hash search (array) */ + int[] nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; + // Initialize hash table + Dictionary map = []; + for (int i = 0; i < nums.Length; i++) { + map[nums[i]] = i; // key: element, value: index + } + int index = HashingSearchArray(map, target); + Console.WriteLine("Index of target element 3 = " + index); + + /* Hash search (linked list) */ + ListNode? head = ListNode.ArrToLinkedList(nums); + // Initialize hash table + Dictionary map1 = []; + while (head != null) { + map1[head.val] = head; // key: node value, value: node + head = head.next; + } + ListNode? node = HashingSearchLinkedList(map1, target); + Console.WriteLine("Node object corresponding to target node value 3 is " + node); + } +} diff --git a/en/codes/csharp/chapter_searching/linear_search.cs b/en/codes/csharp/chapter_searching/linear_search.cs new file mode 100644 index 000000000..046da13d4 --- /dev/null +++ b/en/codes/csharp/chapter_searching/linear_search.cs @@ -0,0 +1,49 @@ +/** + * File: linear_search.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_searching; + +public class linear_search { + /* Linear search (array) */ + int LinearSearchArray(int[] nums, int target) { + // Traverse array + for (int i = 0; i < nums.Length; i++) { + // Found the target element, return its index + if (nums[i] == target) + return i; + } + // Target element not found, return -1 + return -1; + } + + /* Linear search (linked list) */ + ListNode? LinearSearchLinkedList(ListNode? head, int target) { + // Traverse the linked list + while (head != null) { + // Found the target node, return it + if (head.val == target) + return head; + head = head.next; + } + // Target node not found, return null + return null; + } + + [Test] + public void Test() { + int target = 3; + + /* Perform linear search in array */ + int[] nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; + int index = LinearSearchArray(nums, target); + Console.WriteLine("Index of target element 3 = " + index); + + /* Perform linear search in linked list */ + ListNode? head = ListNode.ArrToLinkedList(nums); + ListNode? node = LinearSearchLinkedList(head, target); + Console.WriteLine("Node object corresponding to target node value 3 is " + node); + } +} diff --git a/en/codes/csharp/chapter_searching/two_sum.cs b/en/codes/csharp/chapter_searching/two_sum.cs new file mode 100644 index 000000000..9f8e63a5a --- /dev/null +++ b/en/codes/csharp/chapter_searching/two_sum.cs @@ -0,0 +1,52 @@ +/** + * File: two_sum.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_searching; + +public class two_sum { + /* Method 1: Brute force enumeration */ + int[] TwoSumBruteForce(int[] nums, int target) { + int size = nums.Length; + // Two nested loops, time complexity is O(n^2) + for (int i = 0; i < size - 1; i++) { + for (int j = i + 1; j < size; j++) { + if (nums[i] + nums[j] == target) + return [i, j]; + } + } + return []; + } + + /* Method 2: Auxiliary hash table */ + int[] TwoSumHashTable(int[] nums, int target) { + int size = nums.Length; + // Auxiliary hash table, space complexity is O(n) + Dictionary dic = []; + // Single loop, time complexity is O(n) + for (int i = 0; i < size; i++) { + if (dic.ContainsKey(target - nums[i])) { + return [dic[target - nums[i]], i]; + } + dic.Add(nums[i], i); + } + return []; + } + + [Test] + public void Test() { + // ======= Test Case ======= + int[] nums = [2, 7, 11, 15]; + int target = 13; + + // ====== Driver Code ====== + // Method 1 + int[] res = TwoSumBruteForce(nums, target); + Console.WriteLine("Method 1 res = " + string.Join(",", res)); + // Method 2 + res = TwoSumHashTable(nums, target); + Console.WriteLine("Method 2 res = " + string.Join(",", res)); + } +} diff --git a/en/codes/csharp/chapter_sorting/bubble_sort.cs b/en/codes/csharp/chapter_sorting/bubble_sort.cs new file mode 100644 index 000000000..97e2103d9 --- /dev/null +++ b/en/codes/csharp/chapter_sorting/bubble_sort.cs @@ -0,0 +1,51 @@ +/** + * File: bubble_sort.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_sorting; + +public class bubble_sort { + /* Bubble sort */ + void BubbleSort(int[] nums) { + // Outer loop: unsorted range is [0, i] + for (int i = nums.Length - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]); + } + } + } + } + + /* Bubble sort (flag optimization) */ + void BubbleSortWithFlag(int[] nums) { + // Outer loop: unsorted range is [0, i] + for (int i = nums.Length - 1; i > 0; i--) { + bool flag = false; // Initialize flag + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]); + flag = true; // Record element swap + } + } + if (!flag) break; // No elements were swapped in this round of "bubbling", exit directly + } + } + + [Test] + public void Test() { + int[] nums = [4, 1, 3, 1, 5, 2]; + BubbleSort(nums); + Console.WriteLine("After bubble sort, nums = " + string.Join(",", nums)); + + int[] nums1 = [4, 1, 3, 1, 5, 2]; + BubbleSortWithFlag(nums1); + Console.WriteLine("After bubble sort completes, nums1 = " + string.Join(",", nums1)); + } +} diff --git a/en/codes/csharp/chapter_sorting/bucket_sort.cs b/en/codes/csharp/chapter_sorting/bucket_sort.cs new file mode 100644 index 000000000..8e75dc9b6 --- /dev/null +++ b/en/codes/csharp/chapter_sorting/bucket_sort.cs @@ -0,0 +1,46 @@ +/** + * File: bucket_sort.cs + * Created Time: 2023-04-13 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_sorting; + +public class bucket_sort { + /* Bucket sort */ + void BucketSort(float[] nums) { + // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + int k = nums.Length / 2; + List> buckets = []; + for (int i = 0; i < k; i++) { + buckets.Add([]); + } + // 1. Distribute array elements into various buckets + foreach (float num in nums) { + // Input data range is [0, 1), use num * k to map to index range [0, k-1] + int i = (int)(num * k); + // Add num to bucket i + buckets[i].Add(num); + } + // 2. Sort each bucket + foreach (List bucket in buckets) { + // Use built-in sorting function, can also replace with other sorting algorithms + bucket.Sort(); + } + // 3. Traverse buckets to merge results + int j = 0; + foreach (List bucket in buckets) { + foreach (float num in bucket) { + nums[j++] = num; + } + } + } + + [Test] + public void Test() { + // Assume input data is floating point, interval [0, 1) + float[] nums = [0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f]; + BucketSort(nums); + Console.WriteLine("After bucket sort completes, nums = " + string.Join(" ", nums)); + } +} diff --git a/en/codes/csharp/chapter_sorting/counting_sort.cs b/en/codes/csharp/chapter_sorting/counting_sort.cs new file mode 100644 index 000000000..3fc363517 --- /dev/null +++ b/en/codes/csharp/chapter_sorting/counting_sort.cs @@ -0,0 +1,77 @@ +/** + * File: counting_sort.cs + * Created Time: 2023-04-13 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_sorting; + +public class counting_sort { + /* Counting sort */ + // Simple implementation, cannot be used for sorting objects + void CountingSortNaive(int[] nums) { + // 1. Count the maximum element m in the array + int m = 0; + foreach (int num in nums) { + m = Math.Max(m, num); + } + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + int[] counter = new int[m + 1]; + foreach (int num in nums) { + counter[num]++; + } + // 3. Traverse counter, filling each element back into the original array nums + int i = 0; + for (int num = 0; num < m + 1; num++) { + for (int j = 0; j < counter[num]; j++, i++) { + nums[i] = num; + } + } + } + + /* Counting sort */ + // Complete implementation, can sort objects and is a stable sort + void CountingSort(int[] nums) { + // 1. Count the maximum element m in the array + int m = 0; + foreach (int num in nums) { + m = Math.Max(m, num); + } + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + int[] counter = new int[m + 1]; + foreach (int num in nums) { + counter[num]++; + } + // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + // counter[num]-1 is the last index where num appears in res + for (int i = 0; i < m; i++) { + counter[i + 1] += counter[i]; + } + // 4. Traverse nums in reverse order, placing each element into the result array res + // Initialize the array res to record results + int n = nums.Length; + int[] res = new int[n]; + for (int i = n - 1; i >= 0; i--) { + int num = nums[i]; + res[counter[num] - 1] = num; // Place num at the corresponding index + counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num + } + // Use result array res to overwrite the original array nums + for (int i = 0; i < n; i++) { + nums[i] = res[i]; + } + } + + [Test] + public void Test() { + int[] nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; + CountingSortNaive(nums); + Console.WriteLine("After counting sort (cannot sort objects) completes, nums = " + string.Join(" ", nums)); + + int[] nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; + CountingSort(nums1); + Console.WriteLine("After counting sort completes, nums1 = " + string.Join(" ", nums)); + } +} diff --git a/en/codes/csharp/chapter_sorting/heap_sort.cs b/en/codes/csharp/chapter_sorting/heap_sort.cs new file mode 100644 index 000000000..d0ea610ab --- /dev/null +++ b/en/codes/csharp/chapter_sorting/heap_sort.cs @@ -0,0 +1,52 @@ +/** +* File: heap_sort.cs +* Created Time: 2023-06-01 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_sorting; + +public class heap_sort { + /* Heap length is n, start heapifying node i, from top to bottom */ + void SiftDown(int[] nums, int n, int i) { + while (true) { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + int l = 2 * i + 1; + int r = 2 * i + 2; + int ma = i; + if (l < n && nums[l] > nums[ma]) + ma = l; + if (r < n && nums[r] > nums[ma]) + ma = r; + // Swap two nodes + if (ma == i) + break; + // Swap two nodes + (nums[ma], nums[i]) = (nums[i], nums[ma]); + // Loop downwards heapification + i = ma; + } + } + + /* Heap sort */ + void HeapSort(int[] nums) { + // Build heap operation: heapify all nodes except leaves + for (int i = nums.Length / 2 - 1; i >= 0; i--) { + SiftDown(nums, nums.Length, i); + } + // Extract the largest element from the heap and repeat for n-1 rounds + for (int i = nums.Length - 1; i > 0; i--) { + // Delete node + (nums[i], nums[0]) = (nums[0], nums[i]); + // Start heapifying the root node, from top to bottom + SiftDown(nums, i, 0); + } + } + + [Test] + public void Test() { + int[] nums = [4, 1, 3, 1, 5, 2]; + HeapSort(nums); + Console.WriteLine("After heap sort completes, nums = " + string.Join(" ", nums)); + } +} diff --git a/en/codes/csharp/chapter_sorting/insertion_sort.cs b/en/codes/csharp/chapter_sorting/insertion_sort.cs new file mode 100644 index 000000000..1290ccd49 --- /dev/null +++ b/en/codes/csharp/chapter_sorting/insertion_sort.cs @@ -0,0 +1,30 @@ +/** + * File: insertion_sort.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_sorting; + +public class insertion_sort { + /* Insertion sort */ + void InsertionSort(int[] nums) { + // Outer loop: sorted interval is [0, i-1] + for (int i = 1; i < nums.Length; i++) { + int bas = nums[i], j = i - 1; + // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + while (j >= 0 && nums[j] > bas) { + nums[j + 1] = nums[j]; // Move nums[j] to the right by one position + j--; + } + nums[j + 1] = bas; // Assign base to the correct position + } + } + + [Test] + public void Test() { + int[] nums = [4, 1, 3, 1, 5, 2]; + InsertionSort(nums); + Console.WriteLine("After insertion sort completes, nums = " + string.Join(",", nums)); + } +} diff --git a/en/codes/csharp/chapter_sorting/merge_sort.cs b/en/codes/csharp/chapter_sorting/merge_sort.cs new file mode 100644 index 000000000..8eb181231 --- /dev/null +++ b/en/codes/csharp/chapter_sorting/merge_sort.cs @@ -0,0 +1,56 @@ +/** + * File: merge_sort.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_sorting; + +public class merge_sort { + /* Merge left subarray and right subarray */ + void Merge(int[] nums, int left, int mid, int right) { + // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + // Create a temporary array tmp to store the merged results + int[] tmp = new int[right - left + 1]; + // Initialize the start indices of the left and right subarrays + int i = left, j = mid + 1, k = 0; + // While both subarrays still have elements, compare and copy the smaller element into the temporary array + while (i <= mid && j <= right) { + if (nums[i] <= nums[j]) + tmp[k++] = nums[i++]; + else + tmp[k++] = nums[j++]; + } + // Copy the remaining elements of the left and right subarrays into the temporary array + while (i <= mid) { + tmp[k++] = nums[i++]; + } + while (j <= right) { + tmp[k++] = nums[j++]; + } + // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + for (k = 0; k < tmp.Length; ++k) { + nums[left + k] = tmp[k]; + } + } + + /* Merge sort */ + void MergeSort(int[] nums, int left, int right) { + // Termination condition + if (left >= right) return; // Terminate recursion when subarray length is 1 + // Divide and conquer stage + int mid = left + (right - left) / 2; // Calculate midpoint + MergeSort(nums, left, mid); // Recursively process the left subarray + MergeSort(nums, mid + 1, right); // Recursively process the right subarray + // Merge stage + Merge(nums, left, mid, right); + } + + [Test] + public void Test() { + /* Merge sort */ + int[] nums = [7, 3, 2, 6, 0, 1, 5, 4]; + MergeSort(nums, 0, nums.Length - 1); + Console.WriteLine("After merge sort completes, nums = " + string.Join(",", nums)); + } +} diff --git a/en/codes/csharp/chapter_sorting/quick_sort.cs b/en/codes/csharp/chapter_sorting/quick_sort.cs new file mode 100644 index 000000000..50c90cccd --- /dev/null +++ b/en/codes/csharp/chapter_sorting/quick_sort.cs @@ -0,0 +1,150 @@ +/** + * File: quick_sort.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_sorting; + +class quickSort { + /* Swap elements */ + static void Swap(int[] nums, int i, int j) { + (nums[j], nums[i]) = (nums[i], nums[j]); + } + + /* Sentinel partition */ + static int Partition(int[] nums, int left, int right) { + // Use nums[left] as the pivot + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j--; // Search from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) + i++; // Search from left to right for the first element greater than the pivot + Swap(nums, i, j); // Swap these two elements + } + Swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort */ + public static void QuickSort(int[] nums, int left, int right) { + // Terminate recursion when subarray length is 1 + if (left >= right) + return; + // Sentinel partition + int pivot = Partition(nums, left, right); + // Recursively process the left subarray and right subarray + QuickSort(nums, left, pivot - 1); + QuickSort(nums, pivot + 1, right); + } +} + +/* Quick sort class (median pivot optimization) */ +class QuickSortMedian { + /* Swap elements */ + static void Swap(int[] nums, int i, int j) { + (nums[j], nums[i]) = (nums[i], nums[j]); + } + + /* Select the median of three candidate elements */ + static int MedianThree(int[] nums, int left, int mid, int right) { + int l = nums[left], m = nums[mid], r = nums[right]; + if ((l <= m && m <= r) || (r <= m && m <= l)) + return mid; // m is between l and r + if ((m <= l && l <= r) || (r <= l && l <= m)) + return left; // l is between m and r + return right; + } + + /* Sentinel partition (median of three) */ + static int Partition(int[] nums, int left, int right) { + // Select the median of three candidate elements + int med = MedianThree(nums, left, (left + right) / 2, right); + // Swap the median to the array's leftmost position + Swap(nums, left, med); + // Use nums[left] as the pivot + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j--; // Search from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) + i++; // Search from left to right for the first element greater than the pivot + Swap(nums, i, j); // Swap these two elements + } + Swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort */ + public static void QuickSort(int[] nums, int left, int right) { + // Terminate recursion when subarray length is 1 + if (left >= right) + return; + // Sentinel partition + int pivot = Partition(nums, left, right); + // Recursively process the left subarray and right subarray + QuickSort(nums, left, pivot - 1); + QuickSort(nums, pivot + 1, right); + } +} + +/* Quick sort class (recursion depth optimization) */ +class QuickSortTailCall { + /* Swap elements */ + static void Swap(int[] nums, int i, int j) { + (nums[j], nums[i]) = (nums[i], nums[j]); + } + + /* Sentinel partition */ + static int Partition(int[] nums, int left, int right) { + // Use nums[left] as the pivot + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j--; // Search from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) + i++; // Search from left to right for the first element greater than the pivot + Swap(nums, i, j); // Swap these two elements + } + Swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort (recursion depth optimization) */ + public static void QuickSort(int[] nums, int left, int right) { + // Terminate when subarray length is 1 + while (left < right) { + // Sentinel partition operation + int pivot = Partition(nums, left, right); + // Perform quick sort on the shorter of the two subarrays + if (pivot - left < right - pivot) { + QuickSort(nums, left, pivot - 1); // Recursively sort the left subarray + left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + } else { + QuickSort(nums, pivot + 1, right); // Recursively sort the right subarray + right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + } + } + } +} + +public class quick_sort { + [Test] + public void Test() { + /* Quick sort */ + int[] nums = [2, 4, 1, 0, 3, 5]; + quickSort.QuickSort(nums, 0, nums.Length - 1); + Console.WriteLine("After quick sort completes, nums = " + string.Join(",", nums)); + + /* Quick sort (recursion depth optimization) */ + int[] nums1 = [2, 4, 1, 0, 3, 5]; + QuickSortMedian.QuickSort(nums1, 0, nums1.Length - 1); + Console.WriteLine("After quick sort (median pivot optimization) completes, nums1 = " + string.Join(",", nums1)); + + /* Quick sort (recursion depth optimization) */ + int[] nums2 = [2, 4, 1, 0, 3, 5]; + QuickSortTailCall.QuickSort(nums2, 0, nums2.Length - 1); + Console.WriteLine("After quick sort (recursion depth optimization) completes, nums2 = " + string.Join(",", nums2)); + } +} diff --git a/en/codes/csharp/chapter_sorting/radix_sort.cs b/en/codes/csharp/chapter_sorting/radix_sort.cs new file mode 100644 index 000000000..a3d51cd26 --- /dev/null +++ b/en/codes/csharp/chapter_sorting/radix_sort.cs @@ -0,0 +1,69 @@ +/** + * File: radix_sort.cs + * Created Time: 2023-04-13 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_sorting; + +public class radix_sort { + /* Get the k-th digit of element num, where exp = 10^(k-1) */ + int Digit(int num, int exp) { + // Passing exp instead of k can avoid repeated expensive exponentiation here + return (num / exp) % 10; + } + + /* Counting sort (based on nums k-th digit) */ + void CountingSortDigit(int[] nums, int exp) { + // Decimal digit range is 0~9, therefore need a bucket array of length 10 + int[] counter = new int[10]; + int n = nums.Length; + // Count the occurrence of digits 0~9 + for (int i = 0; i < n; i++) { + int d = Digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d + counter[d]++; // Count the occurrence of digit d + } + // Calculate prefix sum, converting "occurrence count" into "array index" + for (int i = 1; i < 10; i++) { + counter[i] += counter[i - 1]; + } + // Traverse in reverse, based on bucket statistics, place each element into res + int[] res = new int[n]; + for (int i = n - 1; i >= 0; i--) { + int d = Digit(nums[i], exp); + int j = counter[d] - 1; // Get the index j for d in the array + res[j] = nums[i]; // Place the current element at index j + counter[d]--; // Decrease the count of d by 1 + } + // Use result to overwrite the original array nums + for (int i = 0; i < n; i++) { + nums[i] = res[i]; + } + } + + /* Radix sort */ + void RadixSort(int[] nums) { + // Get the maximum element of the array, used to determine the maximum number of digits + int m = int.MinValue; + foreach (int num in nums) { + if (num > m) m = num; + } + // Traverse from the lowest to the highest digit + for (int exp = 1; exp <= m; exp *= 10) { + // Perform counting sort on the k-th digit of array elements + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // i.e., exp = 10^(k-1) + CountingSortDigit(nums, exp); + } + } + + [Test] + public void Test() { + // Radix sort + int[] nums = [ 10546151, 35663510, 42865989, 34862445, 81883077, + 88906420, 72429244, 30524779, 82060337, 63832996 ]; + RadixSort(nums); + Console.WriteLine("After radix sort completes, nums = " + string.Join(" ", nums)); + } +} diff --git a/en/codes/csharp/chapter_sorting/selection_sort.cs b/en/codes/csharp/chapter_sorting/selection_sort.cs new file mode 100644 index 000000000..ec6b676e1 --- /dev/null +++ b/en/codes/csharp/chapter_sorting/selection_sort.cs @@ -0,0 +1,32 @@ +/** +* File: selection_sort.cs +* Created Time: 2023-06-01 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_sorting; + +public class selection_sort { + /* Selection sort */ + void SelectionSort(int[] nums) { + int n = nums.Length; + // Outer loop: unsorted interval is [i, n-1] + for (int i = 0; i < n - 1; i++) { + // Inner loop: find the smallest element within the unsorted interval + int k = i; + for (int j = i + 1; j < n; j++) { + if (nums[j] < nums[k]) + k = j; // Record the index of the smallest element + } + // Swap the smallest element with the first element of the unsorted interval + (nums[k], nums[i]) = (nums[i], nums[k]); + } + } + + [Test] + public void Test() { + int[] nums = [4, 1, 3, 1, 5, 2]; + SelectionSort(nums); + Console.WriteLine("After selection sort completes, nums = " + string.Join(" ", nums)); + } +} diff --git a/en/codes/csharp/chapter_stack_and_queue/array_deque.cs b/en/codes/csharp/chapter_stack_and_queue/array_deque.cs new file mode 100644 index 000000000..ffc391f7b --- /dev/null +++ b/en/codes/csharp/chapter_stack_and_queue/array_deque.cs @@ -0,0 +1,152 @@ +/** + * File: array_deque.cs + * Created Time: 2023-03-08 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_stack_and_queue; + +/* Double-ended queue based on circular array implementation */ +public class ArrayDeque { + int[] nums; // Array for storing double-ended queue elements + int front; // Front pointer, points to the front of the queue element + int queSize; // Double-ended queue length + + /* Constructor */ + public ArrayDeque(int capacity) { + nums = new int[capacity]; + front = queSize = 0; + } + + /* Get the capacity of the double-ended queue */ + int Capacity() { + return nums.Length; + } + + /* Get the length of the double-ended queue */ + public int Size() { + return queSize; + } + + /* Check if the double-ended queue is empty */ + public bool IsEmpty() { + return queSize == 0; + } + + /* Calculate circular array index */ + int Index(int i) { + // Use modulo operation to wrap the array head and tail together + // When i passes the tail of the array, return to the head + // When i passes the head of the array, return to the tail + return (i + Capacity()) % Capacity(); + } + + /* Front of the queue enqueue */ + public void PushFirst(int num) { + if (queSize == Capacity()) { + Console.WriteLine("Double-ended queue is full"); + return; + } + // Use modulo operation to wrap front around to the tail after passing the head of the array + // Add num to the front of the queue + front = Index(front - 1); + // Add num to front of queue + nums[front] = num; + queSize++; + } + + /* Rear of the queue enqueue */ + public void PushLast(int num) { + if (queSize == Capacity()) { + Console.WriteLine("Double-ended queue is full"); + return; + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + int rear = Index(front + queSize); + // Front pointer moves one position backward + nums[rear] = num; + queSize++; + } + + /* Rear of the queue dequeue */ + public int PopFirst() { + int num = PeekFirst(); + // Move front pointer backward by one position + front = Index(front + 1); + queSize--; + return num; + } + + /* Access rear of the queue element */ + public int PopLast() { + int num = PeekLast(); + queSize--; + return num; + } + + /* Return list for printing */ + public int PeekFirst() { + if (IsEmpty()) { + throw new InvalidOperationException(); + } + return nums[front]; + } + + /* Driver Code */ + public int PeekLast() { + if (IsEmpty()) { + throw new InvalidOperationException(); + } + // Initialize double-ended queue + int last = Index(front + queSize - 1); + return nums[last]; + } + + /* Return array for printing */ + public int[] ToArray() { + // Elements enqueue + int[] res = new int[queSize]; + for (int i = 0, j = front; i < queSize; i++, j++) { + res[i] = nums[Index(j)]; + } + return res; + } +} + +public class array_deque { + [Test] + public void Test() { + /* Get the length of the double-ended queue */ + ArrayDeque deque = new(10); + deque.PushLast(3); + deque.PushLast(2); + deque.PushLast(5); + Console.WriteLine("Double-ended queue deque = " + string.Join(" ", deque.ToArray())); + + /* Update element */ + int peekFirst = deque.PeekFirst(); + Console.WriteLine("Front element peekFirst = " + peekFirst); + int peekLast = deque.PeekLast(); + Console.WriteLine("Rear element peekLast = " + peekLast); + + /* Elements enqueue */ + deque.PushLast(4); + Console.WriteLine("After element 4 enqueues at rear, deque = " + string.Join(" ", deque.ToArray())); + deque.PushFirst(1); + Console.WriteLine("After element 1 enqueues at front, deque = " + string.Join(" ", deque.ToArray())); + + /* Element dequeue */ + int popLast = deque.PopLast(); + Console.WriteLine("Rear dequeue element = " + popLast + ", after rear dequeue, deque = " + string.Join(" ", deque.ToArray())); + int popFirst = deque.PopFirst(); + Console.WriteLine("Front dequeue element = " + popFirst + ", after front dequeue, deque = " + string.Join(" ", deque.ToArray())); + + /* Get the length of the double-ended queue */ + int size = deque.Size(); + Console.WriteLine("Double-ended queue length size = " + size); + + /* Check if the double-ended queue is empty */ + bool isEmpty = deque.IsEmpty(); + Console.WriteLine("Double-ended queue is empty = " + isEmpty); + } +} diff --git a/en/codes/csharp/chapter_stack_and_queue/array_queue.cs b/en/codes/csharp/chapter_stack_and_queue/array_queue.cs new file mode 100644 index 000000000..ee897a8c3 --- /dev/null +++ b/en/codes/csharp/chapter_stack_and_queue/array_queue.cs @@ -0,0 +1,114 @@ +/** + * File: array_queue.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_stack_and_queue; + +/* Queue based on circular array implementation */ +class ArrayQueue { + int[] nums; // Array for storing queue elements + int front; // Front pointer, points to the front of the queue element + int queSize; // Queue length + + public ArrayQueue(int capacity) { + nums = new int[capacity]; + front = queSize = 0; + } + + /* Get the capacity of the queue */ + int Capacity() { + return nums.Length; + } + + /* Get the length of the queue */ + public int Size() { + return queSize; + } + + /* Check if the queue is empty */ + public bool IsEmpty() { + return queSize == 0; + } + + /* Enqueue */ + public void Push(int num) { + if (queSize == Capacity()) { + Console.WriteLine("Queue is full"); + return; + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Add num to the rear of the queue + int rear = (front + queSize) % Capacity(); + // Front pointer moves one position backward + nums[rear] = num; + queSize++; + } + + /* Dequeue */ + public int Pop() { + int num = Peek(); + // Move front pointer backward by one position, if it passes the tail, return to array head + front = (front + 1) % Capacity(); + queSize--; + return num; + } + + /* Return list for printing */ + public int Peek() { + if (IsEmpty()) + throw new Exception(); + return nums[front]; + } + + /* Return array */ + public int[] ToArray() { + // Elements enqueue + int[] res = new int[queSize]; + for (int i = 0, j = front; i < queSize; i++, j++) { + res[i] = nums[j % this.Capacity()]; + } + return res; + } +} + +public class array_queue { + [Test] + public void Test() { + /* Access front of the queue element */ + int capacity = 10; + ArrayQueue queue = new(capacity); + + /* Elements enqueue */ + queue.Push(1); + queue.Push(3); + queue.Push(2); + queue.Push(5); + queue.Push(4); + Console.WriteLine("Queue queue = " + string.Join(",", queue.ToArray())); + + /* Return list for printing */ + int peek = queue.Peek(); + Console.WriteLine("Front element peek = " + peek); + + /* Element dequeue */ + int pop = queue.Pop(); + Console.WriteLine("Dequeue element pop = " + pop + ", after dequeue, queue = " + string.Join(",", queue.ToArray())); + + /* Get the length of the queue */ + int size = queue.Size(); + Console.WriteLine("Queue length size = " + size); + + /* Check if the queue is empty */ + bool isEmpty = queue.IsEmpty(); + Console.WriteLine("Queue is empty = " + isEmpty); + + /* Test circular array */ + for (int i = 0; i < 10; i++) { + queue.Push(i); + queue.Pop(); + Console.WriteLine("Round " + i + " enqueue + dequeue, queue = " + string.Join(",", queue.ToArray())); + } + } +} diff --git a/en/codes/csharp/chapter_stack_and_queue/array_stack.cs b/en/codes/csharp/chapter_stack_and_queue/array_stack.cs new file mode 100644 index 000000000..798b5c6e2 --- /dev/null +++ b/en/codes/csharp/chapter_stack_and_queue/array_stack.cs @@ -0,0 +1,84 @@ +/** + * File: array_stack.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_stack_and_queue; + +/* Stack based on array implementation */ +class ArrayStack { + List stack; + public ArrayStack() { + // Initialize list (dynamic array) + stack = []; + } + + /* Get the length of the stack */ + public int Size() { + return stack.Count; + } + + /* Check if the stack is empty */ + public bool IsEmpty() { + return Size() == 0; + } + + /* Push */ + public void Push(int num) { + stack.Add(num); + } + + /* Pop */ + public int Pop() { + if (IsEmpty()) + throw new Exception(); + var val = Peek(); + stack.RemoveAt(Size() - 1); + return val; + } + + /* Return list for printing */ + public int Peek() { + if (IsEmpty()) + throw new Exception(); + return stack[Size() - 1]; + } + + /* Convert List to Array and return */ + public int[] ToArray() { + return [.. stack]; + } +} + +public class array_stack { + [Test] + public void Test() { + /* Access top of the stack element */ + ArrayStack stack = new(); + + /* Elements push onto stack */ + stack.Push(1); + stack.Push(3); + stack.Push(2); + stack.Push(5); + stack.Push(4); + Console.WriteLine("Stack stack = " + string.Join(",", stack.ToArray())); + + /* Return list for printing */ + int peek = stack.Peek(); + Console.WriteLine("Stack top element peek = " + peek); + + /* Element pop from stack */ + int pop = stack.Pop(); + Console.WriteLine("Pop element pop = " + pop + ", after pop, stack = " + string.Join(",", stack.ToArray())); + + /* Get the length of the stack */ + int size = stack.Size(); + Console.WriteLine("Stack length size = " + size); + + /* Check if empty */ + bool isEmpty = stack.IsEmpty(); + Console.WriteLine("Stack is empty = " + isEmpty); + } +} diff --git a/en/codes/csharp/chapter_stack_and_queue/deque.cs b/en/codes/csharp/chapter_stack_and_queue/deque.cs new file mode 100644 index 000000000..86750659d --- /dev/null +++ b/en/codes/csharp/chapter_stack_and_queue/deque.cs @@ -0,0 +1,44 @@ +/** + * File: deque.cs + * Created Time: 2022-12-30 + * Author: moonache (microin1301@outlook.com) + */ + +namespace hello_algo.chapter_stack_and_queue; + +public class deque { + [Test] + public void Test() { + /* Get the length of the double-ended queue */ + // In C#, use LinkedList as deque + LinkedList deque = new(); + + /* Elements enqueue */ + deque.AddLast(2); // Add to the rear of the queue + deque.AddLast(5); + deque.AddLast(4); + deque.AddFirst(3); // Add to the front of the queue + deque.AddFirst(1); + Console.WriteLine("Double-ended queue deque = " + string.Join(",", deque)); + + /* Update element */ + int? peekFirst = deque.First?.Value; // Rear of the queue element + Console.WriteLine("Front element peekFirst = " + peekFirst); + int? peekLast = deque.Last?.Value; // Front of the queue element dequeues + Console.WriteLine("Rear element peekLast = " + peekLast); + + /* Element dequeue */ + deque.RemoveFirst(); // Check if the double-ended queue is empty + Console.WriteLine("After front element dequeues, deque = " + string.Join(",", deque)); + deque.RemoveLast(); // Rear element dequeue + Console.WriteLine("After rear element dequeues, deque = " + string.Join(",", deque)); + + /* Get the length of the double-ended queue */ + int size = deque.Count; + Console.WriteLine("Double-ended queue length size = " + size); + + /* Check if the double-ended queue is empty */ + bool isEmpty = deque.Count == 0; + Console.WriteLine("Double-ended queue is empty = " + isEmpty); + } +} diff --git a/en/codes/csharp/chapter_stack_and_queue/linkedlist_deque.cs b/en/codes/csharp/chapter_stack_and_queue/linkedlist_deque.cs new file mode 100644 index 000000000..38515edc1 --- /dev/null +++ b/en/codes/csharp/chapter_stack_and_queue/linkedlist_deque.cs @@ -0,0 +1,177 @@ +/** + * File: linkedlist_deque.cs + * Created Time: 2023-03-08 + * Author: hpstory (hpstory1024@163.com) + */ + +namespace hello_algo.chapter_stack_and_queue; + +/* Doubly linked list node */ +public class ListNode(int val) { + public int val = val; // Node value + public ListNode? next = null; // Successor node reference + public ListNode? prev = null; // Predecessor node reference +} + +/* Double-ended queue based on doubly linked list implementation */ +public class LinkedListDeque { + ListNode? front, rear; // Head node front, tail node rear + int queSize = 0; // Length of the double-ended queue + + public LinkedListDeque() { + front = null; + rear = null; + } + + /* Get the length of the double-ended queue */ + public int Size() { + return queSize; + } + + /* Check if the double-ended queue is empty */ + public bool IsEmpty() { + return Size() == 0; + } + + /* Enqueue operation */ + void Push(int num, bool isFront) { + ListNode node = new(num); + // If the linked list is empty, make both front and rear point to node + if (IsEmpty()) { + front = node; + rear = node; + } + // Front of the queue enqueue operation + else if (isFront) { + // Add node to the head of the linked list + front!.prev = node; + node.next = front; + front = node; // Update head node + } + // Rear of the queue enqueue operation + else { + // Add node to the tail of the linked list + rear!.next = node; + node.prev = rear; + rear = node; // Update tail node + } + + queSize++; // Update queue length + } + + /* Front of the queue enqueue */ + public void PushFirst(int num) { + Push(num, true); + } + + /* Rear of the queue enqueue */ + public void PushLast(int num) { + Push(num, false); + } + + /* Dequeue operation */ + int? Pop(bool isFront) { + if (IsEmpty()) + throw new Exception(); + int? val; + // Temporarily store head node value + if (isFront) { + val = front?.val; // Delete head node + // Delete head node + ListNode? fNext = front?.next; + if (fNext != null) { + fNext.prev = null; + front!.next = null; + } + front = fNext; // Update head node + } + // Temporarily store tail node value + else { + val = rear?.val; // Delete tail node + // Update tail node + ListNode? rPrev = rear?.prev; + if (rPrev != null) { + rPrev.next = null; + rear!.prev = null; + } + rear = rPrev; // Update tail node + } + + queSize--; // Update queue length + return val; + } + + /* Rear of the queue dequeue */ + public int? PopFirst() { + return Pop(true); + } + + /* Access rear of the queue element */ + public int? PopLast() { + return Pop(false); + } + + /* Return list for printing */ + public int? PeekFirst() { + if (IsEmpty()) + throw new Exception(); + return front?.val; + } + + /* Driver Code */ + public int? PeekLast() { + if (IsEmpty()) + throw new Exception(); + return rear?.val; + } + + /* Return array for printing */ + public int?[] ToArray() { + ListNode? node = front; + int?[] res = new int?[Size()]; + for (int i = 0; i < res.Length; i++) { + res[i] = node?.val; + node = node?.next; + } + + return res; + } +} + +public class linkedlist_deque { + [Test] + public void Test() { + /* Get the length of the double-ended queue */ + LinkedListDeque deque = new(); + deque.PushLast(3); + deque.PushLast(2); + deque.PushLast(5); + Console.WriteLine("Double-ended queue deque = " + string.Join(" ", deque.ToArray())); + + /* Update element */ + int? peekFirst = deque.PeekFirst(); + Console.WriteLine("Front element peekFirst = " + peekFirst); + int? peekLast = deque.PeekLast(); + Console.WriteLine("Rear element peekLast = " + peekLast); + + /* Elements enqueue */ + deque.PushLast(4); + Console.WriteLine("After element 4 enqueues at rear, deque = " + string.Join(" ", deque.ToArray())); + deque.PushFirst(1); + Console.WriteLine("After element 1 enqueues at front, deque = " + string.Join(" ", deque.ToArray())); + + /* Element dequeue */ + int? popLast = deque.PopLast(); + Console.WriteLine("Rear dequeue element = " + popLast + ", after rear dequeue, deque = " + string.Join(" ", deque.ToArray())); + int? popFirst = deque.PopFirst(); + Console.WriteLine("Front dequeue element = " + popFirst + ", after front dequeue, deque = " + string.Join(" ", deque.ToArray())); + + /* Get the length of the double-ended queue */ + int size = deque.Size(); + Console.WriteLine("Double-ended queue length size = " + size); + + /* Check if the double-ended queue is empty */ + bool isEmpty = deque.IsEmpty(); + Console.WriteLine("Double-ended queue is empty = " + isEmpty); + } +} diff --git a/en/codes/csharp/chapter_stack_and_queue/linkedlist_queue.cs b/en/codes/csharp/chapter_stack_and_queue/linkedlist_queue.cs new file mode 100644 index 000000000..e58811ce8 --- /dev/null +++ b/en/codes/csharp/chapter_stack_and_queue/linkedlist_queue.cs @@ -0,0 +1,106 @@ +/** + * File: linkedlist_queue.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_stack_and_queue; + +/* Queue based on linked list implementation */ +class LinkedListQueue { + ListNode? front, rear; // Head node front, tail node rear + int queSize = 0; + + public LinkedListQueue() { + front = null; + rear = null; + } + + /* Get the length of the queue */ + public int Size() { + return queSize; + } + + /* Check if the queue is empty */ + public bool IsEmpty() { + return Size() == 0; + } + + /* Enqueue */ + public void Push(int num) { + // Add num after the tail node + ListNode node = new(num); + // If the queue is empty, make both front and rear point to the node + if (front == null) { + front = node; + rear = node; + // If the queue is not empty, add the node after the tail node + } else if (rear != null) { + rear.next = node; + rear = node; + } + queSize++; + } + + /* Dequeue */ + public int Pop() { + int num = Peek(); + // Delete head node + front = front?.next; + queSize--; + return num; + } + + /* Return list for printing */ + public int Peek() { + if (IsEmpty()) + throw new Exception(); + return front!.val; + } + + /* Convert linked list to Array and return */ + public int[] ToArray() { + if (front == null) + return []; + + ListNode? node = front; + int[] res = new int[Size()]; + for (int i = 0; i < res.Length; i++) { + res[i] = node!.val; + node = node.next; + } + return res; + } +} + +public class linkedlist_queue { + [Test] + public void Test() { + /* Access front of the queue element */ + LinkedListQueue queue = new(); + + /* Elements enqueue */ + queue.Push(1); + queue.Push(3); + queue.Push(2); + queue.Push(5); + queue.Push(4); + Console.WriteLine("Queue queue = " + string.Join(",", queue.ToArray())); + + /* Return list for printing */ + int peek = queue.Peek(); + Console.WriteLine("Front element peek = " + peek); + + /* Element dequeue */ + int pop = queue.Pop(); + Console.WriteLine("Dequeue element pop = " + pop + ", after dequeue, queue = " + string.Join(",", queue.ToArray())); + + /* Get the length of the queue */ + int size = queue.Size(); + Console.WriteLine("Queue length size = " + size); + + /* Check if the queue is empty */ + bool isEmpty = queue.IsEmpty(); + Console.WriteLine("Queue is empty = " + isEmpty); + } +} diff --git a/en/codes/csharp/chapter_stack_and_queue/linkedlist_stack.cs b/en/codes/csharp/chapter_stack_and_queue/linkedlist_stack.cs new file mode 100644 index 000000000..cf0c558b1 --- /dev/null +++ b/en/codes/csharp/chapter_stack_and_queue/linkedlist_stack.cs @@ -0,0 +1,97 @@ +/** + * File: linkedlist_stack.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_stack_and_queue; + +/* Stack based on linked list implementation */ +class LinkedListStack { + ListNode? stackPeek; // Use head node as stack top + int stkSize = 0; // Stack length + + public LinkedListStack() { + stackPeek = null; + } + + /* Get the length of the stack */ + public int Size() { + return stkSize; + } + + /* Check if the stack is empty */ + public bool IsEmpty() { + return Size() == 0; + } + + /* Push */ + public void Push(int num) { + ListNode node = new(num) { + next = stackPeek + }; + stackPeek = node; + stkSize++; + } + + /* Pop */ + public int Pop() { + int num = Peek(); + stackPeek = stackPeek!.next; + stkSize--; + return num; + } + + /* Return list for printing */ + public int Peek() { + if (IsEmpty()) + throw new Exception(); + return stackPeek!.val; + } + + /* Convert List to Array and return */ + public int[] ToArray() { + if (stackPeek == null) + return []; + + ListNode? node = stackPeek; + int[] res = new int[Size()]; + for (int i = res.Length - 1; i >= 0; i--) { + res[i] = node!.val; + node = node.next; + } + return res; + } +} + +public class linkedlist_stack { + [Test] + public void Test() { + /* Access top of the stack element */ + LinkedListStack stack = new(); + + /* Elements push onto stack */ + stack.Push(1); + stack.Push(3); + stack.Push(2); + stack.Push(5); + stack.Push(4); + Console.WriteLine("Stack stack = " + string.Join(",", stack.ToArray())); + + /* Return list for printing */ + int peek = stack.Peek(); + Console.WriteLine("Stack top element peek = " + peek); + + /* Element pop from stack */ + int pop = stack.Pop(); + Console.WriteLine("Pop element pop = " + pop + ", after pop, stack = " + string.Join(",", stack.ToArray())); + + /* Get the length of the stack */ + int size = stack.Size(); + Console.WriteLine("Stack length size = " + size); + + /* Check if empty */ + bool isEmpty = stack.IsEmpty(); + Console.WriteLine("Stack is empty = " + isEmpty); + } +} diff --git a/en/codes/csharp/chapter_stack_and_queue/queue.cs b/en/codes/csharp/chapter_stack_and_queue/queue.cs new file mode 100644 index 000000000..846f58831 --- /dev/null +++ b/en/codes/csharp/chapter_stack_and_queue/queue.cs @@ -0,0 +1,39 @@ +/** + * File: queue.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_stack_and_queue; + +public class queue { + [Test] + public void Test() { + /* Access front of the queue element */ + Queue queue = new(); + + /* Elements enqueue */ + queue.Enqueue(1); + queue.Enqueue(3); + queue.Enqueue(2); + queue.Enqueue(5); + queue.Enqueue(4); + Console.WriteLine("Queue queue = " + string.Join(",", queue)); + + /* Return list for printing */ + int peek = queue.Peek(); + Console.WriteLine("Front element peek = " + peek); + + /* Element dequeue */ + int pop = queue.Dequeue(); + Console.WriteLine("Dequeue element pop = " + pop + ", after dequeue, queue = " + string.Join(",", queue)); + + /* Get the length of the queue */ + int size = queue.Count; + Console.WriteLine("Queue length size = " + size); + + /* Check if the queue is empty */ + bool isEmpty = queue.Count == 0; + Console.WriteLine("Queue is empty = " + isEmpty); + } +} diff --git a/en/codes/csharp/chapter_stack_and_queue/stack.cs b/en/codes/csharp/chapter_stack_and_queue/stack.cs new file mode 100644 index 000000000..4c3e2e886 --- /dev/null +++ b/en/codes/csharp/chapter_stack_and_queue/stack.cs @@ -0,0 +1,40 @@ +/** + * File: stack.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_stack_and_queue; + +public class stack { + [Test] + public void Test() { + /* Access top of the stack element */ + Stack stack = new(); + + /* Elements push onto stack */ + stack.Push(1); + stack.Push(3); + stack.Push(2); + stack.Push(5); + stack.Push(4); + // Note: stack.ToArray() returns reversed sequence, index 0 is stack top + Console.WriteLine("Stack stack = " + string.Join(",", stack)); + + /* Return list for printing */ + int peek = stack.Peek(); + Console.WriteLine("Stack top element peek = " + peek); + + /* Element pop from stack */ + int pop = stack.Pop(); + Console.WriteLine("Pop element pop = " + pop + ", after pop, stack = " + string.Join(",", stack)); + + /* Get the length of the stack */ + int size = stack.Count; + Console.WriteLine("Stack length size = " + size); + + /* Check if empty */ + bool isEmpty = stack.Count == 0; + Console.WriteLine("Stack is empty = " + isEmpty); + } +} diff --git a/en/codes/csharp/chapter_tree/array_binary_tree.cs b/en/codes/csharp/chapter_tree/array_binary_tree.cs new file mode 100644 index 000000000..22b0e4635 --- /dev/null +++ b/en/codes/csharp/chapter_tree/array_binary_tree.cs @@ -0,0 +1,129 @@ +/** +* File: array_binary_tree.cs +* Created Time: 2023-07-20 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_tree; + +/* Binary tree class represented by array */ +public class ArrayBinaryTree(List arr) { + List tree = new(arr); + + /* List capacity */ + public int Size() { + return tree.Count; + } + + /* Get value of node at index i */ + public int? Val(int i) { + // If index out of bounds, return null to represent empty position + if (i < 0 || i >= Size()) + return null; + return tree[i]; + } + + /* Get index of left child node of node at index i */ + public int Left(int i) { + return 2 * i + 1; + } + + /* Get index of right child node of node at index i */ + public int Right(int i) { + return 2 * i + 2; + } + + /* Get index of parent node of node at index i */ + public int Parent(int i) { + return (i - 1) / 2; + } + + /* Level-order traversal */ + public List LevelOrder() { + List res = []; + // Traverse array directly + for (int i = 0; i < Size(); i++) { + if (Val(i).HasValue) + res.Add(Val(i)!.Value); + } + return res; + } + + /* Depth-first traversal */ + void DFS(int i, string order, List res) { + // If empty position, return + if (!Val(i).HasValue) + return; + // Preorder traversal + if (order == "pre") + res.Add(Val(i)!.Value); + DFS(Left(i), order, res); + // Inorder traversal + if (order == "in") + res.Add(Val(i)!.Value); + DFS(Right(i), order, res); + // Postorder traversal + if (order == "post") + res.Add(Val(i)!.Value); + } + + /* Preorder traversal */ + public List PreOrder() { + List res = []; + DFS(0, "pre", res); + return res; + } + + /* Inorder traversal */ + public List InOrder() { + List res = []; + DFS(0, "in", res); + return res; + } + + /* Postorder traversal */ + public List PostOrder() { + List res = []; + DFS(0, "post", res); + return res; + } +} + +public class array_binary_tree { + [Test] + public void Test() { + // Initialize binary tree + // Here we use a function to generate a binary tree directly from an array + List arr = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15]; + + TreeNode? root = TreeNode.ListToTree(arr); + Console.WriteLine("\nInitialize binary tree\n"); + Console.WriteLine("Array representation of binary tree:"); + Console.WriteLine(arr.PrintList()); + Console.WriteLine("Linked list representation of binary tree:"); + PrintUtil.PrintTree(root); + + // Binary tree class represented by array + ArrayBinaryTree abt = new(arr); + + // Access node + int i = 1; + int l = abt.Left(i); + int r = abt.Right(i); + int p = abt.Parent(i); + Console.WriteLine("\nCurrent node index is " + i + ", value is " + abt.Val(i)); + Console.WriteLine("Its left child node index is " + l + ", value is " + (abt.Val(l).HasValue ? abt.Val(l) : "null")); + Console.WriteLine("Its right child node index is " + r + ", value is " + (abt.Val(r).HasValue ? abt.Val(r) : "null")); + Console.WriteLine("Its parent node index is " + p + ", value is " + (abt.Val(p).HasValue ? abt.Val(p) : "null")); + + // Traverse tree + List res = abt.LevelOrder(); + Console.WriteLine("\nLevel-order traversal is:" + res.PrintList()); + res = abt.PreOrder(); + Console.WriteLine("Preorder traversal is:" + res.PrintList()); + res = abt.InOrder(); + Console.WriteLine("Inorder traversal is:" + res.PrintList()); + res = abt.PostOrder(); + Console.WriteLine("Postorder traversal is:" + res.PrintList()); + } +} \ No newline at end of file diff --git a/en/codes/csharp/chapter_tree/avl_tree.cs b/en/codes/csharp/chapter_tree/avl_tree.cs new file mode 100644 index 000000000..c207b618c --- /dev/null +++ b/en/codes/csharp/chapter_tree/avl_tree.cs @@ -0,0 +1,216 @@ +/** + * File: avl_tree.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_tree; + +/* AVL tree */ +class AVLTree { + public TreeNode? root; // Root node + + /* Get node height */ + int Height(TreeNode? node) { + // Empty node height is -1, leaf node height is 0 + return node == null ? -1 : node.height; + } + + /* Update node height */ + void UpdateHeight(TreeNode node) { + // Node height equals the height of the tallest subtree + 1 + node.height = Math.Max(Height(node.left), Height(node.right)) + 1; + } + + /* Get balance factor */ + public int BalanceFactor(TreeNode? node) { + // Empty node balance factor is 0 + if (node == null) return 0; + // Node balance factor = left subtree height - right subtree height + return Height(node.left) - Height(node.right); + } + + /* Right rotation operation */ + TreeNode? RightRotate(TreeNode? node) { + TreeNode? child = node?.left; + TreeNode? grandChild = child?.right; + // Using child as pivot, rotate node to the right + child.right = node; + node.left = grandChild; + // Update node height + UpdateHeight(node); + UpdateHeight(child); + // Return root node of subtree after rotation + return child; + } + + /* Left rotation operation */ + TreeNode? LeftRotate(TreeNode? node) { + TreeNode? child = node?.right; + TreeNode? grandChild = child?.left; + // Using child as pivot, rotate node to the left + child.left = node; + node.right = grandChild; + // Update node height + UpdateHeight(node); + UpdateHeight(child); + // Return root node of subtree after rotation + return child; + } + + /* Perform rotation operation to restore balance to this subtree */ + TreeNode? Rotate(TreeNode? node) { + // Get balance factor of node + int balanceFactorInt = BalanceFactor(node); + // Left-leaning tree + if (balanceFactorInt > 1) { + if (BalanceFactor(node?.left) >= 0) { + // Right rotation + return RightRotate(node); + } else { + // First left rotation then right rotation + node!.left = LeftRotate(node!.left); + return RightRotate(node); + } + } + // Right-leaning tree + if (balanceFactorInt < -1) { + if (BalanceFactor(node?.right) <= 0) { + // Left rotation + return LeftRotate(node); + } else { + // First right rotation then left rotation + node!.right = RightRotate(node!.right); + return LeftRotate(node); + } + } + // Balanced tree, no rotation needed, return directly + return node; + } + + /* Insert node */ + public void Insert(int val) { + root = InsertHelper(root, val); + } + + /* Recursively insert node (helper method) */ + TreeNode? InsertHelper(TreeNode? node, int val) { + if (node == null) return new TreeNode(val); + /* 1. Find insertion position and insert node */ + if (val < node.val) + node.left = InsertHelper(node.left, val); + else if (val > node.val) + node.right = InsertHelper(node.right, val); + else + return node; // Duplicate node not inserted, return directly + UpdateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = Rotate(node); + // Return root node of subtree + return node; + } + + /* Remove node */ + public void Remove(int val) { + root = RemoveHelper(root, val); + } + + /* Recursively delete node (helper method) */ + TreeNode? RemoveHelper(TreeNode? node, int val) { + if (node == null) return null; + /* 1. Find node and delete */ + if (val < node.val) + node.left = RemoveHelper(node.left, val); + else if (val > node.val) + node.right = RemoveHelper(node.right, val); + else { + if (node.left == null || node.right == null) { + TreeNode? child = node.left ?? node.right; + // Number of child nodes = 0, delete node directly and return + if (child == null) + return null; + // Number of child nodes = 1, delete node directly + else + node = child; + } else { + // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + TreeNode? temp = node.right; + while (temp.left != null) { + temp = temp.left; + } + node.right = RemoveHelper(node.right, temp.val!.Value); + node.val = temp.val; + } + } + UpdateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = Rotate(node); + // Return root node of subtree + return node; + } + + /* Search node */ + public TreeNode? Search(int val) { + TreeNode? cur = root; + // Loop search, exit after passing leaf node + while (cur != null) { + // Target node is in cur's right subtree + if (cur.val < val) + cur = cur.right; + // Target node is in cur's left subtree + else if (cur.val > val) + cur = cur.left; + // Found target node, exit loop + else + break; + } + // Return target node + return cur; + } +} + +public class avl_tree { + static void TestInsert(AVLTree tree, int val) { + tree.Insert(val); + Console.WriteLine("\nInsert node " + val + ", AVL tree is"); + PrintUtil.PrintTree(tree.root); + } + + static void TestRemove(AVLTree tree, int val) { + tree.Remove(val); + Console.WriteLine("\nRemove node " + val + ", AVL tree is"); + PrintUtil.PrintTree(tree.root); + } + + [Test] + public void Test() { + /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + AVLTree avlTree = new(); + + /* Insert node */ + // Delete nodes + TestInsert(avlTree, 1); + TestInsert(avlTree, 2); + TestInsert(avlTree, 3); + TestInsert(avlTree, 4); + TestInsert(avlTree, 5); + TestInsert(avlTree, 8); + TestInsert(avlTree, 7); + TestInsert(avlTree, 9); + TestInsert(avlTree, 10); + TestInsert(avlTree, 6); + + /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + TestInsert(avlTree, 7); + + /* Remove node */ + // Delete node with degree 1 + TestRemove(avlTree, 8); // Delete node with degree 2 + TestRemove(avlTree, 5); // Remove node with degree 1 + TestRemove(avlTree, 4); // Remove node with degree 2 + + /* Search node */ + TreeNode? node = avlTree.Search(7); + Console.WriteLine("\nFound node object is " + node + ", node value = " + node?.val); + } +} diff --git a/en/codes/csharp/chapter_tree/binary_search_tree.cs b/en/codes/csharp/chapter_tree/binary_search_tree.cs new file mode 100644 index 000000000..273f4f0cd --- /dev/null +++ b/en/codes/csharp/chapter_tree/binary_search_tree.cs @@ -0,0 +1,160 @@ +/** + * File: binary_search_tree.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_tree; + +class BinarySearchTree { + TreeNode? root; + + public BinarySearchTree() { + // Initialize empty tree + root = null; + } + + /* Get binary tree root node */ + public TreeNode? GetRoot() { + return root; + } + + /* Search node */ + public TreeNode? Search(int num) { + TreeNode? cur = root; + // Loop search, exit after passing leaf node + while (cur != null) { + // Target node is in cur's right subtree + if (cur.val < num) cur = + cur.right; + // Target node is in cur's left subtree + else if (cur.val > num) + cur = cur.left; + // Found target node, exit loop + else + break; + } + // Return target node + return cur; + } + + /* Insert node */ + public void Insert(int num) { + // If tree is empty, initialize root node + if (root == null) { + root = new TreeNode(num); + return; + } + TreeNode? cur = root, pre = null; + // Loop search, exit after passing leaf node + while (cur != null) { + // Found duplicate node, return directly + if (cur.val == num) + return; + pre = cur; + // Insertion position is in cur's right subtree + if (cur.val < num) + cur = cur.right; + // Insertion position is in cur's left subtree + else + cur = cur.left; + } + + // Insert node + TreeNode node = new(num); + if (pre != null) { + if (pre.val < num) + pre.right = node; + else + pre.left = node; + } + } + + + /* Remove node */ + public void Remove(int num) { + // If tree is empty, return directly + if (root == null) + return; + TreeNode? cur = root, pre = null; + // Loop search, exit after passing leaf node + while (cur != null) { + // Found node to delete, exit loop + if (cur.val == num) + break; + pre = cur; + // Node to delete is in cur's right subtree + if (cur.val < num) + cur = cur.right; + // Node to delete is in cur's left subtree + else + cur = cur.left; + } + // If no node to delete, return directly + if (cur == null) + return; + // Number of child nodes = 0 or 1 + if (cur.left == null || cur.right == null) { + // When number of child nodes = 0 / 1, child = null / that child node + TreeNode? child = cur.left ?? cur.right; + // Delete node cur + if (cur != root) { + if (pre!.left == cur) + pre.left = child; + else + pre.right = child; + } else { + // If deleted node is root node, reassign root node + root = child; + } + } + // Number of child nodes = 2 + else { + // Get next node of cur in inorder traversal + TreeNode? tmp = cur.right; + while (tmp.left != null) { + tmp = tmp.left; + } + // Recursively delete node tmp + Remove(tmp.val!.Value); + // Replace cur with tmp + cur.val = tmp.val; + } + } +} + +public class binary_search_tree { + [Test] + public void Test() { + /* Initialize binary search tree */ + BinarySearchTree bst = new(); + // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + int[] nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]; + foreach (int num in nums) { + bst.Insert(num); + } + + Console.WriteLine("\nInitialized binary tree is\n"); + PrintUtil.PrintTree(bst.GetRoot()); + + /* Search node */ + TreeNode? node = bst.Search(7); + Console.WriteLine("\nFound node object is " + node + ", node value = " + node?.val); + + /* Insert node */ + bst.Insert(16); + Console.WriteLine("\nAfter inserting node 16, binary tree is\n"); + PrintUtil.PrintTree(bst.GetRoot()); + + /* Remove node */ + bst.Remove(1); + Console.WriteLine("\nAfter removing node 1, binary tree is\n"); + PrintUtil.PrintTree(bst.GetRoot()); + bst.Remove(2); + Console.WriteLine("\nAfter removing node 2, binary tree is\n"); + PrintUtil.PrintTree(bst.GetRoot()); + bst.Remove(4); + Console.WriteLine("\nAfter removing node 4, binary tree is\n"); + PrintUtil.PrintTree(bst.GetRoot()); + } +} diff --git a/en/codes/csharp/chapter_tree/binary_tree.cs b/en/codes/csharp/chapter_tree/binary_tree.cs new file mode 100644 index 000000000..e527d7537 --- /dev/null +++ b/en/codes/csharp/chapter_tree/binary_tree.cs @@ -0,0 +1,39 @@ +/** + * File: binary_tree.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_tree; + +public class binary_tree { + [Test] + public void Test() { + /* Initialize binary tree */ + // Initialize nodes + TreeNode n1 = new(1); + TreeNode n2 = new(2); + TreeNode n3 = new(3); + TreeNode n4 = new(4); + TreeNode n5 = new(5); + // Build references (pointers) between nodes + n1.left = n2; + n1.right = n3; + n2.left = n4; + n2.right = n5; + Console.WriteLine("\nInitialize binary tree\n"); + PrintUtil.PrintTree(n1); + + /* Insert node P between n1 -> n2 */ + TreeNode P = new(0); + // Delete node + n1.left = P; + P.left = n2; + Console.WriteLine("\nAfter inserting node P\n"); + PrintUtil.PrintTree(n1); + // Remove node P + n1.left = n2; + Console.WriteLine("\nAfter removing node P\n"); + PrintUtil.PrintTree(n1); + } +} diff --git a/en/codes/csharp/chapter_tree/binary_tree_bfs.cs b/en/codes/csharp/chapter_tree/binary_tree_bfs.cs new file mode 100644 index 000000000..b8dacb0ed --- /dev/null +++ b/en/codes/csharp/chapter_tree/binary_tree_bfs.cs @@ -0,0 +1,40 @@ +/** + * File: binary_tree_bfs.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_tree; + +public class binary_tree_bfs { + + /* Level-order traversal */ + List LevelOrder(TreeNode root) { + // Initialize queue, add root node + Queue queue = new(); + queue.Enqueue(root); + // Initialize a list to save the traversal sequence + List list = []; + while (queue.Count != 0) { + TreeNode node = queue.Dequeue(); // Dequeue + list.Add(node.val!.Value); // Save node value + if (node.left != null) + queue.Enqueue(node.left); // Left child node enqueue + if (node.right != null) + queue.Enqueue(node.right); // Right child node enqueue + } + return list; + } + + [Test] + public void Test() { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + TreeNode? root = TreeNode.ListToTree([1, 2, 3, 4, 5, 6, 7]); + Console.WriteLine("\nInitialize binary tree\n"); + PrintUtil.PrintTree(root); + + List list = LevelOrder(root!); + Console.WriteLine("\nLevel-order traversal node print sequence = " + string.Join(",", list)); + } +} diff --git a/en/codes/csharp/chapter_tree/binary_tree_dfs.cs b/en/codes/csharp/chapter_tree/binary_tree_dfs.cs new file mode 100644 index 000000000..1348d6243 --- /dev/null +++ b/en/codes/csharp/chapter_tree/binary_tree_dfs.cs @@ -0,0 +1,59 @@ +/** + * File: binary_tree_dfs.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.chapter_tree; + +public class binary_tree_dfs { + List list = []; + + /* Preorder traversal */ + void PreOrder(TreeNode? root) { + if (root == null) return; + // Visit priority: root node -> left subtree -> right subtree + list.Add(root.val!.Value); + PreOrder(root.left); + PreOrder(root.right); + } + + /* Inorder traversal */ + void InOrder(TreeNode? root) { + if (root == null) return; + // Visit priority: left subtree -> root node -> right subtree + InOrder(root.left); + list.Add(root.val!.Value); + InOrder(root.right); + } + + /* Postorder traversal */ + void PostOrder(TreeNode? root) { + if (root == null) return; + // Visit priority: left subtree -> right subtree -> root node + PostOrder(root.left); + PostOrder(root.right); + list.Add(root.val!.Value); + } + + [Test] + public void Test() { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + TreeNode? root = TreeNode.ListToTree([1, 2, 3, 4, 5, 6, 7]); + Console.WriteLine("\nInitialize binary tree\n"); + PrintUtil.PrintTree(root); + + list.Clear(); + PreOrder(root); + Console.WriteLine("\nPreorder traversal node print sequence = " + string.Join(",", list)); + + list.Clear(); + InOrder(root); + Console.WriteLine("\nInorder traversal node print sequence = " + string.Join(",", list)); + + list.Clear(); + PostOrder(root); + Console.WriteLine("\nPostorder traversal node print sequence = " + string.Join(",", list)); + } +} diff --git a/en/codes/csharp/csharp.sln b/en/codes/csharp/csharp.sln new file mode 100644 index 000000000..0c74ccc53 --- /dev/null +++ b/en/codes/csharp/csharp.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "hello-algo", "hello-algo.csproj", "{48B60439-EFDC-4C8F-AE8D-41979958C8AC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48B60439-EFDC-4C8F-AE8D-41979958C8AC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1E773F8A-FF66-4974-820B-FCE9032D19AE} + EndGlobalSection +EndGlobal diff --git a/en/codes/csharp/hello-algo.csproj b/en/codes/csharp/hello-algo.csproj new file mode 100644 index 000000000..43817cc38 --- /dev/null +++ b/en/codes/csharp/hello-algo.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + hello_algo + enable + enable + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/en/codes/csharp/utils/ListNode.cs b/en/codes/csharp/utils/ListNode.cs new file mode 100644 index 000000000..400b30361 --- /dev/null +++ b/en/codes/csharp/utils/ListNode.cs @@ -0,0 +1,32 @@ +// File: ListNode.cs +// Created Time: 2022-12-16 +// Author: mingXta (1195669834@qq.com) + +namespace hello_algo.utils; + +/* Linked list node */ +public class ListNode(int x) { + public int val = x; + public ListNode? next; + + /* Deserialize array to linked list */ + public static ListNode? ArrToLinkedList(int[] arr) { + ListNode dum = new(0); + ListNode head = dum; + foreach (int val in arr) { + head.next = new ListNode(val); + head = head.next; + } + return dum.next; + } + + public override string? ToString() { + List list = []; + var head = this; + while (head != null) { + list.Add(head.val.ToString()); + head = head.next; + } + return string.Join("->", list); + } +} diff --git a/en/codes/csharp/utils/PrintUtil.cs b/en/codes/csharp/utils/PrintUtil.cs new file mode 100644 index 000000000..cd780ed98 --- /dev/null +++ b/en/codes/csharp/utils/PrintUtil.cs @@ -0,0 +1,132 @@ +/** +* File: PrintUtil.cs +* Created Time: 2022-12-23 +* Author: haptear (haptear@hotmail.com), krahets (krahets@163.com) +*/ + +namespace hello_algo.utils; + +public class Trunk(Trunk? prev, string str) { + public Trunk? prev = prev; + public string str = str; +}; + +public static class PrintUtil { + /* Print list */ + public static void PrintList(IList list) { + Console.WriteLine("[" + string.Join(", ", list) + "]"); + } + + public static string PrintList(this IEnumerable list) { + return $"[ {string.Join(", ", list.Select(x => x?.ToString() ?? "null"))} ]"; + } + + /* Print matrix (Array) */ + public static void PrintMatrix(T[][] matrix) { + Console.WriteLine("["); + foreach (T[] row in matrix) { + Console.WriteLine(" " + string.Join(", ", row) + ","); + } + Console.WriteLine("]"); + } + + /* Print matrix (List) */ + public static void PrintMatrix(List> matrix) { + Console.WriteLine("["); + foreach (List row in matrix) { + Console.WriteLine(" " + string.Join(", ", row) + ","); + } + Console.WriteLine("]"); + } + + /* Print linked list */ + public static void PrintLinkedList(ListNode? head) { + List list = []; + while (head != null) { + list.Add(head.val.ToString()); + head = head.next; + } + Console.Write(string.Join(" -> ", list)); + } + + /** + * Print binary tree + * This tree printer is borrowed from TECHIE DELIGHT + * https://www.techiedelight.com/c-program-print-binary-tree/ + */ + public static void PrintTree(TreeNode? root) { + PrintTree(root, null, false); + } + + /* Print binary tree */ + public static void PrintTree(TreeNode? root, Trunk? prev, bool isRight) { + if (root == null) { + return; + } + + string prev_str = " "; + Trunk trunk = new(prev, prev_str); + + PrintTree(root.right, trunk, true); + + if (prev == null) { + trunk.str = "———"; + } else if (isRight) { + trunk.str = "/———"; + prev_str = " |"; + } else { + trunk.str = "\\———"; + prev.str = prev_str; + } + + ShowTrunks(trunk); + Console.WriteLine(" " + root.val); + + if (prev != null) { + prev.str = prev_str; + } + trunk.str = " |"; + + PrintTree(root.left, trunk, false); + } + + public static void ShowTrunks(Trunk? p) { + if (p == null) { + return; + } + + ShowTrunks(p.prev); + Console.Write(p.str); + } + + /* Print hash table */ + public static void PrintHashMap(Dictionary map) where K : notnull { + foreach (var kv in map.Keys) { + Console.WriteLine(kv.ToString() + " -> " + map[kv]?.ToString()); + } + } + + /* Print heap */ + public static void PrintHeap(Queue queue) { + Console.Write("Heap array representation:"); + List list = [.. queue]; + Console.WriteLine(string.Join(',', list)); + Console.WriteLine("Heap tree representation:"); + TreeNode? tree = TreeNode.ListToTree(list.Cast().ToList()); + PrintTree(tree); + } + + /* Print priority queue */ + public static void PrintHeap(PriorityQueue queue) { + var newQueue = new PriorityQueue(queue.UnorderedItems, queue.Comparer); + Console.Write("Heap array representation:"); + List list = []; + while (newQueue.TryDequeue(out int element, out _)) { + list.Add(element); + } + Console.WriteLine("Heap tree representation:"); + Console.WriteLine(string.Join(',', list.ToList())); + TreeNode? tree = TreeNode.ListToTree(list.Cast().ToList()); + PrintTree(tree); + } +} \ No newline at end of file diff --git a/en/codes/csharp/utils/TreeNode.cs b/en/codes/csharp/utils/TreeNode.cs new file mode 100644 index 000000000..f4a2af8ee --- /dev/null +++ b/en/codes/csharp/utils/TreeNode.cs @@ -0,0 +1,67 @@ +/** + * File: TreeNode.cs + * Created Time: 2022-12-23 + * Author: haptear (haptear@hotmail.com) + */ + +namespace hello_algo.utils; + +/* Binary tree node class */ +public class TreeNode(int? x) { + public int? val = x; // Node value + public int height; // Node height + public TreeNode? left; // Reference to left child node + public TreeNode? right; // Reference to right child node + + // For the serialization encoding rules, please refer to: + // https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ + // Array representation of binary tree: + // [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] + // Linked list representation of binary tree: + // /——— 15 + // /——— 7 + // /——— 3 + // | \——— 6 + // | \——— 12 + // ——— 1 + // \——— 2 + // | /——— 9 + // \——— 4 + // \——— 8 + + /* Deserialize a list into a binary tree: recursion */ + static TreeNode? ListToTreeDFS(List arr, int i) { + if (i < 0 || i >= arr.Count || !arr[i].HasValue) { + return null; + } + TreeNode root = new(arr[i]) { + left = ListToTreeDFS(arr, 2 * i + 1), + right = ListToTreeDFS(arr, 2 * i + 2) + }; + return root; + } + + /* Deserialize a list into a binary tree */ + public static TreeNode? ListToTree(List arr) { + return ListToTreeDFS(arr, 0); + } + + /* Serialize a binary tree into a list: recursion */ + static void TreeToListDFS(TreeNode? root, int i, List res) { + if (root == null) + return; + while (i >= res.Count) { + res.Add(null); + } + res[i] = root.val; + TreeToListDFS(root.left, 2 * i + 1, res); + TreeToListDFS(root.right, 2 * i + 2, res); + } + + /* Serialize a binary tree into a list */ + public static List TreeToList(TreeNode root) { + List res = []; + TreeToListDFS(root, 0, res); + return res; + } +} diff --git a/en/codes/csharp/utils/Vertex.cs b/en/codes/csharp/utils/Vertex.cs new file mode 100644 index 000000000..926bff993 --- /dev/null +++ b/en/codes/csharp/utils/Vertex.cs @@ -0,0 +1,30 @@ +/** + * File: Vertex.cs + * Created Time: 2023-02-06 + * Author: zjkung1123 (zjkung1123@gmail.com), krahets (krahets@163.com) + */ + +namespace hello_algo.utils; + +/* Vertex class */ +public class Vertex(int val) { + public int val = val; + + /* Input value list vals, return vertex list vets */ + public static Vertex[] ValsToVets(int[] vals) { + Vertex[] vets = new Vertex[vals.Length]; + for (int i = 0; i < vals.Length; i++) { + vets[i] = new Vertex(vals[i]); + } + return vets; + } + + /* Input vertex list vets, return value list vals */ + public static List VetsToVals(List vets) { + List vals = []; + foreach (Vertex vet in vets) { + vals.Add(vet.val); + } + return vals; + } +} diff --git a/en/codes/dart/build.dart b/en/codes/dart/build.dart new file mode 100644 index 000000000..7bd5b51a1 --- /dev/null +++ b/en/codes/dart/build.dart @@ -0,0 +1,39 @@ +import 'dart:io'; + +void main() { + Directory foldPath = Directory('codes/dart/'); + List files = foldPath.listSync(); + int totalCount = 0; + int errorCount = 0; + for (var file in files) { + if (file.path.endsWith('build.dart')) continue; + if (file is File && file.path.endsWith('.dart')) { + totalCount++; + try { + Process.runSync('dart', [file.path]); + } catch (e) { + errorCount++; + print('Error: $e'); + print('File: ${file.path}'); + } + } else if (file is Directory) { + List subFiles = file.listSync(); + for (var subFile in subFiles) { + if (subFile is File && subFile.path.endsWith('.dart')) { + totalCount++; + try { + Process.runSync('dart', [subFile.path]); + } catch (e) { + errorCount++; + print('Error: $e'); + print('File: ${file.path}'); + } + } + } + } + } + + print('===== Build Complete ====='); + print('Total: $totalCount'); + print('Error: $errorCount'); +} diff --git a/en/codes/dart/chapter_array_and_linkedlist/array.dart b/en/codes/dart/chapter_array_and_linkedlist/array.dart new file mode 100644 index 000000000..de502849c --- /dev/null +++ b/en/codes/dart/chapter_array_and_linkedlist/array.dart @@ -0,0 +1,105 @@ +/** + * File: array.dart + * Created Time: 2023-01-20 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +// ignore_for_file: unused_local_variable + +import 'dart:math'; + +/* Random access to element */ +int randomAccess(List nums) { + // Randomly select a number in the interval [0, nums.length) + int randomIndex = Random().nextInt(nums.length); + // Retrieve and return the random element + int randomNum = nums[randomIndex]; + return randomNum; +} + +/* Extend array length */ +List extend(List nums, int enlarge) { + // Initialize an array with extended length + List res = List.filled(nums.length + enlarge, 0); + // Copy all elements from the original array to the new array + for (var i = 0; i < nums.length; i++) { + res[i] = nums[i]; + } + // Return the extended new array + return res; +} + +/* Insert element _num at array index index */ +void insert(List nums, int _num, int index) { + // Move all elements at and after index index backward by one position + for (var i = nums.length - 1; i > index; i--) { + nums[i] = nums[i - 1]; + } + // Assign _num to element at index + nums[index] = _num; +} + +/* Remove the element at index index */ +void remove(List nums, int index) { + // Move all elements after index index forward by one position + for (var i = index; i < nums.length - 1; i++) { + nums[i] = nums[i + 1]; + } +} + +/* Traverse array elements */ +void traverse(List nums) { + int count = 0; + // Traverse array by index + for (var i = 0; i < nums.length; i++) { + count += nums[i]; + } + // Direct traversal of array elements + for (int _num in nums) { + count += _num; + } + // Traverse array using forEach method + nums.forEach((_num) { + count += _num; + }); +} + +/* Find the specified element in the array */ +int find(List nums, int target) { + for (var i = 0; i < nums.length; i++) { + if (nums[i] == target) return i; + } + return -1; +} + +/* Driver Code */ +void main() { + /* Initialize array */ + var arr = List.filled(5, 0); + print('Array arr = $arr'); + List nums = [1, 3, 2, 5, 4]; + print('Array nums = $nums'); + + /* Insert element */ + int randomNum = randomAccess(nums); + print('Get random element $randomNum from nums'); + + /* Traverse array */ + nums = extend(nums, 3); + print('Extend array length to 8, get nums = $nums'); + + /* Insert element */ + insert(nums, 6, 3); + print("Insert number 6 at index 3, get nums = $nums"); + + /* Remove element */ + remove(nums, 2); + print("Delete element at index 2, get nums = $nums"); + + /* Traverse array */ + traverse(nums); + + /* Find element */ + int index = find(nums, 3); + print("Find element 3 in nums, index = $index"); +} diff --git a/en/codes/dart/chapter_array_and_linkedlist/linked_list.dart b/en/codes/dart/chapter_array_and_linkedlist/linked_list.dart new file mode 100644 index 000000000..8d4115342 --- /dev/null +++ b/en/codes/dart/chapter_array_and_linkedlist/linked_list.dart @@ -0,0 +1,83 @@ +/** + * File: linked_list.dart + * Created Time: 2023-01-23 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +import '../utils/list_node.dart'; +import '../utils/print_util.dart'; + +/* Insert node P after node n0 in the linked list */ +void insert(ListNode n0, ListNode P) { + ListNode? n1 = n0.next; + P.next = n1; + n0.next = P; +} + +/* Remove the first node after node n0 in the linked list */ +void remove(ListNode n0) { + if (n0.next == null) return; + // n0 -> P -> n1 + ListNode P = n0.next!; + ListNode? n1 = P.next; + n0.next = n1; +} + +/* Access the node at index index in the linked list */ +ListNode? access(ListNode? head, int index) { + for (var i = 0; i < index; i++) { + if (head == null) return null; + head = head.next; + } + return head; +} + +/* Find the first node with value target in the linked list */ +int find(ListNode? head, int target) { + int index = 0; + while (head != null) { + if (head.val == target) { + return index; + } + head = head.next; + index++; + } + return -1; +} + +/* Driver Code */ +void main() { + // Initialize linked list + // Initialize each node + ListNode n0 = ListNode(1); + ListNode n1 = ListNode(3); + ListNode n2 = ListNode(2); + ListNode n3 = ListNode(5); + ListNode n4 = ListNode(4); + // Build references between nodes + n0.next = n1; + n1.next = n2; + n2.next = n3; + n3.next = n4; + + print('Initialized linked list is'); + printLinkedList(n0); + + /* Insert node */ + insert(n0, ListNode(0)); + print('Linked list after inserting node is'); + printLinkedList(n0); + + /* Remove node */ + remove(n0); + print('Linked list after removing node is'); + printLinkedList(n0); + + /* Access node */ + ListNode? node = access(n0, 3); + print('Value of node at index 3 in linked list = ${node!.val}'); + + /* Search node */ + int index = find(n0, 2); + print('Index of node with value 2 in linked list = $index'); +} diff --git a/en/codes/dart/chapter_array_and_linkedlist/list.dart b/en/codes/dart/chapter_array_and_linkedlist/list.dart new file mode 100644 index 000000000..4effdb311 --- /dev/null +++ b/en/codes/dart/chapter_array_and_linkedlist/list.dart @@ -0,0 +1,62 @@ +/** + * File: list.dart + * Created Time: 2023-01-24 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +// ignore_for_file: unused_local_variable + +/* Driver Code */ +void main() { + /* Initialize list */ + List nums = [1, 3, 2, 5, 4]; + print('List nums = $nums'); + + /* Update element */ + int _num = nums[1]; + print('Access element at index 1, get _num = $_num'); + + /* Add elements at the end */ + nums[1] = 0; + print('Update element at index 1 to 0, get nums = $nums'); + + /* Remove element */ + nums.clear(); + print('After clearing list, nums = $nums'); + + /* Direct traversal of list elements */ + nums.add(1); + nums.add(3); + nums.add(2); + nums.add(5); + nums.add(4); + print('After adding elements, nums = $nums'); + + /* Sort list */ + nums.insert(3, 6); + print('Insert number 6 at index 3, get nums = $nums'); + + /* Remove element */ + nums.removeAt(3); + print('Delete element at index 3, get nums = $nums'); + + /* Traverse list by index */ + int count = 0; + for (var i = 0; i < nums.length; i++) { + count += nums[i]; + } + /* Directly traverse list elements */ + count = 0; + for (var x in nums) { + count += x; + } + + /* Concatenate two lists */ + List nums1 = [6, 8, 7, 10, 9]; + nums.addAll(nums1); + print('After concatenating list nums1 to nums, get nums = $nums'); + + /* Sort list */ + nums.sort(); + print('After sorting list, nums = $nums'); +} diff --git a/en/codes/dart/chapter_array_and_linkedlist/my_list.dart b/en/codes/dart/chapter_array_and_linkedlist/my_list.dart new file mode 100644 index 000000000..6569ea188 --- /dev/null +++ b/en/codes/dart/chapter_array_and_linkedlist/my_list.dart @@ -0,0 +1,132 @@ +/** + * File: my_list.dart + * Created Time: 2023-02-05 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +/* List class */ +class MyList { + late List _arr; // Array (stores list elements) + int _capacity = 10; // List capacity + int _size = 0; // List length (current number of elements) + int _extendRatio = 2; // Multiple by which the list capacity is extended each time + + /* Constructor */ + MyList() { + _arr = List.filled(_capacity, 0); + } + + /* Get list length (current number of elements) */ + int size() => _size; + + /* Get list capacity */ + int capacity() => _capacity; + + /* Update element */ + int get(int index) { + if (index >= _size) throw RangeError('Index out of bounds'); + return _arr[index]; + } + + /* Add elements at the end */ + void set(int index, int _num) { + if (index >= _size) throw RangeError('Index out of bounds'); + _arr[index] = _num; + } + + /* Direct traversal of list elements */ + void add(int _num) { + // When the number of elements exceeds capacity, trigger the extension mechanism + if (_size == _capacity) extendCapacity(); + _arr[_size] = _num; + // Update the number of elements + _size++; + } + + /* Sort list */ + void insert(int index, int _num) { + if (index >= _size) throw RangeError('Index out of bounds'); + // When the number of elements exceeds capacity, trigger the extension mechanism + if (_size == _capacity) extendCapacity(); + // Move all elements after index index forward by one position + for (var j = _size - 1; j >= index; j--) { + _arr[j + 1] = _arr[j]; + } + _arr[index] = _num; + // Update the number of elements + _size++; + } + + /* Remove element */ + int remove(int index) { + if (index >= _size) throw RangeError('Index out of bounds'); + int _num = _arr[index]; + // Move all elements after index forward by one position + for (var j = index; j < _size - 1; j++) { + _arr[j] = _arr[j + 1]; + } + // Update the number of elements + _size--; + // Return the removed element + return _num; + } + + /* Driver Code */ + void extendCapacity() { + // Create new array with length _extendRatio times original array + final _newNums = List.filled(_capacity * _extendRatio, 0); + // Copy original array to new array + List.copyRange(_newNums, 0, _arr); + // Update _arr reference + _arr = _newNums; + // Add elements at the end + _capacity = _arr.length; + } + + /* Convert list to array */ + List toArray() { + List arr = []; + for (var i = 0; i < _size; i++) { + arr.add(get(i)); + } + return arr; + } +} + +/* Driver Code */ +void main() { + /* Initialize list */ + MyList nums = MyList(); + /* Direct traversal of list elements */ + nums.add(1); + nums.add(3); + nums.add(2); + nums.add(5); + nums.add(4); + print( + 'List nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}'); + + /* Sort list */ + nums.insert(3, 6); + print('Insert number 6 at index 3, get nums = ${nums.toArray()}'); + + /* Remove element */ + nums.remove(3); + print('Delete element at index 3, get nums = ${nums.toArray()}'); + + /* Update element */ + int _num = nums.get(1); + print('Access element at index 1, get _num = $_num'); + + /* Add elements at the end */ + nums.set(1, 0); + print('Update element at index 1 to 0, get nums = ${nums.toArray()}'); + + /* Test capacity expansion mechanism */ + for (var i = 0; i < 10; i++) { + // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + nums.add(i); + } + print( + 'After expansion, list nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}'); +} diff --git a/en/codes/dart/chapter_backtracking/n_queens.dart b/en/codes/dart/chapter_backtracking/n_queens.dart new file mode 100644 index 000000000..8dc95fddf --- /dev/null +++ b/en/codes/dart/chapter_backtracking/n_queens.dart @@ -0,0 +1,75 @@ +/** + * File: n_queens.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Backtracking algorithm: N queens */ +void backtrack( + int row, + int n, + List> state, + List>> res, + List cols, + List diags1, + List diags2, +) { + // When all rows are placed, record the solution + if (row == n) { + List> copyState = []; + for (List sRow in state) { + copyState.add(List.from(sRow)); + } + res.add(copyState); + return; + } + // Traverse all columns + for (int col = 0; col < n; col++) { + // Calculate the main diagonal and anti-diagonal corresponding to this cell + int diag1 = row - col + n - 1; + int diag2 = row + col; + // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { + // Attempt: place the queen in this cell + state[row][col] = "Q"; + cols[col] = true; + diags1[diag1] = true; + diags2[diag2] = true; + // Place the next row + backtrack(row + 1, n, state, res, cols, diags1, diags2); + // Backtrack: restore this cell to an empty cell + state[row][col] = "#"; + cols[col] = false; + diags1[diag1] = false; + diags2[diag2] = false; + } + } +} + +/* Solve N queens */ +List>> nQueens(int n) { + // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + List> state = List.generate(n, (index) => List.filled(n, "#")); + List cols = List.filled(n, false); // Record whether there is a queen in the column + List diags1 = List.filled(2 * n - 1, false); // Record whether there is a queen on the main diagonal + List diags2 = List.filled(2 * n - 1, false); // Record whether there is a queen on the anti-diagonal + List>> res = []; + + backtrack(0, n, state, res, cols, diags1, diags2); + + return res; +} + +/* Driver Code */ +void main() { + int n = 4; + List>> res = nQueens(n); + print("Input board size is $n"); + print("Total queen placement solutions: ${res.length}"); + for (List> state in res) { + print("--------------------"); + for (List row in state) { + print(row); + } + } +} diff --git a/en/codes/dart/chapter_backtracking/permutations_i.dart b/en/codes/dart/chapter_backtracking/permutations_i.dart new file mode 100644 index 000000000..978effb11 --- /dev/null +++ b/en/codes/dart/chapter_backtracking/permutations_i.dart @@ -0,0 +1,51 @@ +/** + * File: permutations_i.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Backtracking algorithm: Permutations I */ +void backtrack( + List state, + List choices, + List selected, + List> res, +) { + // When the state length equals the number of elements, record the solution + if (state.length == choices.length) { + res.add(List.from(state)); + return; + } + // Traverse all choices + for (int i = 0; i < choices.length; i++) { + int choice = choices[i]; + // Pruning: do not allow repeated selection of elements + if (!selected[i]) { + // Attempt: make choice, update state + selected[i] = true; + state.add(choice); + // Proceed to the next round of selection + backtrack(state, choices, selected, res); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + state.removeLast(); + } + } +} + +/* Permutations I */ +List> permutationsI(List nums) { + List> res = []; + backtrack([], nums, List.filled(nums.length, false), res); + return res; +} + +/* Driver Code */ +void main() { + List nums = [1, 2, 3]; + + List> res = permutationsI(nums); + + print("Input array nums = $nums"); + print("All permutations res = $res"); +} diff --git a/en/codes/dart/chapter_backtracking/permutations_ii.dart b/en/codes/dart/chapter_backtracking/permutations_ii.dart new file mode 100644 index 000000000..ce02b080f --- /dev/null +++ b/en/codes/dart/chapter_backtracking/permutations_ii.dart @@ -0,0 +1,53 @@ +/** + * File: permutations_ii.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Backtracking algorithm: Permutations II */ +void backtrack( + List state, + List choices, + List selected, + List> res, +) { + // When the state length equals the number of elements, record the solution + if (state.length == choices.length) { + res.add(List.from(state)); + return; + } + // Traverse all choices + Set duplicated = {}; + for (int i = 0; i < choices.length; i++) { + int choice = choices[i]; + // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if (!selected[i] && !duplicated.contains(choice)) { + // Attempt: make choice, update state + duplicated.add(choice); // Record the selected element value + selected[i] = true; + state.add(choice); + // Proceed to the next round of selection + backtrack(state, choices, selected, res); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + state.removeLast(); + } + } +} + +/* Permutations II */ +List> permutationsII(List nums) { + List> res = []; + backtrack([], nums, List.filled(nums.length, false), res); + return res; +} + +/* Driver Code */ +void main() { + List nums = [1, 2, 2]; + + List> res = permutationsII(nums); + + print("Input array nums = $nums"); + print("All permutations res = $res"); +} diff --git a/en/codes/dart/chapter_backtracking/preorder_traversal_i_compact.dart b/en/codes/dart/chapter_backtracking/preorder_traversal_i_compact.dart new file mode 100644 index 000000000..34d91a057 --- /dev/null +++ b/en/codes/dart/chapter_backtracking/preorder_traversal_i_compact.dart @@ -0,0 +1,35 @@ +/** + * File: preorder_traversal_i_compact.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +/* Preorder traversal: Example 1 */ +void preOrder(TreeNode? root, List res) { + if (root == null) { + return; + } + if (root.val == 7) { + // Record solution + res.add(root); + } + preOrder(root.left, res); + preOrder(root.right, res); +} + +/* Driver Code */ +void main() { + TreeNode? root = listToTree([1, 7, 3, 4, 5, 6, 7]); + print("\nInitialize binary tree"); + printTree(root); + + // Preorder traversal + List res = []; + preOrder(root, res); + + print("\nOutput all nodes with value 7"); + print(List.generate(res.length, (i) => res[i].val)); +} diff --git a/en/codes/dart/chapter_backtracking/preorder_traversal_ii_compact.dart b/en/codes/dart/chapter_backtracking/preorder_traversal_ii_compact.dart new file mode 100644 index 000000000..d2eaa43fb --- /dev/null +++ b/en/codes/dart/chapter_backtracking/preorder_traversal_ii_compact.dart @@ -0,0 +1,47 @@ +/** + * File: preorder_traversal_ii_compact.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +/* Preorder traversal: Example 2 */ +void preOrder( + TreeNode? root, + List path, + List> res, +) { + if (root == null) { + return; + } + + // Attempt + path.add(root); + if (root.val == 7) { + // Record solution + res.add(List.from(path)); + } + preOrder(root.left, path, res); + preOrder(root.right, path, res); + // Backtrack + path.removeLast(); +} + +/* Driver Code */ +void main() { + TreeNode? root = listToTree([1, 7, 3, 4, 5, 6, 7]); + print("\nInitialize binary tree"); + printTree(root); + + // Preorder traversal + List path = []; + List> res = []; + preOrder(root, path, res); + + print("\nOutput all paths from root node to node 7"); + for (List vals in res) { + print(List.generate(vals.length, (i) => vals[i].val)); + } +} diff --git a/en/codes/dart/chapter_backtracking/preorder_traversal_iii_compact.dart b/en/codes/dart/chapter_backtracking/preorder_traversal_iii_compact.dart new file mode 100644 index 000000000..ccce19cb0 --- /dev/null +++ b/en/codes/dart/chapter_backtracking/preorder_traversal_iii_compact.dart @@ -0,0 +1,47 @@ +/** + * File: preorder_traversal_iii_compact.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +/* Preorder traversal: Example 3 */ +void preOrder( + TreeNode? root, + List path, + List> res, +) { + if (root == null || root.val == 3) { + return; + } + + // Attempt + path.add(root); + if (root.val == 7) { + // Record solution + res.add(List.from(path)); + } + preOrder(root.left, path, res); + preOrder(root.right, path, res); + // Backtrack + path.removeLast(); +} + +/* Driver Code */ +void main() { + TreeNode? root = listToTree([1, 7, 3, 4, 5, 6, 7]); + print("\nInitialize binary tree"); + printTree(root); + + // Preorder traversal + List path = []; + List> res = []; + preOrder(root, path, res); + + print("\nOutput all paths from root node to node 7"); + for (List vals in res) { + print(List.generate(vals.length, (i) => vals[i].val)); + } +} diff --git a/en/codes/dart/chapter_backtracking/preorder_traversal_iii_template.dart b/en/codes/dart/chapter_backtracking/preorder_traversal_iii_template.dart new file mode 100644 index 000000000..85b68e1f1 --- /dev/null +++ b/en/codes/dart/chapter_backtracking/preorder_traversal_iii_template.dart @@ -0,0 +1,73 @@ +/** + * File: preorder_traversal_iii_template.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +/* Check if the current state is a solution */ +bool isSolution(List state) { + return state.isNotEmpty && state.last.val == 7; +} + +/* Record solution */ +void recordSolution(List state, List> res) { + res.add(List.from(state)); +} + +/* Check if the choice is valid under the current state */ +bool isValid(List state, TreeNode? choice) { + return choice != null && choice.val != 3; +} + +/* Update state */ +void makeChoice(List state, TreeNode? choice) { + state.add(choice!); +} + +/* Restore state */ +void undoChoice(List state, TreeNode? choice) { + state.removeLast(); +} + +/* Backtracking algorithm: Example 3 */ +void backtrack( + List state, + List choices, + List> res, +) { + // Check if it is a solution + if (isSolution(state)) { + // Record solution + recordSolution(state, res); + } + // Traverse all choices + for (TreeNode? choice in choices) { + // Pruning: check if the choice is valid + if (isValid(state, choice)) { + // Attempt: make choice, update state + makeChoice(state, choice); + // Proceed to the next round of selection + backtrack(state, [choice!.left, choice.right], res); + // Backtrack: undo choice, restore to previous state + undoChoice(state, choice); + } + } +} + +/* Driver Code */ +void main() { + TreeNode? root = listToTree([1, 7, 3, 4, 5, 6, 7]); + print("\nInitialize binary tree"); + printTree(root); + + // Backtracking algorithm + List> res = []; + backtrack([], [root!], res); + print("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3"); + for (List path in res) { + print(List.from(path.map((e) => e.val))); + } +} diff --git a/en/codes/dart/chapter_backtracking/subset_sum_i.dart b/en/codes/dart/chapter_backtracking/subset_sum_i.dart new file mode 100644 index 000000000..30b42e186 --- /dev/null +++ b/en/codes/dart/chapter_backtracking/subset_sum_i.dart @@ -0,0 +1,56 @@ +/** + * File: subset_sum_i.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Backtracking algorithm: Subset sum I */ +void backtrack( + List state, + int target, + List choices, + int start, + List> res, +) { + // When the subset sum equals target, record the solution + if (target == 0) { + res.add(List.from(state)); + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + for (int i = start; i < choices.length; i++) { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if (target - choices[i] < 0) { + break; + } + // Attempt: make choice, update target, start + state.add(choices[i]); + // Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i, res); + // Backtrack: undo choice, restore to previous state + state.removeLast(); + } +} + +/* Solve subset sum I */ +List> subsetSumI(List nums, int target) { + List state = []; // State (subset) + nums.sort(); // Sort nums + int start = 0; // Start point for traversal + List> res = []; // Result list (subset list) + backtrack(state, target, nums, start, res); + return res; +} + +/* Driver Code */ +void main() { + List nums = [3, 4, 5]; + int target = 9; + + List> res = subsetSumI(nums, target); + + print("Input array nums = $nums, target = $target"); + print("All subsets with sum equal to $target res = $res"); +} diff --git a/en/codes/dart/chapter_backtracking/subset_sum_i_naive.dart b/en/codes/dart/chapter_backtracking/subset_sum_i_naive.dart new file mode 100644 index 000000000..52845d172 --- /dev/null +++ b/en/codes/dart/chapter_backtracking/subset_sum_i_naive.dart @@ -0,0 +1,54 @@ +/** + * File: subset_sum_i_naive.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Backtracking algorithm: Subset sum I */ +void backtrack( + List state, + int target, + int total, + List choices, + List> res, +) { + // When the subset sum equals target, record the solution + if (total == target) { + res.add(List.from(state)); + return; + } + // Traverse all choices + for (int i = 0; i < choices.length; i++) { + // Pruning: if the subset sum exceeds target, skip this choice + if (total + choices[i] > target) { + continue; + } + // Attempt: make choice, update element sum total + state.add(choices[i]); + // Proceed to the next round of selection + backtrack(state, target, total + choices[i], choices, res); + // Backtrack: undo choice, restore to previous state + state.removeLast(); + } +} + +/* Solve subset sum I (including duplicate subsets) */ +List> subsetSumINaive(List nums, int target) { + List state = []; // State (subset) + int total = 0; // Sum of elements + List> res = []; // Result list (subset list) + backtrack(state, target, total, nums, res); + return res; +} + +/* Driver Code */ +void main() { + List nums = [3, 4, 5]; + int target = 9; + + List> res = subsetSumINaive(nums, target); + + print("Input array nums = $nums, target = $target"); + print("All subsets with sum equal to $target res = $res"); + print("Please note that this method outputs results containing duplicate sets"); +} diff --git a/en/codes/dart/chapter_backtracking/subset_sum_ii.dart b/en/codes/dart/chapter_backtracking/subset_sum_ii.dart new file mode 100644 index 000000000..7c8dbe1a0 --- /dev/null +++ b/en/codes/dart/chapter_backtracking/subset_sum_ii.dart @@ -0,0 +1,61 @@ +/** + * File: subset_sum_ii.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Backtracking algorithm: Subset sum II */ +void backtrack( + List state, + int target, + List choices, + int start, + List> res, +) { + // When the subset sum equals target, record the solution + if (target == 0) { + res.add(List.from(state)); + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + for (int i = start; i < choices.length; i++) { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if (target - choices[i] < 0) { + break; + } + // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + if (i > start && choices[i] == choices[i - 1]) { + continue; + } + // Attempt: make choice, update target, start + state.add(choices[i]); + // Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i + 1, res); + // Backtrack: undo choice, restore to previous state + state.removeLast(); + } +} + +/* Solve subset sum II */ +List> subsetSumII(List nums, int target) { + List state = []; // State (subset) + nums.sort(); // Sort nums + int start = 0; // Start point for traversal + List> res = []; // Result list (subset list) + backtrack(state, target, nums, start, res); + return res; +} + +/* Driver Code */ +void main() { + List nums = [4, 4, 5]; + int target = 9; + + List> res = subsetSumII(nums, target); + + print("Input array nums = $nums, target = $target"); + print("All subsets with sum equal to $target res = $res"); +} diff --git a/en/codes/dart/chapter_computational_complexity/iteration.dart b/en/codes/dart/chapter_computational_complexity/iteration.dart new file mode 100644 index 000000000..e05d5ed79 --- /dev/null +++ b/en/codes/dart/chapter_computational_complexity/iteration.dart @@ -0,0 +1,72 @@ +/** + * File: iteration.dart + * Created Time: 2023-08-27 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* for loop */ +int forLoop(int n) { + int res = 0; + // Sum 1, 2, ..., n-1, n + for (int i = 1; i <= n; i++) { + res += i; + } + return res; +} + +/* while loop */ +int whileLoop(int n) { + int res = 0; + int i = 1; // Initialize condition variable + // Sum 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // Update condition variable + } + return res; +} + +/* while loop (two updates) */ +int whileLoopII(int n) { + int res = 0; + int i = 1; // Initialize condition variable + // Sum 1, 4, 10, ... + while (i <= n) { + res += i; + // Update condition variable + i++; + i *= 2; + } + return res; +} + +/* Nested for loop */ +String nestedForLoop(int n) { + String res = ""; + // Loop i = 1, 2, ..., n-1, n + for (int i = 1; i <= n; i++) { + // Loop j = 1, 2, ..., n-1, n + for (int j = 1; j <= n; j++) { + res += "($i, $j), "; + } + } + return res; +} + +/* Driver Code */ +void main() { + int n = 5; + int res; + + res = forLoop(n); + print("\nFor loop sum result res = $res"); + + res = whileLoop(n); + print("\nWhile loop sum result res = $res"); + + res = whileLoopII(n); + print("\nWhile loop (two updates) sum result res = $res"); + + String resStr = nestedForLoop(n); + print("\nNested for loop result $resStr"); +} diff --git a/en/codes/dart/chapter_computational_complexity/recursion.dart b/en/codes/dart/chapter_computational_complexity/recursion.dart new file mode 100644 index 000000000..10116a087 --- /dev/null +++ b/en/codes/dart/chapter_computational_complexity/recursion.dart @@ -0,0 +1,70 @@ +/** + * File: recursion.dart + * Created Time: 2023-08-27 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Recursion */ +int recur(int n) { + // Termination condition + if (n == 1) return 1; + // Recurse: recursive call + int res = recur(n - 1); + // Return: return result + return n + res; +} + +/* Simulate recursion using iteration */ +int forLoopRecur(int n) { + // Use an explicit stack to simulate the system call stack + List stack = []; + int res = 0; + // Recurse: recursive call + for (int i = n; i > 0; i--) { + // Simulate "recurse" with "push" + stack.add(i); + } + // Return: return result + while (!stack.isEmpty) { + // Simulate "return" with "pop" + res += stack.removeLast(); + } + // res = 1+2+3+...+n + return res; +} + +/* Tail recursion */ +int tailRecur(int n, int res) { + // Termination condition + if (n == 0) return res; + // Tail recursive call + return tailRecur(n - 1, res + n); +} + +/* Fibonacci sequence: recursion */ +int fib(int n) { + // Termination condition f(1) = 0, f(2) = 1 + if (n == 1 || n == 2) return n - 1; + // Recursive call f(n) = f(n-1) + f(n-2) + int res = fib(n - 1) + fib(n - 2); + // Return result f(n) + return res; +} + +/* Driver Code */ +void main() { + int n = 5; + int res; + + res = recur(n); + print("\nRecursion sum result res = $res"); + + res = tailRecur(n, 0); + print("\nTail recursion sum result res = $res"); + + res = forLoopRecur(n); + print("\nUsing iteration to simulate recursion sum result res = $res"); + + res = fib(n); + print("\nThe ${n}th Fibonacci number is $res"); +} diff --git a/en/codes/dart/chapter_computational_complexity/space_complexity.dart b/en/codes/dart/chapter_computational_complexity/space_complexity.dart new file mode 100644 index 000000000..4c6507457 --- /dev/null +++ b/en/codes/dart/chapter_computational_complexity/space_complexity.dart @@ -0,0 +1,106 @@ +/** + * File: space_complexity.dart + * Created Time: 2023-2-12 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +// ignore_for_file: unused_local_variable + +import 'dart:collection'; +import '../utils/list_node.dart'; +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +/* Function */ +int function() { + // Perform some operations + return 0; +} + +/* Constant order */ +void constant(int n) { + // Constants, variables, objects occupy O(1) space + final int a = 0; + int b = 0; + List nums = List.filled(10000, 0); + ListNode node = ListNode(0); + // Variables in the loop occupy O(1) space + for (var i = 0; i < n; i++) { + int c = 0; + } + // Functions in the loop occupy O(1) space + for (var i = 0; i < n; i++) { + function(); + } +} + +/* Linear order */ +void linear(int n) { + // Array of length n uses O(n) space + List nums = List.filled(n, 0); + // A list of length n occupies O(n) space + List nodes = []; + for (var i = 0; i < n; i++) { + nodes.add(ListNode(i)); + } + // A hash table of length n occupies O(n) space + Map map = HashMap(); + for (var i = 0; i < n; i++) { + map.putIfAbsent(i, () => i.toString()); + } +} + +/* Linear order (recursive implementation) */ +void linearRecur(int n) { + print('Recursion n = $n'); + if (n == 1) return; + linearRecur(n - 1); +} + +/* Exponential order */ +void quadratic(int n) { + // Matrix uses O(n^2) space + List> numMatrix = List.generate(n, (_) => List.filled(n, 0)); + // 2D list uses O(n^2) space + List> numList = []; + for (var i = 0; i < n; i++) { + List tmp = []; + for (int j = 0; j < n; j++) { + tmp.add(0); + } + numList.add(tmp); + } +} + +/* Quadratic order (recursive implementation) */ +int quadraticRecur(int n) { + if (n <= 0) return 0; + List nums = List.filled(n, 0); + print('In recursion n = $n, nums length = ${nums.length}'); + return quadraticRecur(n - 1); +} + +/* Driver Code */ +TreeNode? buildTree(int n) { + if (n == 0) return null; + TreeNode root = TreeNode(0); + root.left = buildTree(n - 1); + root.right = buildTree(n - 1); + return root; +} + +/* Driver Code */ +void main() { + int n = 5; + // Constant order + constant(n); + // Linear order + linear(n); + linearRecur(n); + // Exponential order + quadratic(n); + quadraticRecur(n); + // Exponential order + TreeNode? root = buildTree(n); + printTree(root); +} diff --git a/en/codes/dart/chapter_computational_complexity/time_complexity.dart b/en/codes/dart/chapter_computational_complexity/time_complexity.dart new file mode 100644 index 000000000..f2338055b --- /dev/null +++ b/en/codes/dart/chapter_computational_complexity/time_complexity.dart @@ -0,0 +1,165 @@ +/** + * File: time_complexity.dart + * Created Time: 2023-02-12 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +// ignore_for_file: unused_local_variable + +/* Constant order */ +int constant(int n) { + int count = 0; + int size = 100000; + for (var i = 0; i < size; i++) { + count++; + } + return count; +} + +/* Linear order */ +int linear(int n) { + int count = 0; + for (var i = 0; i < n; i++) { + count++; + } + return count; +} + +/* Linear order (traversing array) */ +int arrayTraversal(List nums) { + int count = 0; + // Number of iterations is proportional to the array length + for (var _num in nums) { + count++; + } + return count; +} + +/* Exponential order */ +int quadratic(int n) { + int count = 0; + // Number of iterations is quadratically related to the data size n + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; +} + +/* Quadratic order (bubble sort) */ +int bubbleSort(List nums) { + int count = 0; // Counter + // Outer loop: unsorted range is [0, i] + for (var i = nums.length - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (var j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // Element swap includes 3 unit operations + } + } + } + return count; +} + +/* Exponential order (loop implementation) */ +int exponential(int n) { + int count = 0, base = 1; + // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + for (var i = 0; i < n; i++) { + for (var j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; +} + +/* Exponential order (recursive implementation) */ +int expRecur(int n) { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; +} + +/* Logarithmic order (loop implementation) */ +int logarithmic(int n) { + int count = 0; + while (n > 1) { + n = n ~/ 2; + count++; + } + return count; +} + +/* Logarithmic order (recursive implementation) */ +int logRecur(int n) { + if (n <= 1) return 0; + return logRecur(n ~/ 2) + 1; +} + +/* Linearithmic order */ +int linearLogRecur(int n) { + if (n <= 1) return 1; + int count = linearLogRecur(n ~/ 2) + linearLogRecur(n ~/ 2); + for (var i = 0; i < n; i++) { + count++; + } + return count; +} + +/* Factorial order (recursive implementation) */ +int factorialRecur(int n) { + if (n == 0) return 1; + int count = 0; + // Split from 1 into n + for (var i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; +} + +/* Driver Code */ +void main() { + // You can modify n to run and observe the trend of the number of operations for various complexities + int n = 8; + print('Input data size n = $n'); + + int count = constant(n); + print('Constant-time operations count = $count'); + + count = linear(n); + print('Linear-time operations count = $count'); + + count = arrayTraversal(List.filled(n, 0)); + print('Linear-time (array traversal) operations count = $count'); + + count = quadratic(n); + print('Quadratic-time operations count = $count'); + final nums = List.filled(n, 0); + for (int i = 0; i < n; i++) { + nums[i] = n - i; // [n,n-1,...,2,1] + } + count = bubbleSort(nums); + print('Quadratic-time (bubble sort) operations count = $count'); + + count = exponential(n); + print('Exponential-time (iterative) operations count = $count'); + count = expRecur(n); + print('Exponential-time (recursive) operations count = $count'); + + count = logarithmic(n); + print('Logarithmic-time (iterative) operations count = $count'); + count = logRecur(n); + print('Logarithmic-time (recursive) operations count = $count'); + + count = linearLogRecur(n); + print('Linearithmic-time (recursive) operations count = $count'); + + count = factorialRecur(n); + print('Factorial-time (recursive) operations count = $count'); +} diff --git a/en/codes/dart/chapter_computational_complexity/worst_best_time_complexity.dart b/en/codes/dart/chapter_computational_complexity/worst_best_time_complexity.dart new file mode 100644 index 000000000..63d980844 --- /dev/null +++ b/en/codes/dart/chapter_computational_complexity/worst_best_time_complexity.dart @@ -0,0 +1,40 @@ +/** + * File: worst_best_time_complexity.dart + * Created Time: 2023-02-12 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +List randomNumbers(int n) { + final nums = List.filled(n, 0); + // Generate array nums = { 1, 2, 3, ..., n } + for (var i = 0; i < n; i++) { + nums[i] = i + 1; + } + // Randomly shuffle array elements + nums.shuffle(); + + return nums; +} + +/* Find the index of number 1 in array nums */ +int findOne(List nums) { + for (var i = 0; i < nums.length; i++) { + // When element 1 is at the head of the array, best time complexity O(1) is achieved + // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + if (nums[i] == 1) return i; + } + + return -1; +} + +/* Driver Code */ +void main() { + for (var i = 0; i < 10; i++) { + int n = 100; + final nums = randomNumbers(n); + int index = findOne(nums); + print('\nArray [ 1, 2, ..., n ] after shuffling = $nums'); + print('Index of number 1 is + $index'); + } +} diff --git a/en/codes/dart/chapter_divide_and_conquer/binary_search_recur.dart b/en/codes/dart/chapter_divide_and_conquer/binary_search_recur.dart new file mode 100644 index 000000000..d33f1b751 --- /dev/null +++ b/en/codes/dart/chapter_divide_and_conquer/binary_search_recur.dart @@ -0,0 +1,42 @@ +/** + * File: binary_search_recur.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Binary search: problem f(i, j) */ +int dfs(List nums, int target, int i, int j) { + // If the interval is empty, it means there is no target element, return -1 + if (i > j) { + return -1; + } + // Calculate the midpoint index m + int m = (i + j) ~/ 2; + if (nums[m] < target) { + // Recursion subproblem f(m+1, j) + return dfs(nums, target, m + 1, j); + } else if (nums[m] > target) { + // Recursion subproblem f(i, m-1) + return dfs(nums, target, i, m - 1); + } else { + // Found the target element, return its index + return m; + } +} + +/* Binary search */ +int binarySearch(List nums, int target) { + int n = nums.length; + // Solve the problem f(0, n-1) + return dfs(nums, target, 0, n - 1); +} + +/* Driver Code */ +void main() { + int target = 6; + List nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; + + // Binary search (closed interval on both sides) + int index = binarySearch(nums, target); + print("Index of target element 6 = $index"); +} diff --git a/en/codes/dart/chapter_divide_and_conquer/build_tree.dart b/en/codes/dart/chapter_divide_and_conquer/build_tree.dart new file mode 100644 index 000000000..a948bf0e7 --- /dev/null +++ b/en/codes/dart/chapter_divide_and_conquer/build_tree.dart @@ -0,0 +1,55 @@ +/** + * File: build_tree.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +/* Build binary tree: divide and conquer */ +TreeNode? dfs( + List preorder, + Map inorderMap, + int i, + int l, + int r, +) { + // Terminate when the subtree interval is empty + if (r - l < 0) { + return null; + } + // Initialize the root node + TreeNode? root = TreeNode(preorder[i]); + // Query m to divide the left and right subtrees + int m = inorderMap[preorder[i]]!; + // Subproblem: build the left subtree + root.left = dfs(preorder, inorderMap, i + 1, l, m - 1); + // Subproblem: build the right subtree + root.right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r); + // Return the root node + return root; +} + +/* Build binary tree */ +TreeNode? buildTree(List preorder, List inorder) { + // Initialize hash map, storing the mapping from inorder elements to indices + Map inorderMap = {}; + for (int i = 0; i < inorder.length; i++) { + inorderMap[inorder[i]] = i; + } + TreeNode? root = dfs(preorder, inorderMap, 0, 0, inorder.length - 1); + return root; +} + +/* Driver Code */ +void main() { + List preorder = [3, 9, 2, 1, 7]; + List inorder = [9, 3, 1, 2, 7]; + print("Pre-order traversal = $preorder"); + print("In-order traversal = $inorder"); + + TreeNode? root = buildTree(preorder, inorder); + print("The constructed binary tree is:"); + printTree(root!); +} diff --git a/en/codes/dart/chapter_divide_and_conquer/hanota.dart b/en/codes/dart/chapter_divide_and_conquer/hanota.dart new file mode 100644 index 000000000..bf4d4e3a8 --- /dev/null +++ b/en/codes/dart/chapter_divide_and_conquer/hanota.dart @@ -0,0 +1,54 @@ +/** + * File: hanota.dart + * Created Time: 2023-08-10 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Move a disk */ +void move(List src, List tar) { + // Take out a disk from the top of src + int pan = src.removeLast(); + // Place the disk on top of tar + tar.add(pan); +} + +/* Solve the Tower of Hanoi problem f(i) */ +void dfs(int i, List src, List buf, List tar) { + // If there is only one disk left in src, move it directly to tar + if (i == 1) { + move(src, tar); + return; + } + // Subproblem f(i-1): move the top i-1 disks from src to buf using tar + dfs(i - 1, src, tar, buf); + // Subproblem f(1): move the remaining disk from src to tar + move(src, tar); + // Subproblem f(i-1): move the top i-1 disks from buf to tar using src + dfs(i - 1, buf, src, tar); +} + +/* Solve the Tower of Hanoi problem */ +void solveHanota(List A, List B, List C) { + int n = A.length; + // Move the top n disks from A to C using B + dfs(n, A, B, C); +} + +/* Driver Code */ +void main() { + // The tail of the list is the top of the rod + List A = [5, 4, 3, 2, 1]; + List B = []; + List C = []; + print("In initial state:"); + print("A = $A"); + print("B = $B"); + print("C = $C"); + + solveHanota(A, B, C); + + print("After disk movement is complete:"); + print("A = $A"); + print("B = $B"); + print("C = $C"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_backtrack.dart b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_backtrack.dart new file mode 100644 index 000000000..38d02b666 --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_backtrack.dart @@ -0,0 +1,39 @@ +/** + * File: climbing_stairs_backtrack.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Backtracking */ +void backtrack(List choices, int state, int n, List res) { + // When climbing to the n-th stair, add 1 to the solution count + if (state == n) { + res[0]++; + } + // Traverse all choices + for (int choice in choices) { + // Pruning: not allowed to go beyond the n-th stair + if (state + choice > n) continue; + // Attempt: make choice, update state + backtrack(choices, state + choice, n, res); + // Backtrack + } +} + +/* Climbing stairs: Backtracking */ +int climbingStairsBacktrack(int n) { + List choices = [1, 2]; // Can choose to climb up 1 or 2 stairs + int state = 0; // Start climbing from the 0-th stair + List res = []; + res.add(0); // Use res[0] to record the solution count + backtrack(choices, state, n, res); + return res[0]; +} + +/* Driver Code */ +void main() { + int n = 9; + + int res = climbingStairsBacktrack(n); + print("Climbing $n stairs has $res solutions"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_constraint_dp.dart b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_constraint_dp.dart new file mode 100644 index 000000000..e89c358b7 --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_constraint_dp.dart @@ -0,0 +1,33 @@ +/** + * File: climbing_stairs_constraint_dp.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Climbing stairs with constraint: Dynamic programming */ +int climbingStairsConstraintDP(int n) { + if (n == 1 || n == 2) { + return 1; + } + // Initialize dp table, used to store solutions to subproblems + List> dp = List.generate(n + 1, (index) => List.filled(3, 0)); + // Initial state: preset the solution to the smallest subproblem + dp[1][1] = 1; + dp[1][2] = 0; + dp[2][1] = 0; + dp[2][2] = 1; + // State transition: gradually solve larger subproblems from smaller ones + for (int i = 3; i <= n; i++) { + dp[i][1] = dp[i - 1][2]; + dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; + } + return dp[n][1] + dp[n][2]; +} + +/* Driver Code */ +void main() { + int n = 9; + + int res = climbingStairsConstraintDP(n); + print("Climbing $n stairs has $res solutions"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs.dart b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs.dart new file mode 100644 index 000000000..8a913dfa4 --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs.dart @@ -0,0 +1,27 @@ +/** + * File: climbing_stairs_dfs.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Search */ +int dfs(int i) { + // Known dp[1] and dp[2], return them + if (i == 1 || i == 2) return i; + // dp[i] = dp[i-1] + dp[i-2] + int count = dfs(i - 1) + dfs(i - 2); + return count; +} + +/* Climbing stairs: Search */ +int climbingStairsDFS(int n) { + return dfs(n); +} + +/* Driver Code */ +void main() { + int n = 9; + + int res = climbingStairsDFS(n); + print("Climbing $n stairs has $res solutions"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs_mem.dart b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs_mem.dart new file mode 100644 index 000000000..9deb07499 --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dfs_mem.dart @@ -0,0 +1,33 @@ +/** + * File: climbing_stairs_dfs_mem.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Memoization search */ +int dfs(int i, List mem) { + // Known dp[1] and dp[2], return them + if (i == 1 || i == 2) return i; + // If record dp[i] exists, return it directly + if (mem[i] != -1) return mem[i]; + // dp[i] = dp[i-1] + dp[i-2] + int count = dfs(i - 1, mem) + dfs(i - 2, mem); + // Record dp[i] + mem[i] = count; + return count; +} + +/* Climbing stairs: Memoization search */ +int climbingStairsDFSMem(int n) { + // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + List mem = List.filled(n + 1, -1); + return dfs(n, mem); +} + +/* Driver Code */ +void main() { + int n = 9; + + int res = climbingStairsDFSMem(n); + print("Climbing $n stairs has $res solutions"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dp.dart b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dp.dart new file mode 100644 index 000000000..43be90a58 --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/climbing_stairs_dp.dart @@ -0,0 +1,43 @@ +/** + * File: climbing_stairs_dp.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Climbing stairs: Dynamic programming */ +int climbingStairsDP(int n) { + if (n == 1 || n == 2) return n; + // Initialize dp table, used to store solutions to subproblems + List dp = List.filled(n + 1, 0); + // Initial state: preset the solution to the smallest subproblem + dp[1] = 1; + dp[2] = 2; + // State transition: gradually solve larger subproblems from smaller ones + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; +} + +/* Climbing stairs: Space-optimized dynamic programming */ +int climbingStairsDPComp(int n) { + if (n == 1 || n == 2) return n; + int a = 1, b = 2; + for (int i = 3; i <= n; i++) { + int tmp = b; + b = a + b; + a = tmp; + } + return b; +} + +/* Driver Code */ +void main() { + int n = 9; + + int res = climbingStairsDP(n); + print("Climbing $n stairs has $res solutions"); + + res = climbingStairsDPComp(n); + print("Climbing $n stairs has $res solutions"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/coin_change.dart b/en/codes/dart/chapter_dynamic_programming/coin_change.dart new file mode 100644 index 000000000..463161229 --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/coin_change.dart @@ -0,0 +1,68 @@ +/** + * File: coin_change.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:math'; + +/* Coin change: Dynamic programming */ +int coinChangeDP(List coins, int amt) { + int n = coins.length; + int MAX = amt + 1; + // Initialize dp table + List> dp = List.generate(n + 1, (index) => List.filled(amt + 1, 0)); + // State transition: first row and first column + for (int a = 1; a <= amt; a++) { + dp[0][a] = MAX; + } + // State transition: rest of the rows and columns + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); + } + } + } + return dp[n][amt] != MAX ? dp[n][amt] : -1; +} + +/* Coin change: Space-optimized dynamic programming */ +int coinChangeDPComp(List coins, int amt) { + int n = coins.length; + int MAX = amt + 1; + // Initialize dp table + List dp = List.filled(amt + 1, MAX); + dp[0] = 0; + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1); + } + } + } + return dp[amt] != MAX ? dp[amt] : -1; +} + +/* Driver Code */ +void main() { + List coins = [1, 2, 5]; + int amt = 4; + + // Dynamic programming + int res = coinChangeDP(coins, amt); + print("Minimum coins needed to make target amount is $res"); + + // Space-optimized dynamic programming + res = coinChangeDPComp(coins, amt); + print("Minimum coins needed to make target amount is $res"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/coin_change_ii.dart b/en/codes/dart/chapter_dynamic_programming/coin_change_ii.dart new file mode 100644 index 000000000..5c30e80fb --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/coin_change_ii.dart @@ -0,0 +1,64 @@ +/** + * File: coin_change_ii.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Coin change II: Dynamic programming */ +int coinChangeIIDP(List coins, int amt) { + int n = coins.length; + // Initialize dp table + List> dp = List.generate(n + 1, (index) => List.filled(amt + 1, 0)); + // Initialize first column + for (int i = 0; i <= n; i++) { + dp[i][0] = 1; + } + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; + } + } + } + return dp[n][amt]; +} + +/* Coin change II: Space-optimized dynamic programming */ +int coinChangeIIDPComp(List coins, int amt) { + int n = coins.length; + // Initialize dp table + List dp = List.filled(amt + 1, 0); + dp[0] = 1; + // State transition + for (int i = 1; i <= n; i++) { + for (int a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[a] = dp[a] + dp[a - coins[i - 1]]; + } + } + } + return dp[amt]; +} + +/* Driver Code */ +void main() { + List coins = [1, 2, 5]; + int amt = 5; + + // Dynamic programming + int res = coinChangeIIDP(coins, amt); + print("Number of coin combinations to make target amount is $res"); + + // Space-optimized dynamic programming + res = coinChangeIIDPComp(coins, amt); + print("Number of coin combinations to make target amount is $res"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/edit_distance.dart b/en/codes/dart/chapter_dynamic_programming/edit_distance.dart new file mode 100644 index 000000000..f48093e3a --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/edit_distance.dart @@ -0,0 +1,125 @@ +/** + * File: edit_distance.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:math'; + +/* Edit distance: Brute-force search */ +int editDistanceDFS(String s, String t, int i, int j) { + // If both s and t are empty, return 0 + if (i == 0 && j == 0) return 0; + // If s is empty, return length of t + if (i == 0) return j; + // If t is empty, return length of s + if (j == 0) return i; + // If two characters are equal, skip both characters + if (s[i - 1] == t[j - 1]) return editDistanceDFS(s, t, i - 1, j - 1); + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + int insert = editDistanceDFS(s, t, i, j - 1); + int delete = editDistanceDFS(s, t, i - 1, j); + int replace = editDistanceDFS(s, t, i - 1, j - 1); + // Return minimum edit steps + return min(min(insert, delete), replace) + 1; +} + +/* Edit distance: Memoization search */ +int editDistanceDFSMem(String s, String t, List> mem, int i, int j) { + // If both s and t are empty, return 0 + if (i == 0 && j == 0) return 0; + // If s is empty, return length of t + if (i == 0) return j; + // If t is empty, return length of s + if (j == 0) return i; + // If there's a record, return it directly + if (mem[i][j] != -1) return mem[i][j]; + // If two characters are equal, skip both characters + if (s[i - 1] == t[j - 1]) return editDistanceDFSMem(s, t, mem, i - 1, j - 1); + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + int insert = editDistanceDFSMem(s, t, mem, i, j - 1); + int delete = editDistanceDFSMem(s, t, mem, i - 1, j); + int replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1); + // Record and return minimum edit steps + mem[i][j] = min(min(insert, delete), replace) + 1; + return mem[i][j]; +} + +/* Edit distance: Dynamic programming */ +int editDistanceDP(String s, String t) { + int n = s.length, m = t.length; + List> dp = List.generate(n + 1, (_) => List.filled(m + 1, 0)); + // State transition: first row and first column + for (int i = 1; i <= n; i++) { + dp[i][0] = i; + } + for (int j = 1; j <= m; j++) { + dp[0][j] = j; + } + // State transition: rest of the rows and columns + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + if (s[i - 1] == t[j - 1]) { + // If two characters are equal, skip both characters + dp[i][j] = dp[i - 1][j - 1]; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; + } + } + } + return dp[n][m]; +} + +/* Edit distance: Space-optimized dynamic programming */ +int editDistanceDPComp(String s, String t) { + int n = s.length, m = t.length; + List dp = List.filled(m + 1, 0); + // State transition: first row + for (int j = 1; j <= m; j++) { + dp[j] = j; + } + // State transition: rest of the rows + for (int i = 1; i <= n; i++) { + // State transition: first column + int leftup = dp[0]; // Temporarily store dp[i-1, j-1] + dp[0] = i; + // State transition: rest of the columns + for (int j = 1; j <= m; j++) { + int temp = dp[j]; + if (s[i - 1] == t[j - 1]) { + // If two characters are equal, skip both characters + dp[j] = leftup; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1; + } + leftup = temp; // Update for next round's dp[i-1, j-1] + } + } + return dp[m]; +} + +/* Driver Code */ +void main() { + String s = "bag"; + String t = "pack"; + int n = s.length, m = t.length; + + // Brute-force search + int res = editDistanceDFS(s, t, n, m); + print("Changing " + s + " to " + t + " requires minimum $res edits"); + + // Memoization search + List> mem = List.generate(n + 1, (_) => List.filled(m + 1, -1)); + res = editDistanceDFSMem(s, t, mem, n, m); + print("Changing " + s + " to " + t + " requires minimum $res edits"); + + // Dynamic programming + res = editDistanceDP(s, t); + print("Changing " + s + " to " + t + " requires minimum $res edits"); + + // Space-optimized dynamic programming + res = editDistanceDPComp(s, t); + print("Changing " + s + " to " + t + " requires minimum $res edits"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/knapsack.dart b/en/codes/dart/chapter_dynamic_programming/knapsack.dart new file mode 100644 index 000000000..b13744b84 --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/knapsack.dart @@ -0,0 +1,116 @@ +/** + * File: knapsack.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:math'; + +/* 0-1 knapsack: Brute-force search */ +int knapsackDFS(List wgt, List val, int i, int c) { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i == 0 || c == 0) { + return 0; + } + // If exceeds knapsack capacity, can only choose not to put it in + if (wgt[i - 1] > c) { + return knapsackDFS(wgt, val, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + int no = knapsackDFS(wgt, val, i - 1, c); + int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; + // Return the larger value of the two options + return max(no, yes); +} + +/* 0-1 knapsack: Memoization search */ +int knapsackDFSMem( + List wgt, + List val, + List> mem, + int i, + int c, +) { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i == 0 || c == 0) { + return 0; + } + // If there's a record, return it directly + if (mem[i][c] != -1) { + return mem[i][c]; + } + // If exceeds knapsack capacity, can only choose not to put it in + if (wgt[i - 1] > c) { + return knapsackDFSMem(wgt, val, mem, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + int no = knapsackDFSMem(wgt, val, mem, i - 1, c); + int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; + // Record and return the larger value of the two options + mem[i][c] = max(no, yes); + return mem[i][c]; +} + +/* 0-1 knapsack: Dynamic programming */ +int knapsackDP(List wgt, List val, int cap) { + int n = wgt.length; + // Initialize dp table + List> dp = List.generate(n + 1, (index) => List.filled(cap + 1, 0)); + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c]; + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[n][cap]; +} + +/* 0-1 knapsack: Space-optimized dynamic programming */ +int knapsackDPComp(List wgt, List val, int cap) { + int n = wgt.length; + // Initialize dp table + List dp = List.filled(cap + 1, 0); + // State transition + for (int i = 1; i <= n; i++) { + // Traverse in reverse order + for (int c = cap; c >= 1; c--) { + if (wgt[i - 1] <= c) { + // The larger value between not selecting and selecting item i + dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; +} + +/* Driver Code */ +void main() { + List wgt = [10, 20, 30, 40, 50]; + List val = [50, 120, 150, 210, 240]; + int cap = 50; + int n = wgt.length; + + // Brute-force search + int res = knapsackDFS(wgt, val, n, cap); + print("Maximum item value not exceeding knapsack capacity is $res"); + + // Memoization search + List> mem = + List.generate(n + 1, (index) => List.filled(cap + 1, -1)); + res = knapsackDFSMem(wgt, val, mem, n, cap); + print("Maximum item value not exceeding knapsack capacity is $res"); + + // Dynamic programming + res = knapsackDP(wgt, val, cap); + print("Maximum item value not exceeding knapsack capacity is $res"); + + // Space-optimized dynamic programming + res = knapsackDPComp(wgt, val, cap); + print("Maximum item value not exceeding knapsack capacity is $res"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/min_cost_climbing_stairs_dp.dart b/en/codes/dart/chapter_dynamic_programming/min_cost_climbing_stairs_dp.dart new file mode 100644 index 000000000..e87be967c --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/min_cost_climbing_stairs_dp.dart @@ -0,0 +1,48 @@ +/** + * File: min_cost_climbing_stairs_dp.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:math'; + +/* Minimum cost climbing stairs: Dynamic programming */ +int minCostClimbingStairsDP(List cost) { + int n = cost.length - 1; + if (n == 1 || n == 2) return cost[n]; + // Initialize dp table, used to store solutions to subproblems + List dp = List.filled(n + 1, 0); + // Initial state: preset the solution to the smallest subproblem + dp[1] = cost[1]; + dp[2] = cost[2]; + // State transition: gradually solve larger subproblems from smaller ones + for (int i = 3; i <= n; i++) { + dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; + } + return dp[n]; +} + +/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +int minCostClimbingStairsDPComp(List cost) { + int n = cost.length - 1; + if (n == 1 || n == 2) return cost[n]; + int a = cost[1], b = cost[2]; + for (int i = 3; i <= n; i++) { + int tmp = b; + b = min(a, tmp) + cost[i]; + a = tmp; + } + return b; +} + +/* Driver Code */ +void main() { + List cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]; + print("Input stair cost list is $cost"); + + int res = minCostClimbingStairsDP(cost); + print("Minimum cost to climb stairs is $res"); + + res = minCostClimbingStairsDPComp(cost); + print("Minimum cost to climb stairs is $res"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/min_path_sum.dart b/en/codes/dart/chapter_dynamic_programming/min_path_sum.dart new file mode 100644 index 000000000..1ab3b46fc --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/min_path_sum.dart @@ -0,0 +1,120 @@ +/** + * File: min_path_sum.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:math'; + +/* Minimum path sum: Brute-force search */ +int minPathSumDFS(List> grid, int i, int j) { + // If it's the top-left cell, terminate the search + if (i == 0 && j == 0) { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + // In Dart, int type is fixed-range integer, no value representing "infinity" + return BigInt.from(2).pow(31).toInt(); + } + // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + int up = minPathSumDFS(grid, i - 1, j); + int left = minPathSumDFS(grid, i, j - 1); + // Return the minimum path cost from top-left to (i, j) + return min(left, up) + grid[i][j]; +} + +/* Minimum path sum: Memoization search */ +int minPathSumDFSMem(List> grid, List> mem, int i, int j) { + // If it's the top-left cell, terminate the search + if (i == 0 && j == 0) { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + // In Dart, int type is fixed-range integer, no value representing "infinity" + return BigInt.from(2).pow(31).toInt(); + } + // If there's a record, return it directly + if (mem[i][j] != -1) { + return mem[i][j]; + } + // Minimum path cost for left and upper cells + int up = minPathSumDFSMem(grid, mem, i - 1, j); + int left = minPathSumDFSMem(grid, mem, i, j - 1); + // Record and return the minimum path cost from top-left to (i, j) + mem[i][j] = min(left, up) + grid[i][j]; + return mem[i][j]; +} + +/* Minimum path sum: Dynamic programming */ +int minPathSumDP(List> grid) { + int n = grid.length, m = grid[0].length; + // Initialize dp table + List> dp = List.generate(n, (i) => List.filled(m, 0)); + dp[0][0] = grid[0][0]; + // State transition: first row + for (int j = 1; j < m; j++) { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + // State transition: first column + for (int i = 1; i < n; i++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + // State transition: rest of the rows and columns + for (int i = 1; i < n; i++) { + for (int j = 1; j < m; j++) { + dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; + } + } + return dp[n - 1][m - 1]; +} + +/* Minimum path sum: Space-optimized dynamic programming */ +int minPathSumDPComp(List> grid) { + int n = grid.length, m = grid[0].length; + // Initialize dp table + List dp = List.filled(m, 0); + dp[0] = grid[0][0]; + for (int j = 1; j < m; j++) { + dp[j] = dp[j - 1] + grid[0][j]; + } + // State transition: rest of the rows + for (int i = 1; i < n; i++) { + // State transition: first column + dp[0] = dp[0] + grid[i][0]; + // State transition: rest of the columns + for (int j = 1; j < m; j++) { + dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + return dp[m - 1]; +} + +/* Driver Code */ +void main() { + List> grid = [ + [1, 3, 1, 5], + [2, 2, 4, 2], + [5, 3, 2, 1], + [4, 3, 5, 2], + ]; + int n = grid.length, m = grid[0].length; + +// Brute-force search + int res = minPathSumDFS(grid, n - 1, m - 1); + print("Minimum path sum from top-left to bottom-right is $res"); + +// Memoization search + List> mem = List.generate(n, (i) => List.filled(m, -1)); + res = minPathSumDFSMem(grid, mem, n - 1, m - 1); + print("Minimum path sum from top-left to bottom-right is $res"); + +// Dynamic programming + res = minPathSumDP(grid); + print("Minimum path sum from top-left to bottom-right is $res"); + +// Space-optimized dynamic programming + res = minPathSumDPComp(grid); + print("Minimum path sum from top-left to bottom-right is $res"); +} diff --git a/en/codes/dart/chapter_dynamic_programming/unbounded_knapsack.dart b/en/codes/dart/chapter_dynamic_programming/unbounded_knapsack.dart new file mode 100644 index 000000000..a6411a2ce --- /dev/null +++ b/en/codes/dart/chapter_dynamic_programming/unbounded_knapsack.dart @@ -0,0 +1,62 @@ +/** + * File: unbounded_knapsack.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:math'; + +/* Unbounded knapsack: Dynamic programming */ +int unboundedKnapsackDP(List wgt, List val, int cap) { + int n = wgt.length; + // Initialize dp table + List> dp = List.generate(n + 1, (index) => List.filled(cap + 1, 0)); + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c]; + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[n][cap]; +} + +/* Unbounded knapsack: Space-optimized dynamic programming */ +int unboundedKnapsackDPComp(List wgt, List val, int cap) { + int n = wgt.length; + // Initialize dp table + List dp = List.filled(cap + 1, 0); + // State transition + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[c] = dp[c]; + } else { + // The larger value between not selecting and selecting item i + dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; +} + +/* Driver Code */ +void main() { + List wgt = [1, 2, 3]; + List val = [5, 11, 15]; + int cap = 4; + + // Dynamic programming + int res = unboundedKnapsackDP(wgt, val, cap); + print("Maximum item value not exceeding knapsack capacity is $res"); + + // Space-optimized dynamic programming + int resComp = unboundedKnapsackDPComp(wgt, val, cap); + print("Maximum item value not exceeding knapsack capacity is $resComp"); +} diff --git a/en/codes/dart/chapter_graph/graph_adjacency_list.dart b/en/codes/dart/chapter_graph/graph_adjacency_list.dart new file mode 100644 index 000000000..5240af2ac --- /dev/null +++ b/en/codes/dart/chapter_graph/graph_adjacency_list.dart @@ -0,0 +1,124 @@ +/** + * File: graph_adjacency_list.dart + * Created Time: 2023-05-15 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/vertex.dart'; + +/* Undirected graph class based on adjacency list */ +class GraphAdjList { + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + Map> adjList = {}; + + /* Constructor */ + GraphAdjList(List> edges) { + for (List edge in edges) { + addVertex(edge[0]); + addVertex(edge[1]); + addEdge(edge[0], edge[1]); + } + } + + /* Get the number of vertices */ + int size() { + return adjList.length; + } + + /* Add edge */ + void addEdge(Vertex vet1, Vertex vet2) { + if (!adjList.containsKey(vet1) || + !adjList.containsKey(vet2) || + vet1 == vet2) { + throw ArgumentError; + } + // Add edge vet1 - vet2 + adjList[vet1]!.add(vet2); + adjList[vet2]!.add(vet1); + } + + /* Remove edge */ + void removeEdge(Vertex vet1, Vertex vet2) { + if (!adjList.containsKey(vet1) || + !adjList.containsKey(vet2) || + vet1 == vet2) { + throw ArgumentError; + } + // Remove edge vet1 - vet2 + adjList[vet1]!.remove(vet2); + adjList[vet2]!.remove(vet1); + } + + /* Add vertex */ + void addVertex(Vertex vet) { + if (adjList.containsKey(vet)) return; + // Add a new linked list in the adjacency list + adjList[vet] = []; + } + + /* Remove vertex */ + void removeVertex(Vertex vet) { + if (!adjList.containsKey(vet)) { + throw ArgumentError; + } + // Remove the linked list corresponding to vertex vet in the adjacency list + adjList.remove(vet); + // Traverse the linked lists of other vertices and remove all edges containing vet + adjList.forEach((key, value) { + value.remove(vet); + }); + } + + /* Print adjacency list */ + void printAdjList() { + print("Adjacency list ="); + adjList.forEach((key, value) { + List tmp = []; + for (Vertex vertex in value) { + tmp.add(vertex.val); + } + print("${key.val}: $tmp,"); + }); + } +} + +/* Driver Code */ +void main() { + /* Add edge */ + List v = Vertex.valsToVets([1, 3, 2, 5, 4]); + List> edges = [ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[2], v[3]], + [v[2], v[4]], + [v[3], v[4]], + ]; + GraphAdjList graph = GraphAdjList(edges); + print("\nAfter initialization, graph is"); + graph.printAdjList(); + + /* Add edge */ + // Vertices 1, 3 are v[0], v[1] + graph.addEdge(v[0], v[2]); + print("\nAfter adding edge 1-2, graph is"); + graph.printAdjList(); + + /* Remove edge */ + // Vertex 3 is v[1] + graph.removeEdge(v[0], v[1]); + print("\nAfter removing edge 1-3, graph is"); + graph.printAdjList(); + + /* Add vertex */ + Vertex v5 = Vertex(6); + graph.addVertex(v5); + print("\nAfter adding vertex 6, graph is"); + graph.printAdjList(); + + /* Remove vertex */ + // Vertex 3 is v[1] + graph.removeVertex(v[1]); + print("\nAfter removing vertex 3, graph is"); + graph.printAdjList(); +} diff --git a/en/codes/dart/chapter_graph/graph_adjacency_matrix.dart b/en/codes/dart/chapter_graph/graph_adjacency_matrix.dart new file mode 100644 index 000000000..7bfd10eaa --- /dev/null +++ b/en/codes/dart/chapter_graph/graph_adjacency_matrix.dart @@ -0,0 +1,133 @@ +/** + * File: graph_adjacency_matrix.dart + * Created Time: 2023-05-15 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; + +/* Undirected graph class based on adjacency matrix */ +class GraphAdjMat { + List vertices = []; // Vertex elements, elements represent "vertex values", indices represent "vertex indices" + List> adjMat = []; // Adjacency matrix, where the row and column indices correspond to the "vertex index" + + /* Constructor */ + GraphAdjMat(List vertices, List> edges) { + this.vertices = []; + this.adjMat = []; + // Add vertex + for (int val in vertices) { + addVertex(val); + } + // Add edge + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + for (List e in edges) { + addEdge(e[0], e[1]); + } + } + + /* Get the number of vertices */ + int size() { + return vertices.length; + } + + /* Add vertex */ + void addVertex(int val) { + int n = size(); + // Add the value of the new vertex to the vertex list + vertices.add(val); + // Add a row to the adjacency matrix + List newRow = List.filled(n, 0, growable: true); + adjMat.add(newRow); + // Add a column to the adjacency matrix + for (List row in adjMat) { + row.add(0); + } + } + + /* Remove vertex */ + void removeVertex(int index) { + if (index >= size()) { + throw IndexError; + } + // Remove the vertex at index from the vertex list + vertices.removeAt(index); + // Remove the row at index from the adjacency matrix + adjMat.removeAt(index); + // Remove the column at index from the adjacency matrix + for (List row in adjMat) { + row.removeAt(index); + } + } + + /* Add edge */ + // Parameters i, j correspond to the vertices element indices + void addEdge(int i, int j) { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { + throw IndexError; + } + // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + adjMat[i][j] = 1; + adjMat[j][i] = 1; + } + + /* Remove edge */ + // Parameters i, j correspond to the vertices element indices + void removeEdge(int i, int j) { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { + throw IndexError; + } + adjMat[i][j] = 0; + adjMat[j][i] = 0; + } + + /* Print adjacency matrix */ + void printAdjMat() { + print("Vertex list = $vertices"); + print("Adjacency matrix = "); + printMatrix(adjMat); + } +} + +/* Driver Code */ +void main() { + /* Add edge */ + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + List vertices = [1, 3, 2, 5, 4]; + List> edges = [ + [0, 1], + [0, 3], + [1, 2], + [2, 3], + [2, 4], + [3, 4], + ]; + GraphAdjMat graph = GraphAdjMat(vertices, edges); + print("\nAfter initialization, graph is"); + graph.printAdjMat(); + + /* Add edge */ + // Add vertex + graph.addEdge(0, 2); + print("\nAfter adding edge 1-2, graph is"); + graph.printAdjMat(); + + /* Remove edge */ + // Vertices 1, 3 have indices 0, 1 respectively + graph.removeEdge(0, 1); + print("\nAfter removing edge 1-3, graph is"); + graph.printAdjMat(); + + /* Add vertex */ + graph.addVertex(6); + print("\nAfter adding vertex 6, graph is"); + graph.printAdjMat(); + + /* Remove vertex */ + // Vertex 3 has index 1 + graph.removeVertex(1); + print("\nAfter removing vertex 3, graph is"); + graph.printAdjMat(); +} diff --git a/en/codes/dart/chapter_graph/graph_bfs.dart b/en/codes/dart/chapter_graph/graph_bfs.dart new file mode 100644 index 000000000..797f4a27d --- /dev/null +++ b/en/codes/dart/chapter_graph/graph_bfs.dart @@ -0,0 +1,66 @@ +/** + * File: graph_bfs.dart + * Created Time: 2023-05-15 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:collection'; + +import '../utils/vertex.dart'; +import 'graph_adjacency_list.dart'; + +/* Breadth-first traversal */ +List graphBFS(GraphAdjList graph, Vertex startVet) { + // Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + // Vertex traversal sequence + List res = []; + // Hash set for recording vertices that have been visited + Set visited = {}; + visited.add(startVet); + // Queue used to implement BFS + Queue que = Queue(); + que.add(startVet); + // Starting from vertex vet, loop until all vertices are visited + while (que.isNotEmpty) { + Vertex vet = que.removeFirst(); // Dequeue the front vertex + res.add(vet); // Record visited vertex + // Traverse all adjacent vertices of this vertex + for (Vertex adjVet in graph.adjList[vet]!) { + if (visited.contains(adjVet)) { + continue; // Skip vertices that have been visited + } + que.add(adjVet); // Only enqueue unvisited vertices + visited.add(adjVet); // Mark this vertex as visited + } + } + // Return vertex traversal sequence + return res; +} + +/* Dirver Code */ +void main() { + /* Add edge */ + List v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + List> edges = [ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[1], v[4]], + [v[2], v[5]], + [v[3], v[4]], + [v[3], v[6]], + [v[4], v[5]], + [v[4], v[7]], + [v[5], v[8]], + [v[6], v[7]], + [v[7], v[8]], + ]; + GraphAdjList graph = GraphAdjList(edges); + print("\nAfter initialization, graph is"); + graph.printAdjList(); + + /* Breadth-first traversal */ + List res = graphBFS(graph, v[0]); + print("\nBreadth-first traversal (BFS) vertex sequence is"); + print(Vertex.vetsToVals(res)); +} diff --git a/en/codes/dart/chapter_graph/graph_dfs.dart b/en/codes/dart/chapter_graph/graph_dfs.dart new file mode 100644 index 000000000..3671a9c67 --- /dev/null +++ b/en/codes/dart/chapter_graph/graph_dfs.dart @@ -0,0 +1,59 @@ +/** + * File: graph_dfs.dart + * Created Time: 2023-05-15 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/vertex.dart'; +import 'graph_adjacency_list.dart'; + +/* Depth-first traversal helper function */ +void dfs( + GraphAdjList graph, + Set visited, + List res, + Vertex vet, +) { + res.add(vet); // Record visited vertex + visited.add(vet); // Mark this vertex as visited + // Traverse all adjacent vertices of this vertex + for (Vertex adjVet in graph.adjList[vet]!) { + if (visited.contains(adjVet)) { + continue; // Skip vertices that have been visited + } + // Recursively visit adjacent vertices + dfs(graph, visited, res, adjVet); + } +} + +/* Depth-first traversal */ +List graphDFS(GraphAdjList graph, Vertex startVet) { + // Vertex traversal sequence + List res = []; + // Hash set for recording vertices that have been visited + Set visited = {}; + dfs(graph, visited, res, startVet); + return res; +} + +/* Driver Code */ +void main() { + /* Add edge */ + List v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6]); + List> edges = [ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[2], v[5]], + [v[4], v[5]], + [v[5], v[6]], + ]; + GraphAdjList graph = GraphAdjList(edges); + print("\nAfter initialization, graph is"); + graph.printAdjList(); + + /* Depth-first traversal */ + List res = graphDFS(graph, v[0]); + print("\nDepth-first traversal (DFS) vertex sequence is"); + print(Vertex.vetsToVals(res)); +} diff --git a/en/codes/dart/chapter_greedy/coin_change_greedy.dart b/en/codes/dart/chapter_greedy/coin_change_greedy.dart new file mode 100644 index 000000000..3e8e34eac --- /dev/null +++ b/en/codes/dart/chapter_greedy/coin_change_greedy.dart @@ -0,0 +1,50 @@ +/** + * File: coin_change_greedy.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Coin change: Greedy algorithm */ +int coinChangeGreedy(List coins, int amt) { + // Assume coins list is sorted + int i = coins.length - 1; + int count = 0; + // Loop to make greedy choices until no remaining amount + while (amt > 0) { + // Find the coin that is less than and closest to the remaining amount + while (i > 0 && coins[i] > amt) { + i--; + } + // Choose coins[i] + amt -= coins[i]; + count++; + } + // If no feasible solution is found, return -1 + return amt == 0 ? count : -1; +} + +/* Driver Code */ +void main() { + // Greedy algorithm: Can guarantee finding the global optimal solution + List coins = [1, 5, 10, 20, 50, 100]; + int amt = 186; + int res = coinChangeGreedy(coins, amt); + print("\ncoins = $coins, amt = $amt"); + print("Minimum coins needed to make $amt is $res"); + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = [1, 20, 50]; + amt = 60; + res = coinChangeGreedy(coins, amt); + print("\ncoins = $coins, amt = $amt"); + print("Minimum coins needed to make $amt is $res"); + print("Actually the minimum number needed is 3, i.e., 20 + 20 + 20"); + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = [1, 49, 50]; + amt = 98; + res = coinChangeGreedy(coins, amt); + print("\ncoins = $coins, amt = $amt"); + print("Minimum coins needed to make $amt is $res"); + print("Actually the minimum number needed is 2, i.e., 49 + 49"); +} diff --git a/en/codes/dart/chapter_greedy/fractional_knapsack.dart b/en/codes/dart/chapter_greedy/fractional_knapsack.dart new file mode 100644 index 000000000..4fa3c92ee --- /dev/null +++ b/en/codes/dart/chapter_greedy/fractional_knapsack.dart @@ -0,0 +1,47 @@ +/** + * File: fractional_knapsack.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Item */ +class Item { + int w; // Item weight + int v; // Item value + + Item(this.w, this.v); +} + +/* Fractional knapsack: Greedy algorithm */ +double fractionalKnapsack(List wgt, List val, int cap) { + // Create item list with two attributes: weight, value + List items = List.generate(wgt.length, (i) => Item(wgt[i], val[i])); + // Sort by unit value item.v / item.w from high to low + items.sort((a, b) => (b.v / b.w).compareTo(a.v / a.w)); + // Loop for greedy selection + double res = 0; + for (Item item in items) { + if (item.w <= cap) { + // If remaining capacity is sufficient, put the entire current item into the knapsack + res += item.v; + cap -= item.w; + } else { + // If remaining capacity is insufficient, put part of the current item into the knapsack + res += item.v / item.w * cap; + // No remaining capacity, so break out of the loop + break; + } + } + return res; +} + +/* Driver Code */ +void main() { + List wgt = [10, 20, 30, 40, 50]; + List val = [50, 120, 150, 210, 240]; + int cap = 50; + + // Greedy algorithm + double res = fractionalKnapsack(wgt, val, cap); + print("Maximum item value not exceeding knapsack capacity is $res"); +} diff --git a/en/codes/dart/chapter_greedy/max_capacity.dart b/en/codes/dart/chapter_greedy/max_capacity.dart new file mode 100644 index 000000000..48cb47303 --- /dev/null +++ b/en/codes/dart/chapter_greedy/max_capacity.dart @@ -0,0 +1,37 @@ +/** + * File: max_capacity.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:math'; + +/* Max capacity: Greedy algorithm */ +int maxCapacity(List ht) { + // Initialize i, j to be at both ends of the array + int i = 0, j = ht.length - 1; + // Initial max capacity is 0 + int res = 0; + // Loop for greedy selection until the two boards meet + while (i < j) { + // Update max capacity + int cap = min(ht[i], ht[j]) * (j - i); + res = max(res, cap); + // Move the shorter board inward + if (ht[i] < ht[j]) { + i++; + } else { + j--; + } + } + return res; +} + +/* Driver Code */ +void main() { + List ht = [3, 8, 5, 2, 7, 7, 3, 4]; + + // Greedy algorithm + int res = maxCapacity(ht); + print("Maximum capacity is $res"); +} diff --git a/en/codes/dart/chapter_greedy/max_product_cutting.dart b/en/codes/dart/chapter_greedy/max_product_cutting.dart new file mode 100644 index 000000000..53d21996c --- /dev/null +++ b/en/codes/dart/chapter_greedy/max_product_cutting.dart @@ -0,0 +1,37 @@ +/** + * File: max_product_cutting.dart + * Created Time: 2023-08-11 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:math'; + +/* Max product cutting: Greedy algorithm */ +int maxProductCutting(int n) { + // When n <= 3, must cut out a 1 + if (n <= 3) { + return 1 * (n - 1); + } + // Greedily cut out 3, a is the number of 3s, b is the remainder + int a = n ~/ 3; + int b = n % 3; + if (b == 1) { + // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + return (pow(3, a - 1) * 2 * 2).toInt(); + } + if (b == 2) { + // When the remainder is 2, do nothing + return (pow(3, a) * 2).toInt(); + } + // When the remainder is 0, do nothing + return pow(3, a).toInt(); +} + +/* Driver Code */ +void main() { + int n = 58; + + // Greedy algorithm + int res = maxProductCutting(n); + print("Maximum cutting product is $res"); +} diff --git a/en/codes/dart/chapter_hashing/array_hash_map.dart b/en/codes/dart/chapter_hashing/array_hash_map.dart new file mode 100644 index 000000000..cc11982f7 --- /dev/null +++ b/en/codes/dart/chapter_hashing/array_hash_map.dart @@ -0,0 +1,126 @@ +/** + * File: array_hash_map.dart + * Created Time: 2023-03-29 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Key-value pair */ +class Pair { + int key; + String val; + Pair(this.key, this.val); +} + +/* Hash table based on array implementation */ +class ArrayHashMap { + late List _buckets; + + ArrayHashMap() { + // Initialize array with 100 buckets + _buckets = List.filled(100, null); + } + + /* Hash function */ + int _hashFunc(int key) { + final int index = key % 100; + return index; + } + + /* Query operation */ + String? get(int key) { + final int index = _hashFunc(key); + final Pair? pair = _buckets[index]; + if (pair == null) { + return null; + } + return pair.val; + } + + /* Add operation */ + void put(int key, String val) { + final Pair pair = Pair(key, val); + final int index = _hashFunc(key); + _buckets[index] = pair; + } + + /* Remove operation */ + void remove(int key) { + final int index = _hashFunc(key); + _buckets[index] = null; + } + + /* Get all key-value pairs */ + List pairSet() { + List pairSet = []; + for (final Pair? pair in _buckets) { + if (pair != null) { + pairSet.add(pair); + } + } + return pairSet; + } + + /* Get all keys */ + List keySet() { + List keySet = []; + for (final Pair? pair in _buckets) { + if (pair != null) { + keySet.add(pair.key); + } + } + return keySet; + } + + /* Get all values */ + List values() { + List valueSet = []; + for (final Pair? pair in _buckets) { + if (pair != null) { + valueSet.add(pair.val); + } + } + return valueSet; + } + + /* Print hash table */ + void printHashMap() { + for (final Pair kv in pairSet()) { + print("${kv.key} -> ${kv.val}"); + } + } +} + +/* Driver Code */ +void main() { + /* Initialize hash table */ + final ArrayHashMap map = ArrayHashMap(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.put(12836, "Xiao Ha"); + map.put(15937, "Xiao Luo"); + map.put(16750, "Xiao Suan"); + map.put(13276, "Xiao Fa"); + map.put(10583, "Xiao Ya"); + print("\nAfter adding is complete, hash table is\nKey -> Value"); + map.printHashMap(); + + /* Query operation */ + // Input key into hash table to get value + String? name = map.get(15937); + print("\nInput student ID 15937, found name $name"); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(10583); + print("\nAfter removing 10583, hash table is\nKey -> Value"); + map.printHashMap(); + + /* Traverse hash table */ + print("\nTraverse key-value pairs Key->Value"); + map.pairSet().forEach((kv) => print("${kv.key} -> ${kv.val}")); + print("\nTraverse keys only Key"); + map.keySet().forEach((key) => print("$key")); + print("\nTraverse values only Value"); + map.values().forEach((val) => print("$val")); +} diff --git a/en/codes/dart/chapter_hashing/built_in_hash.dart b/en/codes/dart/chapter_hashing/built_in_hash.dart new file mode 100644 index 000000000..7f216cead --- /dev/null +++ b/en/codes/dart/chapter_hashing/built_in_hash.dart @@ -0,0 +1,34 @@ +/** + * File: built_in_hash.dart + * Created Time: 2023-06-25 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../chapter_stack_and_queue/linkedlist_deque.dart'; + +/* Driver Code */ +void main() { + int _num = 3; + int hashNum = _num.hashCode; + print("Hash value of integer $_num is $hashNum"); + + bool bol = true; + int hashBol = bol.hashCode; + print("Hash value of boolean $bol is $hashBol"); + + double dec = 3.14159; + int hashDec = dec.hashCode; + print("Hash value of decimal $dec is $hashDec"); + + String str = "Hello Algo"; + int hashStr = str.hashCode; + print("Hash value of string $str is $hashStr"); + + List arr = [12836, "Xiao Ha"]; + int hashArr = arr.hashCode; + print("Hash value of array $arr is $hashArr"); + + ListNode obj = new ListNode(0); + int hashObj = obj.hashCode; + print("Hash value of node object $obj is $hashObj"); +} diff --git a/en/codes/dart/chapter_hashing/hash_map.dart b/en/codes/dart/chapter_hashing/hash_map.dart new file mode 100644 index 000000000..1841415c3 --- /dev/null +++ b/en/codes/dart/chapter_hashing/hash_map.dart @@ -0,0 +1,41 @@ +/** + * File: hash_map.dart + * Created Time: 2023-03-29 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Driver Code */ +void main() { + /* Initialize hash table */ + final Map map = {}; + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map[12836] = "Xiao Ha"; + map[15937] = "Xiao Luo"; + map[16750] = "Xiao Suan"; + map[13276] = "Xiao Fa"; + map[10583] = "Xiao Ya"; + print("\nAfter adding is complete, hash table is\nKey -> Value"); + map.forEach((key, value) => print("$key -> $value")); + + /* Query operation */ + // Input key into hash table to get value + final String? name = map[15937]; + print("\nInput student ID 15937, found name $name"); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(10583); + print("\nAfter removing 10583, hash table is\nKey -> Value"); + map.forEach((key, value) => print("$key -> $value")); + + /* Traverse hash table */ + print("\nTraverse key-value pairs Key->Value"); + map.forEach((key, value) => print("$key -> $value")); + print("\nTraverse keys only Key"); + map.keys.forEach((key) => print(key)); + print("\nTraverse values only Value"); + map.forEach((key, value) => print("$value")); + map.values.forEach((value) => print(value)); +} diff --git a/en/codes/dart/chapter_hashing/hash_map_chaining.dart b/en/codes/dart/chapter_hashing/hash_map_chaining.dart new file mode 100644 index 000000000..b35841d20 --- /dev/null +++ b/en/codes/dart/chapter_hashing/hash_map_chaining.dart @@ -0,0 +1,138 @@ +/** + * File: hash_map_chaining.dart + * Created Time: 2023-06-24 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'array_hash_map.dart'; + +/* Hash table with separate chaining */ +class HashMapChaining { + late int size; // Number of key-value pairs + late int capacity; // Hash table capacity + late double loadThres; // Load factor threshold for triggering expansion + late int extendRatio; // Expansion multiplier + late List> buckets; // Bucket array + + /* Constructor */ + HashMapChaining() { + size = 0; + capacity = 4; + loadThres = 2.0 / 3.0; + extendRatio = 2; + buckets = List.generate(capacity, (_) => []); + } + + /* Hash function */ + int hashFunc(int key) { + return key % capacity; + } + + /* Load factor */ + double loadFactor() { + return size / capacity; + } + + /* Query operation */ + String? get(int key) { + int index = hashFunc(key); + List bucket = buckets[index]; + // Traverse bucket, if key is found, return corresponding val + for (Pair pair in bucket) { + if (pair.key == key) { + return pair.val; + } + } + // If key is not found, return null + return null; + } + + /* Add operation */ + void put(int key, String val) { + // When load factor exceeds threshold, perform expansion + if (loadFactor() > loadThres) { + extend(); + } + int index = hashFunc(key); + List bucket = buckets[index]; + // Traverse bucket, if specified key is encountered, update corresponding val and return + for (Pair pair in bucket) { + if (pair.key == key) { + pair.val = val; + return; + } + } + // If key does not exist, append key-value pair to the end + Pair pair = Pair(key, val); + bucket.add(pair); + size++; + } + + /* Remove operation */ + void remove(int key) { + int index = hashFunc(key); + List bucket = buckets[index]; + // Traverse bucket and remove key-value pair from it + for (Pair pair in bucket) { + if (pair.key == key) { + bucket.remove(pair); + size--; + break; + } + } + } + + /* Expand hash table */ + void extend() { + // Temporarily store the original hash table + List> bucketsTmp = buckets; + // Initialize expanded new hash table + capacity *= extendRatio; + buckets = List.generate(capacity, (_) => []); + size = 0; + // Move key-value pairs from original hash table to new hash table + for (List bucket in bucketsTmp) { + for (Pair pair in bucket) { + put(pair.key, pair.val); + } + } + } + + /* Print hash table */ + void printHashMap() { + for (List bucket in buckets) { + List res = []; + for (Pair pair in bucket) { + res.add("${pair.key} -> ${pair.val}"); + } + print(res); + } + } +} + +/* Driver Code */ +void main() { + /* Initialize hash table */ + HashMapChaining map = HashMapChaining(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.put(12836, "Xiao Ha"); + map.put(15937, "Xiao Luo"); + map.put(16750, "Xiao Suan"); + map.put(13276, "Xiao Fa"); + map.put(10583, "Xiao Ya"); + print("\nAfter adding is complete, hash table is\nKey -> Value"); + map.printHashMap(); + + /* Query operation */ + // Input key into hash table to get value + String? name = map.get(13276); + print("\nInput student ID 13276, found name ${name}"); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(12836); + print("\nAfter removing 12836, hash table is\nKey -> Value"); + map.printHashMap(); +} diff --git a/en/codes/dart/chapter_hashing/hash_map_open_addressing.dart b/en/codes/dart/chapter_hashing/hash_map_open_addressing.dart new file mode 100644 index 000000000..0c52edac5 --- /dev/null +++ b/en/codes/dart/chapter_hashing/hash_map_open_addressing.dart @@ -0,0 +1,157 @@ +/** + * File: hash_map_open_addressing.dart + * Created Time: 2023-06-25 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'array_hash_map.dart'; + +/* Hash table with open addressing */ +class HashMapOpenAddressing { + late int _size; // Number of key-value pairs + int _capacity = 4; // Hash table capacity + double _loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion + int _extendRatio = 2; // Expansion multiplier + late List _buckets; // Bucket array + Pair _TOMBSTONE = Pair(-1, "-1"); // Removal marker + + /* Constructor */ + HashMapOpenAddressing() { + _size = 0; + _buckets = List.generate(_capacity, (index) => null); + } + + /* Hash function */ + int hashFunc(int key) { + return key % _capacity; + } + + /* Load factor */ + double loadFactor() { + return _size / _capacity; + } + + /* Search for bucket index corresponding to key */ + int findBucket(int key) { + int index = hashFunc(key); + int firstTombstone = -1; + // Linear probing, break when encountering an empty bucket + while (_buckets[index] != null) { + // If key is encountered, return the corresponding bucket index + if (_buckets[index]!.key == key) { + // If a removal marker was encountered before, move the key-value pair to that index + if (firstTombstone != -1) { + _buckets[firstTombstone] = _buckets[index]; + _buckets[index] = _TOMBSTONE; + return firstTombstone; // Return the moved bucket index + } + return index; // Return bucket index + } + // Record the first removal marker encountered + if (firstTombstone == -1 && _buckets[index] == _TOMBSTONE) { + firstTombstone = index; + } + // Calculate bucket index, wrap around to the head if past the tail + index = (index + 1) % _capacity; + } + // If key does not exist, return the index for insertion + return firstTombstone == -1 ? index : firstTombstone; + } + + /* Query operation */ + String? get(int key) { + // Search for bucket index corresponding to key + int index = findBucket(key); + // If key-value pair is found, return corresponding val + if (_buckets[index] != null && _buckets[index] != _TOMBSTONE) { + return _buckets[index]!.val; + } + // If key-value pair does not exist, return null + return null; + } + + /* Add operation */ + void put(int key, String val) { + // When load factor exceeds threshold, perform expansion + if (loadFactor() > _loadThres) { + extend(); + } + // Search for bucket index corresponding to key + int index = findBucket(key); + // If key-value pair is found, overwrite val and return + if (_buckets[index] != null && _buckets[index] != _TOMBSTONE) { + _buckets[index]!.val = val; + return; + } + // If key-value pair does not exist, add the key-value pair + _buckets[index] = new Pair(key, val); + _size++; + } + + /* Remove operation */ + void remove(int key) { + // Search for bucket index corresponding to key + int index = findBucket(key); + // If key-value pair is found, overwrite it with removal marker + if (_buckets[index] != null && _buckets[index] != _TOMBSTONE) { + _buckets[index] = _TOMBSTONE; + _size--; + } + } + + /* Expand hash table */ + void extend() { + // Temporarily store the original hash table + List bucketsTmp = _buckets; + // Initialize expanded new hash table + _capacity *= _extendRatio; + _buckets = List.generate(_capacity, (index) => null); + _size = 0; + // Move key-value pairs from original hash table to new hash table + for (Pair? pair in bucketsTmp) { + if (pair != null && pair != _TOMBSTONE) { + put(pair.key, pair.val); + } + } + } + + /* Print hash table */ + void printHashMap() { + for (Pair? pair in _buckets) { + if (pair == null) { + print("null"); + } else if (pair == _TOMBSTONE) { + print("TOMBSTONE"); + } else { + print("${pair.key} -> ${pair.val}"); + } + } + } +} + +/* Driver Code */ +void main() { + /* Initialize hash table */ + HashMapOpenAddressing map = HashMapOpenAddressing(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.put(12836, "Xiao Ha"); + map.put(15937, "Xiao Luo"); + map.put(16750, "Xiao Suan"); + map.put(13276, "Xiao Fa"); + map.put(10583, "Xiao Ya"); + print("\nAfter adding is complete, hash table is\nKey -> Value"); + map.printHashMap(); + + /* Query operation */ + // Input key into hash table to get value + String? name = map.get(13276); + print("\nInput student ID 13276, found name $name"); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(16750); + print("\nAfter removing 16750, hash table is\nKey -> Value"); + map.printHashMap(); +} diff --git a/en/codes/dart/chapter_hashing/simple_hash.dart b/en/codes/dart/chapter_hashing/simple_hash.dart new file mode 100644 index 000000000..a92933a40 --- /dev/null +++ b/en/codes/dart/chapter_hashing/simple_hash.dart @@ -0,0 +1,62 @@ +/** + * File: simple_hash.dart + * Created Time: 2023-06-25 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Additive hash */ +int addHash(String key) { + int hash = 0; + final int MODULUS = 1000000007; + for (int i = 0; i < key.length; i++) { + hash = (hash + key.codeUnitAt(i)) % MODULUS; + } + return hash; +} + +/* Multiplicative hash */ +int mulHash(String key) { + int hash = 0; + final int MODULUS = 1000000007; + for (int i = 0; i < key.length; i++) { + hash = (31 * hash + key.codeUnitAt(i)) % MODULUS; + } + return hash; +} + +/* XOR hash */ +int xorHash(String key) { + int hash = 0; + final int MODULUS = 1000000007; + for (int i = 0; i < key.length; i++) { + hash ^= key.codeUnitAt(i); + } + return hash & MODULUS; +} + +/* Rotational hash */ +int rotHash(String key) { + int hash = 0; + final int MODULUS = 1000000007; + for (int i = 0; i < key.length; i++) { + hash = ((hash << 4) ^ (hash >> 28) ^ key.codeUnitAt(i)) % MODULUS; + } + return hash; +} + +/* Dirver Code */ +void main() { + String key = "Hello Algo"; + + int hash = addHash(key); + print("Additive hash value is $hash"); + + hash = mulHash(key); + print("Multiplicative hash value is $hash"); + + hash = xorHash(key); + print("XOR hash value is $hash"); + + hash = rotHash(key); + print("Rotational hash value is $hash"); +} diff --git a/en/codes/dart/chapter_heap/my_heap.dart b/en/codes/dart/chapter_heap/my_heap.dart new file mode 100644 index 000000000..d986930ff --- /dev/null +++ b/en/codes/dart/chapter_heap/my_heap.dart @@ -0,0 +1,151 @@ +/** + * File: my_heap.dart + * Created Time: 2023-04-09 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; + +/* Max heap */ +class MaxHeap { + late List _maxHeap; + + /* Constructor, build heap based on input list */ + MaxHeap(List nums) { + // Add list elements to heap as is + _maxHeap = nums; + // Heapify all nodes except leaf nodes + for (int i = _parent(size() - 1); i >= 0; i--) { + siftDown(i); + } + } + + /* Get index of left child node */ + int _left(int i) { + return 2 * i + 1; + } + + /* Get index of right child node */ + int _right(int i) { + return 2 * i + 2; + } + + /* Get index of parent node */ + int _parent(int i) { + return (i - 1) ~/ 2; // Floor division + } + + /* Swap elements */ + void _swap(int i, int j) { + int tmp = _maxHeap[i]; + _maxHeap[i] = _maxHeap[j]; + _maxHeap[j] = tmp; + } + + /* Get heap size */ + int size() { + return _maxHeap.length; + } + + /* Check if heap is empty */ + bool isEmpty() { + return size() == 0; + } + + /* Access top element */ + int peek() { + return _maxHeap[0]; + } + + /* Element enters heap */ + void push(int val) { + // Add node + _maxHeap.add(val); + // Heapify from bottom to top + siftUp(size() - 1); + } + + /* Starting from node i, heapify from bottom to top */ + void siftUp(int i) { + while (true) { + // Get parent node of node i + int p = _parent(i); + // When "crossing root node" or "node needs no repair", end heapify + if (p < 0 || _maxHeap[i] <= _maxHeap[p]) { + break; + } + // Swap two nodes + _swap(i, p); + // Loop upward heapify + i = p; + } + } + + /* Element exits heap */ + int pop() { + // Handle empty case + if (isEmpty()) throw Exception('Heap is empty'); + // Delete node + _swap(0, size() - 1); + // Remove node + int val = _maxHeap.removeLast(); + // Return top element + siftDown(0); + // Return heap top element + return val; + } + + /* Starting from node i, heapify from top to bottom */ + void siftDown(int i) { + while (true) { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + int l = _left(i); + int r = _right(i); + int ma = i; + if (l < size() && _maxHeap[l] > _maxHeap[ma]) ma = l; + if (r < size() && _maxHeap[r] > _maxHeap[ma]) ma = r; + // Swap two nodes + if (ma == i) break; + // Swap two nodes + _swap(i, ma); + // Loop downwards heapification + i = ma; + } + } + + /* Driver Code */ + void print() { + printHeap(_maxHeap); + } +} + +/* Driver Code */ +void main() { + /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + MaxHeap maxHeap = MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); + print("\nAfter inputting list and building heap"); + maxHeap.print(); + + /* Check if heap is empty */ + int peek = maxHeap.peek(); + print("\nHeap top element is $peek"); + + /* Element enters heap */ + int val = 7; + maxHeap.push(val); + print("\nAfter element $val pushes to heap"); + maxHeap.print(); + + /* Time complexity is O(n), not O(nlogn) */ + peek = maxHeap.pop(); + print("\nAfter heap top element $peek pops from heap"); + maxHeap.print(); + + /* Get heap size */ + int size = maxHeap.size(); + print("\nHeap size is $size"); + + /* Check if heap is empty */ + bool isEmpty = maxHeap.isEmpty(); + print("\nIs heap empty $isEmpty"); +} diff --git a/en/codes/dart/chapter_heap/top_k.dart b/en/codes/dart/chapter_heap/top_k.dart new file mode 100644 index 000000000..159896357 --- /dev/null +++ b/en/codes/dart/chapter_heap/top_k.dart @@ -0,0 +1,150 @@ +/** + * File: top_k.dart + * Created Time: 2023-08-15 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; + +/* Find the largest k elements in array based on heap */ +MinHeap topKHeap(List nums, int k) { + // Initialize min heap, push first k elements of array to heap + MinHeap heap = MinHeap(nums.sublist(0, k)); + // Starting from the (k+1)th element, maintain heap length as k + for (int i = k; i < nums.length; i++) { + // If current element is greater than top element, top element exits heap, current element enters heap + if (nums[i] > heap.peek()) { + heap.pop(); + heap.push(nums[i]); + } + } + return heap; +} + +/* Driver Code */ +void main() { + List nums = [1, 7, 6, 3, 2]; + int k = 3; + + MinHeap res = topKHeap(nums, k); + print("The largest $k elements are"); + res.print(); +} + +/* Min heap */ +class MinHeap { + late List _minHeap; + + /* Constructor, build heap based on input list */ + MinHeap(List nums) { + // Add list elements to heap as is + _minHeap = nums; + // Heapify all nodes except leaf nodes + for (int i = _parent(size() - 1); i >= 0; i--) { + siftDown(i); + } + } + + /* Return elements in heap */ + List getHeap() { + return _minHeap; + } + + /* Get index of left child node */ + int _left(int i) { + return 2 * i + 1; + } + + /* Get index of right child node */ + int _right(int i) { + return 2 * i + 2; + } + + /* Get index of parent node */ + int _parent(int i) { + return (i - 1) ~/ 2; // Floor division + } + + /* Swap elements */ + void _swap(int i, int j) { + int tmp = _minHeap[i]; + _minHeap[i] = _minHeap[j]; + _minHeap[j] = tmp; + } + + /* Get heap size */ + int size() { + return _minHeap.length; + } + + /* Check if heap is empty */ + bool isEmpty() { + return size() == 0; + } + + /* Access top element */ + int peek() { + return _minHeap[0]; + } + + /* Element enters heap */ + void push(int val) { + // Add node + _minHeap.add(val); + // Heapify from bottom to top + siftUp(size() - 1); + } + + /* Starting from node i, heapify from bottom to top */ + void siftUp(int i) { + while (true) { + // Get parent node of node i + int p = _parent(i); + // When "crossing root node" or "node needs no repair", end heapify + if (p < 0 || _minHeap[i] >= _minHeap[p]) { + break; + } + // Swap two nodes + _swap(i, p); + // Loop upward heapify + i = p; + } + } + + /* Element exits heap */ + int pop() { + // Handle empty case + if (isEmpty()) throw Exception('Heap is empty'); + // Delete node + _swap(0, size() - 1); + // Remove node + int val = _minHeap.removeLast(); + // Return top element + siftDown(0); + // Return heap top element + return val; + } + + /* Starting from node i, heapify from top to bottom */ + void siftDown(int i) { + while (true) { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + int l = _left(i); + int r = _right(i); + int mi = i; + if (l < size() && _minHeap[l] < _minHeap[mi]) mi = l; + if (r < size() && _minHeap[r] < _minHeap[mi]) mi = r; + // Swap two nodes + if (mi == i) break; + // Swap two nodes + _swap(i, mi); + // Loop downwards heapification + i = mi; + } + } + + /* Driver Code */ + void print() { + printHeap(_minHeap); + } +} diff --git a/en/codes/dart/chapter_searching/binary_search.dart b/en/codes/dart/chapter_searching/binary_search.dart new file mode 100644 index 000000000..dabdb13fe --- /dev/null +++ b/en/codes/dart/chapter_searching/binary_search.dart @@ -0,0 +1,63 @@ +/** + * File: binary_search.dart + * Created Time: 2023-05-12 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +/* Binary search (closed interval on both sides) */ +int binarySearch(List nums, int target) { + // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + int i = 0, j = nums.length - 1; + // Loop, exit when the search interval is empty (empty when i > j) + while (i <= j) { + int m = i + (j - i) ~/ 2; // Calculate the midpoint index m + if (nums[m] < target) { + // This means target is in the interval [m+1, j] + i = m + 1; + } else if (nums[m] > target) { + // This means target is in the interval [i, m-1] + j = m - 1; + } else { + // Found the target element, return its index + return m; + } + } + // Target element not found, return -1 + return -1; +} + +/* Binary search (left-closed right-open interval) */ +int binarySearchLCRO(List nums, int target) { + // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + int i = 0, j = nums.length; + // Loop, exit when the search interval is empty (empty when i = j) + while (i < j) { + int m = i + (j - i) ~/ 2; // Calculate the midpoint index m + if (nums[m] < target) { + // This means target is in the interval [m+1, j) + i = m + 1; + } else if (nums[m] > target) { + // This means target is in the interval [i, m) + j = m; + } else { + // Found the target element, return its index + return m; + } + } + // Target element not found, return -1 + return -1; +} + +/* Driver Code*/ +void main() { + int target = 6; + final nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; + + /* Binary search (closed interval) */ + int index = binarySearch(nums, target); + print('Index of target element 6 = $index'); + + /* Binary search (left-closed right-open interval) */ + index = binarySearchLCRO(nums, target); + print('Index of target element 6 = $index'); +} diff --git a/en/codes/dart/chapter_searching/binary_search_edge.dart b/en/codes/dart/chapter_searching/binary_search_edge.dart new file mode 100644 index 000000000..d1b47acfc --- /dev/null +++ b/en/codes/dart/chapter_searching/binary_search_edge.dart @@ -0,0 +1,48 @@ +/** + * File: binary_search_edge.dart + * Created Time: 2023-08-14 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'binary_search_insertion.dart'; + +/* Binary search for the leftmost target */ +int binarySearchLeftEdge(List nums, int target) { + // Equivalent to finding the insertion point of target + int i = binarySearchInsertion(nums, target); + // Target not found, return -1 + if (i == nums.length || nums[i] != target) { + return -1; + } + // Found target, return index i + return i; +} + +/* Binary search for the rightmost target */ +int binarySearchRightEdge(List nums, int target) { + // Convert to finding the leftmost target + 1 + int i = binarySearchInsertion(nums, target + 1); + // j points to the rightmost target, i points to the first element greater than target + int j = i - 1; + // Target not found, return -1 + if (j == -1 || nums[j] != target) { + return -1; + } + // Found target, return index j + return j; +} + +/* Driver Code */ +void main() { + // Array with duplicate elements + List nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; + print("\nArray nums = $nums"); + + // Binary search left and right boundaries + for (int target in [6, 7]) { + int index = binarySearchLeftEdge(nums, target); + print("Leftmost element $target index is $index"); + index = binarySearchRightEdge(nums, target); + print("Rightmost element $target index is $index"); + } +} diff --git a/en/codes/dart/chapter_searching/binary_search_insertion.dart b/en/codes/dart/chapter_searching/binary_search_insertion.dart new file mode 100644 index 000000000..f40947a22 --- /dev/null +++ b/en/codes/dart/chapter_searching/binary_search_insertion.dart @@ -0,0 +1,60 @@ +/** + * File: binary_search_insertion.dart + * Created Time: 2023-08-14 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Binary search for insertion point (no duplicate elements) */ +int binarySearchInsertionSimple(List nums, int target) { + int i = 0, j = nums.length - 1; // Initialize closed interval [0, n-1] + while (i <= j) { + int m = i + (j - i) ~/ 2; // Calculate the midpoint index m + if (nums[m] < target) { + i = m + 1; // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // target is in the interval [i, m-1] + } else { + return m; // Found target, return insertion point m + } + } + // Target not found, return insertion point i + return i; +} + +/* Binary search for insertion point (with duplicate elements) */ +int binarySearchInsertion(List nums, int target) { + int i = 0, j = nums.length - 1; // Initialize closed interval [0, n-1] + while (i <= j) { + int m = i + (j - i) ~/ 2; // Calculate the midpoint index m + if (nums[m] < target) { + i = m + 1; // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // target is in the interval [i, m-1] + } else { + j = m - 1; // The first element less than target is in the interval [i, m-1] + } + } + // Return insertion point i + return i; +} + +/* Driver Code */ +void main() { + // Array without duplicate elements + List nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; + print("\nArray nums = $nums"); + // Binary search for insertion point + for (int target in [6, 9]) { + int index = binarySearchInsertionSimple(nums, target); + print("Insertion point index for element $target is $index"); + } + + // Array with duplicate elements + nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; + print("\nArray nums = $nums"); + // Binary search for insertion point + for (int target in [2, 6, 20]) { + int index = binarySearchInsertion(nums, target); + print("Insertion point index for element $target is $index"); + } +} diff --git a/en/codes/dart/chapter_searching/hashing_search.dart b/en/codes/dart/chapter_searching/hashing_search.dart new file mode 100644 index 000000000..0f1af23e3 --- /dev/null +++ b/en/codes/dart/chapter_searching/hashing_search.dart @@ -0,0 +1,54 @@ +/** + * File: hashing_search.dart + * Created Time: 2023-05-12 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +import 'dart:collection'; +import '../utils/list_node.dart'; + +/* Hash search (array) */ +int hashingSearchArray(Map map, int target) { + // Hash table's key: target element, value: index + // If this key does not exist in the hash table, return -1 + if (!map.containsKey(target)) { + return -1; + } + return map[target]!; +} + +/* Hash search (linked list) */ +ListNode? hashingSearchLinkedList(Map map, int target) { + // Hash table key: target node value, value: node object + // If key is not in hash table, return null + if (!map.containsKey(target)) { + return null; + } + return map[target]!; +} + +/* Driver Code */ +void main() { + int target = 3; + + /* Hash search (array) */ + List nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; + // Initialize hash table + Map map = HashMap(); + for (int i = 0; i < nums.length; i++) { + map.putIfAbsent(nums[i], () => i); // key: element, value: index + } + int index = hashingSearchArray(map, target); + print('Index of target element 3 = $index'); + + /* Hash search (linked list) */ + ListNode? head = listToLinkedList(nums); + // Initialize hash table + Map map1 = HashMap(); + while (head != null) { + map1.putIfAbsent(head.val, () => head!); // key: node value, value: node + head = head.next; + } + ListNode? node = hashingSearchLinkedList(map1, target); + print('Node object corresponding to target node value 3 is $node'); +} diff --git a/en/codes/dart/chapter_searching/linear_search.dart b/en/codes/dart/chapter_searching/linear_search.dart new file mode 100644 index 000000000..22cf5c808 --- /dev/null +++ b/en/codes/dart/chapter_searching/linear_search.dart @@ -0,0 +1,47 @@ +/** + * File: linear_search.dart + * Created Time: 2023-05-12 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +import '../utils/list_node.dart'; + +/* Linear search (array) */ +int linearSearchArray(List nums, int target) { + // Traverse array + for (int i = 0; i < nums.length; i++) { + // Found the target element, return its index + if (nums[i] == target) { + return i; + } + } + // Target element not found, return -1 + return -1; +} + +/* Linear search (linked list) */ +ListNode? linearSearchList(ListNode? head, int target) { + // Traverse the linked list + while (head != null) { + // Found the target node, return it + if (head.val == target) return head; + head = head.next; + } + // Target element not found, return null + return null; +} + +/* Driver Code */ +void main() { + int target = 3; + + /* Perform linear search in array */ + List nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; + int index = linearSearchArray(nums, target); + print('Index of target element 3 = $index'); + + /* Perform linear search in linked list */ + ListNode? head = listToLinkedList(nums); + ListNode? node = linearSearchList(head, target); + print('Node object corresponding to target node value 3 is $node'); +} diff --git a/en/codes/dart/chapter_searching/two_sum.dart b/en/codes/dart/chapter_searching/two_sum.dart new file mode 100644 index 000000000..88e102dd1 --- /dev/null +++ b/en/codes/dart/chapter_searching/two_sum.dart @@ -0,0 +1,49 @@ +/** + * File: two_sum.dart + * Created Time: 2023-2-11 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +import 'dart:collection'; + +/* Method 1: Brute force enumeration */ +List twoSumBruteForce(List nums, int target) { + int size = nums.length; + // Two nested loops, time complexity is O(n^2) + for (var i = 0; i < size - 1; i++) { + for (var j = i + 1; j < size; j++) { + if (nums[i] + nums[j] == target) return [i, j]; + } + } + return [0]; +} + +/* Method 2: Auxiliary hash table */ +List twoSumHashTable(List nums, int target) { + int size = nums.length; + // Auxiliary hash table, space complexity is O(n) + Map dic = HashMap(); + // Single loop, time complexity is O(n) + for (var i = 0; i < size; i++) { + if (dic.containsKey(target - nums[i])) { + return [dic[target - nums[i]]!, i]; + } + dic.putIfAbsent(nums[i], () => i); + } + return [0]; +} + +/* Driver Code */ +void main() { + // ======= Test Case ======= + List nums = [2, 7, 11, 15]; + int target = 13; + + // ====== Driver Code ====== + // Method 1 + List res = twoSumBruteForce(nums, target); + print('Method 1 res = $res'); + // Method 2 + res = twoSumHashTable(nums, target); + print('Method 2 res = $res'); +} diff --git a/en/codes/dart/chapter_sorting/bubble_sort.dart b/en/codes/dart/chapter_sorting/bubble_sort.dart new file mode 100644 index 000000000..31534a36e --- /dev/null +++ b/en/codes/dart/chapter_sorting/bubble_sort.dart @@ -0,0 +1,51 @@ +/** + * File: bubble_sort.dart + * Created Time: 2023-02-14 + * Author: what-is-me (whatisme@outlook.jp) + */ + +/* Bubble sort */ +void bubbleSort(List nums) { + // Outer loop: unsorted range is [0, i] + for (int i = nums.length - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + } + } + } +} + +/* Bubble sort (flag optimization) */ +void bubbleSortWithFlag(List nums) { + // Outer loop: unsorted range is [0, i] + for (int i = nums.length - 1; i > 0; i--) { + bool flag = false; // Initialize flag + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + flag = true; // Record element swap + } + } + if (!flag) break; // No elements were swapped in this round of "bubbling", exit directly + } +} + +/* Driver Code */ +void main() { + List nums = [4, 1, 3, 1, 5, 2]; + bubbleSort(nums); + print("After bubble sort, nums = $nums"); + + List nums1 = [4, 1, 3, 1, 5, 2]; + bubbleSortWithFlag(nums1); + print("After bubble sort, nums1 = $nums1"); +} diff --git a/en/codes/dart/chapter_sorting/bucket_sort.dart b/en/codes/dart/chapter_sorting/bucket_sort.dart new file mode 100644 index 000000000..3e7b4d991 --- /dev/null +++ b/en/codes/dart/chapter_sorting/bucket_sort.dart @@ -0,0 +1,39 @@ +/** + * File: bucket_sort.dart + * Created Time: 2023-05-12 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +/* Bucket sort */ +void bucketSort(List nums) { + // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + int k = nums.length ~/ 2; + List> buckets = List.generate(k, (index) => []); + + // 1. Distribute array elements into various buckets + for (double _num in nums) { + // Input data range is [0, 1), use _num * k to map to index range [0, k-1] + int i = (_num * k).toInt(); + // Add _num to bucket bucket_idx + buckets[i].add(_num); + } + // 2. Sort each bucket + for (List bucket in buckets) { + bucket.sort(); + } + // 3. Traverse buckets to merge results + int i = 0; + for (List bucket in buckets) { + for (double _num in bucket) { + nums[i++] = _num; + } + } +} + +/* Driver Code*/ +void main() { + // Assume input data is floating point, interval [0, 1) + final nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37]; + bucketSort(nums); + print('After bucket sort, nums = $nums'); +} diff --git a/en/codes/dart/chapter_sorting/counting_sort.dart b/en/codes/dart/chapter_sorting/counting_sort.dart new file mode 100644 index 000000000..0e8923247 --- /dev/null +++ b/en/codes/dart/chapter_sorting/counting_sort.dart @@ -0,0 +1,72 @@ +/** + * File: counting_sort.dart + * Created Time: 2023-05-12 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ +import 'dart:math'; + +/* Counting sort */ +// Simple implementation, cannot be used for sorting objects +void countingSortNaive(List nums) { + // 1. Count the maximum element m in the array + int m = 0; + for (int _num in nums) { + m = max(m, _num); + } + // 2. Count the occurrence of each number + // counter[_num] represents occurrence count of _num + List counter = List.filled(m + 1, 0); + for (int _num in nums) { + counter[_num]++; + } + // 3. Traverse counter, filling each element back into the original array nums + int i = 0; + for (int _num = 0; _num < m + 1; _num++) { + for (int j = 0; j < counter[_num]; j++, i++) { + nums[i] = _num; + } + } +} + +/* Counting sort */ +// Complete implementation, can sort objects and is a stable sort +void countingSort(List nums) { + // 1. Count the maximum element m in the array + int m = 0; + for (int _num in nums) { + m = max(m, _num); + } + // 2. Count the occurrence of each number + // counter[_num] represents occurrence count of _num + List counter = List.filled(m + 1, 0); + for (int _num in nums) { + counter[_num]++; + } + // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + // That is, counter[_num]-1 is the last occurrence index of _num in res + for (int i = 0; i < m; i++) { + counter[i + 1] += counter[i]; + } + // 4. Traverse nums in reverse order, placing each element into the result array res + // Initialize the array res to record results + int n = nums.length; + List res = List.filled(n, 0); + for (int i = n - 1; i >= 0; i--) { + int _num = nums[i]; + res[counter[_num] - 1] = _num; // Place _num at corresponding index + counter[_num]--; // Decrement prefix sum by 1 to get next placement index for _num + } + // Use result array res to overwrite the original array nums + nums.setAll(0, res); +} + +/* Driver Code*/ +void main() { + final nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; + countingSortNaive(nums); + print('After counting sort (cannot sort objects), nums = $nums'); + + final nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; + countingSort(nums1); + print('After counting sort, nums1 = $nums1'); +} diff --git a/en/codes/dart/chapter_sorting/heap_sort.dart b/en/codes/dart/chapter_sorting/heap_sort.dart new file mode 100644 index 000000000..62eb5da8d --- /dev/null +++ b/en/codes/dart/chapter_sorting/heap_sort.dart @@ -0,0 +1,49 @@ +/** + * File: heap_sort.dart + * Created Time: 2023-06-01 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Heap length is n, start heapifying node i, from top to bottom */ +void siftDown(List nums, int n, int i) { + while (true) { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + int l = 2 * i + 1; + int r = 2 * i + 2; + int ma = i; + if (l < n && nums[l] > nums[ma]) ma = l; + if (r < n && nums[r] > nums[ma]) ma = r; + // Swap two nodes + if (ma == i) break; + // Swap two nodes + int temp = nums[i]; + nums[i] = nums[ma]; + nums[ma] = temp; + // Loop downwards heapification + i = ma; + } +} + +/* Heap sort */ +void heapSort(List nums) { + // Build heap operation: heapify all nodes except leaves + for (int i = nums.length ~/ 2 - 1; i >= 0; i--) { + siftDown(nums, nums.length, i); + } + // Extract the largest element from the heap and repeat for n-1 rounds + for (int i = nums.length - 1; i > 0; i--) { + // Delete node + int tmp = nums[0]; + nums[0] = nums[i]; + nums[i] = tmp; + // Start heapifying the root node, from top to bottom + siftDown(nums, i, 0); + } +} + +/* Driver Code */ +void main() { + List nums = [4, 1, 3, 1, 5, 2]; + heapSort(nums); + print("After heap sort, nums = $nums"); +} diff --git a/en/codes/dart/chapter_sorting/insertion_sort.dart b/en/codes/dart/chapter_sorting/insertion_sort.dart new file mode 100644 index 000000000..8fcccb994 --- /dev/null +++ b/en/codes/dart/chapter_sorting/insertion_sort.dart @@ -0,0 +1,26 @@ +/** + * File: insertion_sort.dart + * Created Time: 2023-02-14 + * Author: what-is-me (whatisme@outlook.jp) + */ + +/* Insertion sort */ +void insertionSort(List nums) { + // Outer loop: sorted interval is [0, i-1] + for (int i = 1; i < nums.length; i++) { + int base = nums[i], j = i - 1; + // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // Move nums[j] to the right by one position + j--; + } + nums[j + 1] = base; // Assign base to the correct position + } +} + +/* Driver Code */ +void main() { + List nums = [4, 1, 3, 1, 5, 2]; + insertionSort(nums); + print("After insertion sort, nums = $nums"); +} diff --git a/en/codes/dart/chapter_sorting/merge_sort.dart b/en/codes/dart/chapter_sorting/merge_sort.dart new file mode 100644 index 000000000..31a72533b --- /dev/null +++ b/en/codes/dart/chapter_sorting/merge_sort.dart @@ -0,0 +1,52 @@ +/** + * File: merge_sort.dart + * Created Time: 2023-02-14 + * Author: what-is-me (whatisme@outlook.jp) + */ + +/* Merge left subarray and right subarray */ +void merge(List nums, int left, int mid, int right) { + // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + // Create a temporary array tmp to store the merged results + List tmp = List.filled(right - left + 1, 0); + // Initialize the start indices of the left and right subarrays + int i = left, j = mid + 1, k = 0; + // While both subarrays still have elements, compare and copy the smaller element into the temporary array + while (i <= mid && j <= right) { + if (nums[i] <= nums[j]) + tmp[k++] = nums[i++]; + else + tmp[k++] = nums[j++]; + } + // Copy the remaining elements of the left and right subarrays into the temporary array + while (i <= mid) { + tmp[k++] = nums[i++]; + } + while (j <= right) { + tmp[k++] = nums[j++]; + } + // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + for (k = 0; k < tmp.length; k++) { + nums[left + k] = tmp[k]; + } +} + +/* Merge sort */ +void mergeSort(List nums, int left, int right) { + // Termination condition + if (left >= right) return; // Terminate recursion when subarray length is 1 + // Divide and conquer stage + int mid = left + (right - left) ~/ 2; // Calculate midpoint + mergeSort(nums, left, mid); // Recursively process the left subarray + mergeSort(nums, mid + 1, right); // Recursively process the right subarray + // Merge stage + merge(nums, left, mid, right); +} + +/* Driver Code */ +void main() { + /* Merge sort */ + List nums = [7, 3, 2, 6, 0, 1, 5, 4]; + mergeSort(nums, 0, nums.length - 1); + print("After merge sort, nums = $nums"); +} diff --git a/en/codes/dart/chapter_sorting/quick_sort.dart b/en/codes/dart/chapter_sorting/quick_sort.dart new file mode 100644 index 000000000..42bbabab8 --- /dev/null +++ b/en/codes/dart/chapter_sorting/quick_sort.dart @@ -0,0 +1,145 @@ +/** + * File: quick_sort.dart + * Created Time: 2023-02-14 + * Author: what-is-me (whatisme@outlook.jp) + */ + +/* Quick sort class */ +class QuickSort { + /* Swap elements */ + static void _swap(List nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Sentinel partition */ + static int _partition(List nums, int left, int right) { + // Use nums[left] as the pivot + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) j--; // Search from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) i++; // Search from left to right for the first element greater than the pivot + _swap(nums, i, j); // Swap these two elements + } + _swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort */ + static void quickSort(List nums, int left, int right) { + // Terminate recursion when subarray length is 1 + if (left >= right) return; + // Sentinel partition + int pivot = _partition(nums, left, right); + // Recursively process the left subarray and right subarray + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } +} + +/* Quick sort class (median pivot optimization) */ +class QuickSortMedian { + /* Swap elements */ + static void _swap(List nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Select the median of three candidate elements */ + static int _medianThree(List nums, int left, int mid, int right) { + int l = nums[left], m = nums[mid], r = nums[right]; + if ((l <= m && m <= r) || (r <= m && m <= l)) + return mid; // m is between l and r + if ((m <= l && l <= r) || (r <= l && l <= m)) + return left; // l is between m and r + return right; + } + + /* Sentinel partition (median of three) */ + static int _partition(List nums, int left, int right) { + // Select the median of three candidate elements + int med = _medianThree(nums, left, (left + right) ~/ 2, right); + // Swap the median to the array's leftmost position + _swap(nums, left, med); + // Use nums[left] as the pivot + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) j--; // Search from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) i++; // Search from left to right for the first element greater than the pivot + _swap(nums, i, j); // Swap these two elements + } + _swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort */ + static void quickSort(List nums, int left, int right) { + // Terminate recursion when subarray length is 1 + if (left >= right) return; + // Sentinel partition + int pivot = _partition(nums, left, right); + // Recursively process the left subarray and right subarray + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } +} + +/* Quick sort class (recursion depth optimization) */ +class QuickSortTailCall { + /* Swap elements */ + static void _swap(List nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Sentinel partition */ + static int _partition(List nums, int left, int right) { + // Use nums[left] as the pivot + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) j--; // Search from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) i++; // Search from left to right for the first element greater than the pivot + _swap(nums, i, j); // Swap these two elements + } + _swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort (recursion depth optimization) */ + static void quickSort(List nums, int left, int right) { + // Terminate when subarray length is 1 + while (left < right) { + // Sentinel partition operation + int pivot = _partition(nums, left, right); + // Perform quick sort on the shorter of the two subarrays + if (pivot - left < right - pivot) { + quickSort(nums, left, pivot - 1); // Recursively sort the left subarray + left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + } else { + quickSort(nums, pivot + 1, right); // Recursively sort the right subarray + right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + } + } + } +} + +/* Driver Code */ +void main() { + /* Quick sort */ + List nums = [2, 4, 1, 0, 3, 5]; + QuickSort.quickSort(nums, 0, nums.length - 1); + print("After quick sort, nums = $nums"); + + /* Quick sort (recursion depth optimization) */ + List nums1 = [2, 4, 1, 0, 3, 5]; + QuickSortMedian.quickSort(nums1, 0, nums1.length - 1); + print("After quick sort (median pivot optimization), nums1 = $nums1"); + + /* Quick sort (recursion depth optimization) */ + List nums2 = [2, 4, 1, 0, 3, 5]; + QuickSortTailCall.quickSort(nums2, 0, nums2.length - 1); + print("After quick sort (recursion depth optimization), nums2 = $nums2"); +} diff --git a/en/codes/dart/chapter_sorting/radix_sort.dart b/en/codes/dart/chapter_sorting/radix_sort.dart new file mode 100644 index 000000000..9274ab1df --- /dev/null +++ b/en/codes/dart/chapter_sorting/radix_sort.dart @@ -0,0 +1,71 @@ +/** + * File: radix_sort.dart + * Created Time: 2023-02-14 + * Author: what-is-me (whatisme@outlook.jp) + */ + +/* Get k-th digit of element _num, where exp = 10^(k-1) */ +int digit(int _num, int exp) { + // Passing exp instead of k can avoid repeated expensive exponentiation here + return (_num ~/ exp) % 10; +} + +/* Counting sort (based on nums k-th digit) */ +void countingSortDigit(List nums, int exp) { + // Decimal digit range is 0~9, therefore need a bucket array of length 10 + List counter = List.filled(10, 0); + int n = nums.length; + // Count the occurrence of digits 0~9 + for (int i = 0; i < n; i++) { + int d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d + counter[d]++; // Count the occurrence of digit d + } + // Calculate prefix sum, converting "occurrence count" into "array index" + for (int i = 1; i < 10; i++) { + counter[i] += counter[i - 1]; + } + // Traverse in reverse, based on bucket statistics, place each element into res + List res = List.filled(n, 0); + for (int i = n - 1; i >= 0; i--) { + int d = digit(nums[i], exp); + int j = counter[d] - 1; // Get the index j for d in the array + res[j] = nums[i]; // Place the current element at index j + counter[d]--; // Decrease the count of d by 1 + } + // Use result to overwrite the original array nums + for (int i = 0; i < n; i++) nums[i] = res[i]; +} + +/* Radix sort */ +void radixSort(List nums) { + // Get the maximum element of the array, used to determine the maximum number of digits + // In Dart, int length is 64 bits + int m = -1 << 63; + for (int _num in nums) if (_num > m) m = _num; + // Traverse from the lowest to the highest digit + for (int exp = 1; exp <= m; exp *= 10) + // Perform counting sort on the k-th digit of array elements + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // i.e., exp = 10^(k-1) + countingSortDigit(nums, exp); +} + +/* Driver Code */ +void main() { + // Radix sort + List nums = [ + 10546151, + 35663510, + 42865989, + 34862445, + 81883077, + 88906420, + 72429244, + 30524779, + 82060337, + 63832996 + ]; + radixSort(nums); + print("After radix sort, nums = $nums"); +} diff --git a/en/codes/dart/chapter_sorting/selection_sort.dart b/en/codes/dart/chapter_sorting/selection_sort.dart new file mode 100644 index 000000000..5a15c9a35 --- /dev/null +++ b/en/codes/dart/chapter_sorting/selection_sort.dart @@ -0,0 +1,29 @@ +/** + * File: selection_sort.dart + * Created Time: 2023-06-01 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Selection sort */ +void selectionSort(List nums) { + int n = nums.length; + // Outer loop: unsorted interval is [i, n-1] + for (int i = 0; i < n - 1; i++) { + // Inner loop: find the smallest element within the unsorted interval + int k = i; + for (int j = i + 1; j < n; j++) { + if (nums[j] < nums[k]) k = j; // Record the index of the smallest element + } + // Swap the smallest element with the first element of the unsorted interval + int temp = nums[i]; + nums[i] = nums[k]; + nums[k] = temp; + } +} + +/* Driver Code */ +void main() { + List nums = [4, 1, 3, 1, 5, 2]; + selectionSort(nums); + print("After selection sort, nums = $nums"); +} diff --git a/en/codes/dart/chapter_stack_and_queue/array_deque.dart b/en/codes/dart/chapter_stack_and_queue/array_deque.dart new file mode 100644 index 000000000..2a41e6ac8 --- /dev/null +++ b/en/codes/dart/chapter_stack_and_queue/array_deque.dart @@ -0,0 +1,146 @@ +/** + * File: array_deque.dart + * Created Time: 2023-03-28 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Double-ended queue based on circular array implementation */ +class ArrayDeque { + late List _nums; // Array for storing double-ended queue elements + late int _front; // Front pointer, points to the front of the queue element + late int _queSize; // Double-ended queue length + + /* Constructor */ + ArrayDeque(int capacity) { + this._nums = List.filled(capacity, 0); + this._front = this._queSize = 0; + } + + /* Get the capacity of the double-ended queue */ + int capacity() { + return _nums.length; + } + + /* Get the length of the double-ended queue */ + int size() { + return _queSize; + } + + /* Check if the double-ended queue is empty */ + bool isEmpty() { + return _queSize == 0; + } + + /* Calculate circular array index */ + int index(int i) { + // Use modulo operation to wrap the array head and tail together + // When i passes the tail of the array, return to the head + // When i passes the head of the array, return to the tail + return (i + capacity()) % capacity(); + } + + /* Front of the queue enqueue */ + void pushFirst(int _num) { + if (_queSize == capacity()) { + throw Exception("Double-ended queue is full"); + } + // Use modulo operation to wrap front around to the tail after passing the head of the array + // Use modulo operation to wrap _front from array head back to tail + _front = index(_front - 1); + // Add _num to queue front + _nums[_front] = _num; + _queSize++; + } + + /* Rear of the queue enqueue */ + void pushLast(int _num) { + if (_queSize == capacity()) { + throw Exception("Double-ended queue is full"); + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + int rear = index(_front + _queSize); + // Add _num to queue rear + _nums[rear] = _num; + _queSize++; + } + + /* Rear of the queue dequeue */ + int popFirst() { + int _num = peekFirst(); + // Move front pointer right by one + _front = index(_front + 1); + _queSize--; + return _num; + } + + /* Access rear of the queue element */ + int popLast() { + int _num = peekLast(); + _queSize--; + return _num; + } + + /* Return list for printing */ + int peekFirst() { + if (isEmpty()) { + throw Exception("Deque is empty"); + } + return _nums[_front]; + } + + /* Driver Code */ + int peekLast() { + if (isEmpty()) { + throw Exception("Deque is empty"); + } + // Initialize double-ended queue + int last = index(_front + _queSize - 1); + return _nums[last]; + } + + /* Return array for printing */ + List toArray() { + // Elements enqueue + List res = List.filled(_queSize, 0); + for (int i = 0, j = _front; i < _queSize; i++, j++) { + res[i] = _nums[index(j)]; + } + return res; + } +} + +/* Driver Code */ +void main() { + /* Get the length of the double-ended queue */ + final ArrayDeque deque = ArrayDeque(10); + deque.pushLast(3); + deque.pushLast(2); + deque.pushLast(5); + print("Deque deque = ${deque.toArray()}"); + + /* Update element */ + final int peekFirst = deque.peekFirst(); + print("Front element peekFirst = $peekFirst"); + final int peekLast = deque.peekLast(); + print("Rear element peekLast = $peekLast"); + + /* Elements enqueue */ + deque.pushLast(4); + print("After element 4 enqueues at rear, deque = ${deque.toArray()}"); + deque.pushFirst(1); + print("After element 1 enqueues at front, deque = ${deque.toArray()}"); + + /* Element dequeue */ + final int popLast = deque.popLast(); + print("Dequeue rear element = $popLast, after rear dequeue deque = ${deque.toArray()}"); + final int popFirst = deque.popFirst(); + print("Dequeue front element = $popFirst, after front dequeue deque = ${deque.toArray()}"); + + /* Get the length of the double-ended queue */ + final int size = deque.size(); + print("Deque length size = $size"); + + /* Check if the double-ended queue is empty */ + final bool isEmpty = deque.isEmpty(); + print("Is deque empty = $isEmpty"); +} diff --git a/en/codes/dart/chapter_stack_and_queue/array_queue.dart b/en/codes/dart/chapter_stack_and_queue/array_queue.dart new file mode 100644 index 000000000..45e3553f6 --- /dev/null +++ b/en/codes/dart/chapter_stack_and_queue/array_queue.dart @@ -0,0 +1,110 @@ +/** + * File: array_queue.dart + * Created Time: 2023-03-28 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Queue based on circular array implementation */ +class ArrayQueue { + late List _nums; // Array for storing queue elements + late int _front; // Front pointer, points to the front of the queue element + late int _queSize; // Queue length + + ArrayQueue(int capacity) { + _nums = List.filled(capacity, 0); + _front = _queSize = 0; + } + + /* Get the capacity of the queue */ + int capaCity() { + return _nums.length; + } + + /* Get the length of the queue */ + int size() { + return _queSize; + } + + /* Check if the queue is empty */ + bool isEmpty() { + return _queSize == 0; + } + + /* Enqueue */ + void push(int _num) { + if (_queSize == capaCity()) { + throw Exception("Queue is full"); + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Add num to the rear of the queue + int rear = (_front + _queSize) % capaCity(); + // Add _num to queue rear + _nums[rear] = _num; + _queSize++; + } + + /* Dequeue */ + int pop() { + int _num = peek(); + // Move front pointer backward by one position, if it passes the tail, return to array head + _front = (_front + 1) % capaCity(); + _queSize--; + return _num; + } + + /* Return list for printing */ + int peek() { + if (isEmpty()) { + throw Exception("Queue is empty"); + } + return _nums[_front]; + } + + /* Return Array */ + List toArray() { + // Elements enqueue + final List res = List.filled(_queSize, 0); + for (int i = 0, j = _front; i < _queSize; i++, j++) { + res[i] = _nums[j % capaCity()]; + } + return res; + } +} + +/* Driver Code */ +void main() { + /* Access front of the queue element */ + final int capacity = 10; + final ArrayQueue queue = ArrayQueue(capacity); + + /* Elements enqueue */ + queue.push(1); + queue.push(3); + queue.push(2); + queue.push(5); + queue.push(4); + print("Queue queue = ${queue.toArray()}"); + + /* Return list for printing */ + final int peek = queue.peek(); + print("Front element peek = $peek"); + + /* Element dequeue */ + final int pop = queue.pop(); + print("Dequeue element pop = $pop, after dequeue queue = ${queue.toArray()}"); + + /* Get queue length */ + final int size = queue.size(); + print("Queue length size = $size"); + + /* Check if the queue is empty */ + final bool isEmpty = queue.isEmpty(); + print("Is queue empty = $isEmpty"); + + /* Test circular array */ + for (int i = 0; i < 10; i++) { + queue.push(i); + queue.pop(); + print("After round $i enqueue + dequeue, queue = ${queue.toArray()}"); + } +} diff --git a/en/codes/dart/chapter_stack_and_queue/array_stack.dart b/en/codes/dart/chapter_stack_and_queue/array_stack.dart new file mode 100644 index 000000000..6b5922417 --- /dev/null +++ b/en/codes/dart/chapter_stack_and_queue/array_stack.dart @@ -0,0 +1,77 @@ +/** + * File: array_stack.dart + * Created Time: 2023-03-28 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Stack based on array implementation */ +class ArrayStack { + late List _stack; + ArrayStack() { + _stack = []; + } + + /* Get the length of the stack */ + int size() { + return _stack.length; + } + + /* Check if the stack is empty */ + bool isEmpty() { + return _stack.isEmpty; + } + + /* Push */ + void push(int _num) { + _stack.add(_num); + } + + /* Pop */ + int pop() { + if (isEmpty()) { + throw Exception("Stack is empty"); + } + return _stack.removeLast(); + } + + /* Return list for printing */ + int peek() { + if (isEmpty()) { + throw Exception("Stack is empty"); + } + return _stack.last; + } + + /* Convert stack to Array and return */ + List toArray() => _stack; +} + +/* Driver Code */ +void main() { + /* Access top of the stack element */ + final ArrayStack stack = ArrayStack(); + + /* Elements push onto stack */ + stack.push(1); + stack.push(3); + stack.push(2); + stack.push(5); + stack.push(4); + print("Stack stack = ${stack.toArray()}"); + + /* Return list for printing */ + final int peek = stack.peek(); + print("Top element peek = $peek"); + + /* Element pop from stack */ + final int pop = stack.pop(); + print("Pop element pop = $pop, after pop stack = ${stack.toArray()}"); + + /* Get the length of the stack */ + final int size = stack.size(); + print("Stack length size = $size"); + + /* Check if empty */ + final bool isEmpty = stack.isEmpty(); + print("Is stack empty = $isEmpty"); +} diff --git a/en/codes/dart/chapter_stack_and_queue/deque.dart b/en/codes/dart/chapter_stack_and_queue/deque.dart new file mode 100644 index 000000000..c38e3c96e --- /dev/null +++ b/en/codes/dart/chapter_stack_and_queue/deque.dart @@ -0,0 +1,42 @@ +/** + * File: deque.dart + * Created Time: 2023-03-28 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:collection'; + +void main() { + /* Get the length of the double-ended queue */ + final Queue deque = Queue(); + deque.addFirst(3); + deque.addLast(2); + deque.addLast(5); + print("Deque deque = $deque"); + + /* Update element */ + final int peekFirst = deque.first; + print("Front element peekFirst = $peekFirst"); + final int peekLast = deque.last; + print("Rear element peekLast = $peekLast"); + + /* Elements enqueue */ + deque.addLast(4); + print("After element 4 enqueues at rear, deque = $deque"); + deque.addFirst(1); + print("After element 1 enqueues at front, deque = $deque"); + + /* Element dequeue */ + final int popLast = deque.removeLast(); + print("Dequeue rear element = $popLast, after rear dequeue deque = $deque"); + final int popFirst = deque.removeFirst(); + print("Dequeue front element = $popFirst, after front dequeue deque = $deque"); + + /* Get the length of the double-ended queue */ + final int size = deque.length; + print("Deque length size = $size"); + + /* Check if the double-ended queue is empty */ + final bool isEmpty = deque.isEmpty; + print("Is deque empty = $isEmpty"); +} diff --git a/en/codes/dart/chapter_stack_and_queue/linkedlist_deque.dart b/en/codes/dart/chapter_stack_and_queue/linkedlist_deque.dart new file mode 100644 index 000000000..ce5c0ed8c --- /dev/null +++ b/en/codes/dart/chapter_stack_and_queue/linkedlist_deque.dart @@ -0,0 +1,167 @@ +/** + * File: linkedlist_deque.dart + * Created Time: 2023-03-28 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Doubly linked list node */ +class ListNode { + int val; // Node value + ListNode? next; // Successor node reference + ListNode? prev; // Predecessor node reference + + ListNode(this.val, {this.next, this.prev}); +} + +/* Deque implemented based on doubly linked list */ +class LinkedListDeque { + late ListNode? _front; // Head node _front + late ListNode? _rear; // Tail node _rear + int _queSize = 0; // Length of the double-ended queue + + LinkedListDeque() { + this._front = null; + this._rear = null; + } + + /* Get deque length */ + int size() { + return this._queSize; + } + + /* Check if the double-ended queue is empty */ + bool isEmpty() { + return size() == 0; + } + + /* Enqueue operation */ + void push(int _num, bool isFront) { + final ListNode node = ListNode(_num); + if (isEmpty()) { + // If list is empty, let both _front and _rear point to node + _front = _rear = node; + } else if (isFront) { + // Front of the queue enqueue operation + // Add node to the head of the linked list + _front!.prev = node; + node.next = _front; + _front = node; // Update head node + } else { + // Rear of the queue enqueue operation + // Add node to the tail of the linked list + _rear!.next = node; + node.prev = _rear; + _rear = node; // Update tail node + } + _queSize++; // Update queue length + } + + /* Front of the queue enqueue */ + void pushFirst(int _num) { + push(_num, true); + } + + /* Rear of the queue enqueue */ + void pushLast(int _num) { + push(_num, false); + } + + /* Dequeue operation */ + int? pop(bool isFront) { + // If queue is empty, return null directly + if (isEmpty()) { + return null; + } + final int val; + if (isFront) { + // Temporarily store head node value + val = _front!.val; // Delete head node + // Delete head node + ListNode? fNext = _front!.next; + if (fNext != null) { + fNext.prev = null; + _front!.next = null; + } + _front = fNext; // Update head node + } else { + // Temporarily store tail node value + val = _rear!.val; // Delete tail node + // Update tail node + ListNode? rPrev = _rear!.prev; + if (rPrev != null) { + rPrev.next = null; + _rear!.prev = null; + } + _rear = rPrev; // Update tail node + } + _queSize--; // Update queue length + return val; + } + + /* Rear of the queue dequeue */ + int? popFirst() { + return pop(true); + } + + /* Access rear of the queue element */ + int? popLast() { + return pop(false); + } + + /* Return list for printing */ + int? peekFirst() { + return _front?.val; + } + + /* Driver Code */ + int? peekLast() { + return _rear?.val; + } + + /* Return array for printing */ + List toArray() { + ListNode? node = _front; + final List res = []; + for (int i = 0; i < _queSize; i++) { + res.add(node!.val); + node = node.next; + } + return res; + } +} + +/* Driver Code */ +void main() { + /* Get the length of the double-ended queue */ + final LinkedListDeque deque = LinkedListDeque(); + deque.pushLast(3); + deque.pushLast(2); + deque.pushLast(5); + print("Deque deque = ${deque.toArray()}"); + + /* Update element */ + int? peekFirst = deque.peekFirst(); + print("Front element peekFirst = $peekFirst"); + int? peekLast = deque.peekLast(); + print("Rear element peekLast = $peekLast"); + + /* Elements enqueue */ + deque.pushLast(4); + print("After element 4 enqueues at rear, deque = ${deque.toArray()}"); + deque.pushFirst(1); + print("After element 1 enqueues at front, deque = ${deque.toArray()}"); + + /* Element dequeue */ + int? popLast = deque.popLast(); + print("Dequeue rear element = $popLast, after rear dequeue deque = ${deque.toArray()}"); + int? popFirst = deque.popFirst(); + print("Dequeue front element = $popFirst, after front dequeue deque = ${deque.toArray()}"); + + /* Get the length of the double-ended queue */ + int size = deque.size(); + print("Deque length size = $size"); + + /* Check if the double-ended queue is empty */ + bool isEmpty = deque.isEmpty(); + print("Is deque empty = $isEmpty"); +} diff --git a/en/codes/dart/chapter_stack_and_queue/linkedlist_queue.dart b/en/codes/dart/chapter_stack_and_queue/linkedlist_queue.dart new file mode 100644 index 000000000..bc289d734 --- /dev/null +++ b/en/codes/dart/chapter_stack_and_queue/linkedlist_queue.dart @@ -0,0 +1,103 @@ +/** + * File: linkedlist_queue.dart + * Created Time: 2023-03-28 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/list_node.dart'; + +/* Queue based on linked list implementation */ +class LinkedListQueue { + ListNode? _front; // Head node _front + ListNode? _rear; // Tail node _rear + int _queSize = 0; // Queue length + + LinkedListQueue() { + _front = null; + _rear = null; + } + + /* Get the length of the queue */ + int size() { + return _queSize; + } + + /* Check if the queue is empty */ + bool isEmpty() { + return _queSize == 0; + } + + /* Enqueue */ + void push(int _num) { + // Add _num after tail node + final node = ListNode(_num); + // If the queue is empty, make both front and rear point to the node + if (_front == null) { + _front = node; + _rear = node; + } else { + // If the queue is not empty, add the node after the tail node + _rear!.next = node; + _rear = node; + } + _queSize++; + } + + /* Dequeue */ + int pop() { + final int _num = peek(); + // Delete head node + _front = _front!.next; + _queSize--; + return _num; + } + + /* Return list for printing */ + int peek() { + if (_queSize == 0) { + throw Exception('Queue is empty'); + } + return _front!.val; + } + + /* Convert linked list to Array and return */ + List toArray() { + ListNode? node = _front; + final List queue = []; + while (node != null) { + queue.add(node.val); + node = node.next; + } + return queue; + } +} + +/* Driver Code */ +void main() { + /* Access front of the queue element */ + final queue = LinkedListQueue(); + + /* Elements enqueue */ + queue.push(1); + queue.push(3); + queue.push(2); + queue.push(5); + queue.push(4); + print("Queue queue = ${queue.toArray()}"); + + /* Return list for printing */ + final int peek = queue.peek(); + print("Front element peek = $peek"); + + /* Element dequeue */ + final int pop = queue.pop(); + print("Dequeue element pop = $pop, after dequeue queue = ${queue.toArray()}"); + + /* Get the length of the queue */ + final int size = queue.size(); + print("Queue length size = $size"); + + /* Check if the queue is empty */ + final bool isEmpty = queue.isEmpty(); + print("Is queue empty = $isEmpty"); +} diff --git a/en/codes/dart/chapter_stack_and_queue/linkedlist_stack.dart b/en/codes/dart/chapter_stack_and_queue/linkedlist_stack.dart new file mode 100644 index 000000000..b4c022849 --- /dev/null +++ b/en/codes/dart/chapter_stack_and_queue/linkedlist_stack.dart @@ -0,0 +1,93 @@ +/** + * File: linkedlist_stack.dart + * Created Time: 2023-03-27 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/list_node.dart'; + +/* Stack implemented based on linked list class */ +class LinkedListStack { + ListNode? _stackPeek; // Use head node as stack top + int _stkSize = 0; // Stack length + + LinkedListStack() { + _stackPeek = null; + } + + /* Get the length of the stack */ + int size() { + return _stkSize; + } + + /* Check if the stack is empty */ + bool isEmpty() { + return _stkSize == 0; + } + + /* Push */ + void push(int _num) { + final ListNode node = ListNode(_num); + node.next = _stackPeek; + _stackPeek = node; + _stkSize++; + } + + /* Pop */ + int pop() { + final int _num = peek(); + _stackPeek = _stackPeek!.next; + _stkSize--; + return _num; + } + + /* Return list for printing */ + int peek() { + if (_stackPeek == null) { + throw Exception("Stack is empty"); + } + return _stackPeek!.val; + } + + /* Convert linked list to List and return */ + List toList() { + ListNode? node = _stackPeek; + List list = []; + while (node != null) { + list.add(node.val); + node = node.next; + } + list = list.reversed.toList(); + return list; + } +} + +/* Driver Code */ +void main() { + /* Access top of the stack element */ + final LinkedListStack stack = LinkedListStack(); + + /* Elements push onto stack */ + stack.push(1); + stack.push(3); + stack.push(2); + stack.push(5); + stack.push(4); + print("Stack stack = ${stack.toList()}"); + + /* Return list for printing */ + final int peek = stack.peek(); + print("Top element peek = $peek"); + + /* Element pop from stack */ + final int pop = stack.pop(); + print("Pop element pop = $pop, after pop stack = ${stack.toList()}"); + + /* Get the length of the stack */ + final int size = stack.size(); + print("Stack length size = $size"); + + /* Check if empty */ + final bool isEmpty = stack.isEmpty(); + print("Is stack empty = $isEmpty"); +} diff --git a/en/codes/dart/chapter_stack_and_queue/queue.dart b/en/codes/dart/chapter_stack_and_queue/queue.dart new file mode 100644 index 000000000..ae70db2b0 --- /dev/null +++ b/en/codes/dart/chapter_stack_and_queue/queue.dart @@ -0,0 +1,37 @@ +/** + * File: queue.dart + * Created Time: 2023-03-28 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:collection'; + +void main() { + /* Access front of the queue element */ + // In Dart, generally use Queue class as queue + final Queue queue = Queue(); + + /* Elements enqueue */ + queue.add(1); + queue.add(3); + queue.add(2); + queue.add(5); + queue.add(4); + print("Queue queue = $queue"); + + /* Return list for printing */ + final int peek = queue.first; + print("Front element peek = $peek"); + + /* Element dequeue */ + final int pop = queue.removeFirst(); + print("Dequeue element pop = $pop, after dequeue queue = $queue"); + + /* Get queue length */ + final int size = queue.length; + print("Queue length size = $size"); + + /* Check if the queue is empty */ + final bool isEmpty = queue.isEmpty; + print("Is queue empty = $isEmpty"); +} diff --git a/en/codes/dart/chapter_stack_and_queue/stack.dart b/en/codes/dart/chapter_stack_and_queue/stack.dart new file mode 100644 index 000000000..e36c19bf7 --- /dev/null +++ b/en/codes/dart/chapter_stack_and_queue/stack.dart @@ -0,0 +1,35 @@ +/** + * File: stack.dart + * Created Time: 2023-03-27 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +void main() { + /* Access top of the stack element */ + // Dart has no built-in stack class, can use List as stack + final List stack = []; + + /* Elements push onto stack */ + stack.add(1); + stack.add(3); + stack.add(2); + stack.add(5); + stack.add(4); + print("Stack stack = $stack"); + + /* Return list for printing */ + final int peek = stack.last; + print("Top element peek = $peek"); + + /* Element pop from stack */ + final int pop = stack.removeLast(); + print("Pop element pop = $pop, after pop stack = $stack"); + + /* Get the length of the stack */ + final int size = stack.length; + print("Stack length size = $size"); + + /* Check if empty */ + final bool isEmpty = stack.isEmpty; + print("Is stack empty = $isEmpty"); +} diff --git a/en/codes/dart/chapter_tree/array_binary_tree.dart b/en/codes/dart/chapter_tree/array_binary_tree.dart new file mode 100644 index 000000000..4347ec14c --- /dev/null +++ b/en/codes/dart/chapter_tree/array_binary_tree.dart @@ -0,0 +1,152 @@ +/** + * File: array_binary_tree.dart + * Created Time: 2023-08-15 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +/* Binary tree class represented by array */ +class ArrayBinaryTree { + late List _tree; + + /* Constructor */ + ArrayBinaryTree(this._tree); + + /* List capacity */ + int size() { + return _tree.length; + } + + /* Get value of node at index i */ + int? val(int i) { + // If index out of bounds, return null to represent empty position + if (i < 0 || i >= size()) { + return null; + } + return _tree[i]; + } + + /* Get index of left child node of node at index i */ + int? left(int i) { + return 2 * i + 1; + } + + /* Get index of right child node of node at index i */ + int? right(int i) { + return 2 * i + 2; + } + + /* Get index of parent node of node at index i */ + int? parent(int i) { + return (i - 1) ~/ 2; + } + + /* Level-order traversal */ + List levelOrder() { + List res = []; + for (int i = 0; i < size(); i++) { + if (val(i) != null) { + res.add(val(i)!); + } + } + return res; + } + + /* Depth-first traversal */ + void dfs(int i, String order, List res) { + // If empty position, return + if (val(i) == null) { + return; + } + // Preorder traversal + if (order == 'pre') { + res.add(val(i)); + } + dfs(left(i)!, order, res); + // Inorder traversal + if (order == 'in') { + res.add(val(i)); + } + dfs(right(i)!, order, res); + // Postorder traversal + if (order == 'post') { + res.add(val(i)); + } + } + + /* Preorder traversal */ + List preOrder() { + List res = []; + dfs(0, 'pre', res); + return res; + } + + /* Inorder traversal */ + List inOrder() { + List res = []; + dfs(0, 'in', res); + return res; + } + + /* Postorder traversal */ + List postOrder() { + List res = []; + dfs(0, 'post', res); + return res; + } +} + +/* Driver Code */ +void main() { + // Initialize binary tree + // Here we use a function to generate a binary tree directly from an array + List arr = [ + 1, + 2, + 3, + 4, + null, + 6, + 7, + 8, + 9, + null, + null, + 12, + null, + null, + 15 + ]; + + TreeNode? root = listToTree(arr); + print("\nInitialize binary tree\n"); + print("Array representation of binary tree:"); + print(arr); + print("Linked list representation of binary tree:"); + printTree(root); + + // Binary tree class represented by array + ArrayBinaryTree abt = ArrayBinaryTree(arr); + + // Access node + int i = 1; + int? l = abt.left(i); + int? r = abt.right(i); + int? p = abt.parent(i); + print("\nCurrent node index is $i, value is ${abt.val(i)}"); + print("Its left child index is $l, value is ${(l == null ? "null" : abt.val(l))}"); + print("Its right child index is $r, value is ${(r == null ? "null" : abt.val(r))}"); + print("Its parent node index is $p, value is ${(p == null ? "null" : abt.val(p))}"); + + // Traverse tree + List res = abt.levelOrder(); + print("\nLevel-order traversal is: $res"); + res = abt.preOrder(); + print("Pre-order traversal is $res"); + res = abt.inOrder(); + print("In-order traversal is $res"); + res = abt.postOrder(); + print("Post-order traversal is $res"); +} diff --git a/en/codes/dart/chapter_tree/avl_tree.dart b/en/codes/dart/chapter_tree/avl_tree.dart new file mode 100644 index 000000000..f6be5e173 --- /dev/null +++ b/en/codes/dart/chapter_tree/avl_tree.dart @@ -0,0 +1,218 @@ +/** + * File: avl_tree.dart + * Created Time: 2023-04-04 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:math'; +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +class AVLTree { + TreeNode? root; + + /* Constructor */ + AVLTree() { + root = null; + } + + /* Get node height */ + int height(TreeNode? node) { + // Empty node height is -1, leaf node height is 0 + return node == null ? -1 : node.height; + } + + /* Update node height */ + void updateHeight(TreeNode? node) { + // Node height equals the height of the tallest subtree + 1 + node!.height = max(height(node.left), height(node.right)) + 1; + } + + /* Get balance factor */ + int balanceFactor(TreeNode? node) { + // Empty node balance factor is 0 + if (node == null) return 0; + // Node balance factor = left subtree height - right subtree height + return height(node.left) - height(node.right); + } + + /* Right rotation operation */ + TreeNode? rightRotate(TreeNode? node) { + TreeNode? child = node!.left; + TreeNode? grandChild = child!.right; + // Using child as pivot, rotate node to the right + child.right = node; + node.left = grandChild; + // Update node height + updateHeight(node); + updateHeight(child); + // Return root node of subtree after rotation + return child; + } + + /* Left rotation operation */ + TreeNode? leftRotate(TreeNode? node) { + TreeNode? child = node!.right; + TreeNode? grandChild = child!.left; + // Using child as pivot, rotate node to the left + child.left = node; + node.right = grandChild; + // Update node height + updateHeight(node); + updateHeight(child); + // Return root node of subtree after rotation + return child; + } + + /* Perform rotation operation to restore balance to this subtree */ + TreeNode? rotate(TreeNode? node) { + // Get balance factor of node + int factor = balanceFactor(node); + // Left-leaning tree + if (factor > 1) { + if (balanceFactor(node!.left) >= 0) { + // Right rotation + return rightRotate(node); + } else { + // First left rotation then right rotation + node.left = leftRotate(node.left); + return rightRotate(node); + } + } + // Right-leaning tree + if (factor < -1) { + if (balanceFactor(node!.right) <= 0) { + // Left rotation + return leftRotate(node); + } else { + // First right rotation then left rotation + node.right = rightRotate(node.right); + return leftRotate(node); + } + } + // Balanced tree, no rotation needed, return directly + return node; + } + + /* Insert node */ + void insert(int val) { + root = insertHelper(root, val); + } + + /* Recursively insert node (helper method) */ + TreeNode? insertHelper(TreeNode? node, int val) { + if (node == null) return TreeNode(val); + /* 1. Find insertion position and insert node */ + if (val < node.val) + node.left = insertHelper(node.left, val); + else if (val > node.val) + node.right = insertHelper(node.right, val); + else + return node; // Duplicate node not inserted, return directly + updateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = rotate(node); + // Return root node of subtree + return node; + } + + /* Remove node */ + void remove(int val) { + root = removeHelper(root, val); + } + + /* Recursively delete node (helper method) */ + TreeNode? removeHelper(TreeNode? node, int val) { + if (node == null) return null; + /* 1. Find node and delete */ + if (val < node.val) + node.left = removeHelper(node.left, val); + else if (val > node.val) + node.right = removeHelper(node.right, val); + else { + if (node.left == null || node.right == null) { + TreeNode? child = node.left ?? node.right; + // Number of child nodes = 0, delete node directly and return + if (child == null) + return null; + // Number of child nodes = 1, delete node directly + else + node = child; + } else { + // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + TreeNode? temp = node.right; + while (temp!.left != null) { + temp = temp.left; + } + node.right = removeHelper(node.right, temp.val); + node.val = temp.val; + } + } + updateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = rotate(node); + // Return root node of subtree + return node; + } + + /* Search node */ + TreeNode? search(int val) { + TreeNode? cur = root; + // Loop search, exit after passing leaf node + while (cur != null) { + // Target node is in cur's right subtree + if (val < cur.val) + cur = cur.left; + // Target node is in cur's left subtree + else if (val > cur.val) + cur = cur.right; + // Target node equals current node + else + break; + } + return cur; + } +} + +void testInsert(AVLTree tree, int val) { + tree.insert(val); + print("\nAfter inserting node $val, AVL tree is"); + printTree(tree.root); +} + +void testRemove(AVLTree tree, int val) { + tree.remove(val); + print("\nAfter deleting node $val, AVL tree is"); + printTree(tree.root); +} + +/* Driver Code */ +void main() { + /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + AVLTree avlTree = AVLTree(); + /* Insert node */ + // Delete nodes + testInsert(avlTree, 1); + testInsert(avlTree, 2); + testInsert(avlTree, 3); + testInsert(avlTree, 4); + testInsert(avlTree, 5); + testInsert(avlTree, 8); + testInsert(avlTree, 7); + testInsert(avlTree, 9); + testInsert(avlTree, 10); + testInsert(avlTree, 6); + + /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + testInsert(avlTree, 7); + + /* Remove node */ + // Delete node with degree 1 + testRemove(avlTree, 8); // Delete node with degree 2 + testRemove(avlTree, 5); // Remove node with degree 1 + testRemove(avlTree, 4); // Remove node with degree 2 + + /* Search node */ + TreeNode? node = avlTree.search(7); + print("\nFound node object is $node, node value = ${node!.val}"); +} diff --git a/en/codes/dart/chapter_tree/binary_search_tree.dart b/en/codes/dart/chapter_tree/binary_search_tree.dart new file mode 100644 index 000000000..b37632f5f --- /dev/null +++ b/en/codes/dart/chapter_tree/binary_search_tree.dart @@ -0,0 +1,153 @@ +/** + * File: binary_search_tree.dart + * Created Time: 2023-04-04 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +/* Binary search tree */ +class BinarySearchTree { + late TreeNode? _root; + + /* Constructor */ + BinarySearchTree() { + // Initialize empty tree + _root = null; + } + + /* Get root node of binary tree */ + TreeNode? getRoot() { + return _root; + } + + /* Search node */ + TreeNode? search(int _num) { + TreeNode? cur = _root; + // Loop search, exit after passing leaf node + while (cur != null) { + // Target node is in cur's right subtree + if (cur.val < _num) + cur = cur.right; + // Target node is in cur's left subtree + else if (cur.val > _num) + cur = cur.left; + // Found target node, exit loop + else + break; + } + // Return target node + return cur; + } + + /* Insert node */ + void insert(int _num) { + // If tree is empty, initialize root node + if (_root == null) { + _root = TreeNode(_num); + return; + } + TreeNode? cur = _root; + TreeNode? pre = null; + // Loop search, exit after passing leaf node + while (cur != null) { + // Found duplicate node, return directly + if (cur.val == _num) return; + pre = cur; + // Insertion position is in cur's right subtree + if (cur.val < _num) + cur = cur.right; + // Insertion position is in cur's left subtree + else + cur = cur.left; + } + // Insert node + TreeNode? node = TreeNode(_num); + if (pre!.val < _num) + pre.right = node; + else + pre.left = node; + } + + /* Remove node */ + void remove(int _num) { + // If tree is empty, return directly + if (_root == null) return; + TreeNode? cur = _root; + TreeNode? pre = null; + // Loop search, exit after passing leaf node + while (cur != null) { + // Found node to delete, exit loop + if (cur.val == _num) break; + pre = cur; + // Node to delete is in cur's right subtree + if (cur.val < _num) + cur = cur.right; + // Node to delete is in cur's left subtree + else + cur = cur.left; + } + // If no node to delete, return directly + if (cur == null) return; + // Number of child nodes = 0 or 1 + if (cur.left == null || cur.right == null) { + // When number of child nodes = 0 / 1, child = null / that child node + TreeNode? child = cur.left ?? cur.right; + // Delete node cur + if (cur != _root) { + if (pre!.left == cur) + pre.left = child; + else + pre.right = child; + } else { + // If deleted node is root node, reassign root node + _root = child; + } + } else { + // Number of child nodes = 2 + // Get next node of cur in inorder traversal + TreeNode? tmp = cur.right; + while (tmp!.left != null) { + tmp = tmp.left; + } + // Recursively delete node tmp + remove(tmp.val); + // Replace cur with tmp + cur.val = tmp.val; + } + } +} + +/* Driver Code */ +void main() { + /* Initialize binary search tree */ + BinarySearchTree bst = BinarySearchTree(); + // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + List nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]; + for (int _num in nums) { + bst.insert(_num); + } + print("\nInitialized binary tree is\n"); + printTree(bst.getRoot()); + + /* Search node */ + TreeNode? node = bst.search(7); + print("\nFound node object is $node, node value = ${node?.val}"); + + /* Insert node */ + bst.insert(16); + print("\nAfter inserting node 16, binary tree is\n"); + printTree(bst.getRoot()); + + /* Remove node */ + bst.remove(1); + print("\nAfter removing node 1, binary tree is\n"); + printTree(bst.getRoot()); + bst.remove(2); + print("\nAfter removing node 2, binary tree is\n"); + printTree(bst.getRoot()); + bst.remove(4); + print("\nAfter removing node 4, binary tree is\n"); + printTree(bst.getRoot()); +} diff --git a/en/codes/dart/chapter_tree/binary_tree.dart b/en/codes/dart/chapter_tree/binary_tree.dart new file mode 100644 index 000000000..8555ac3b7 --- /dev/null +++ b/en/codes/dart/chapter_tree/binary_tree.dart @@ -0,0 +1,37 @@ +/** + * File: binary_tree.dart + * Created Time: 2023-04-03 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +void main() { + /* Initialize binary tree */ + // Initialize node + TreeNode n1 = TreeNode(1); + TreeNode n2 = TreeNode(2); + TreeNode n3 = TreeNode(3); + TreeNode n4 = TreeNode(4); + TreeNode n5 = TreeNode(5); + // Build references (pointers) between nodes + n1.left = n2; + n1.right = n3; + n2.left = n4; + n2.right = n5; + print("\nInitialize binary tree\n"); + printTree(n1); + + /* Insert node P between n1 -> n2 */ + TreeNode p = TreeNode(0); + // Insert node p between n1 -> n2 + n1.left = p; + p.left = n2; + print("\nAfter inserting node P\n"); + printTree(n1); + // Remove node P + n1.left = n2; + print("\nAfter removing node P\n"); + printTree(n1); +} diff --git a/en/codes/dart/chapter_tree/binary_tree_bfs.dart b/en/codes/dart/chapter_tree/binary_tree_bfs.dart new file mode 100644 index 000000000..e4e2a2a19 --- /dev/null +++ b/en/codes/dart/chapter_tree/binary_tree_bfs.dart @@ -0,0 +1,38 @@ +/** + * File: binary_tree_bfs.dart + * Created Time: 2023-04-03 + * Author: liuyuxin (gvenusleo@gmai.com) + */ + +import 'dart:collection'; +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +/* Level-order traversal */ +List levelOrder(TreeNode? root) { + // Initialize queue, add root node + Queue queue = Queue(); + queue.add(root); + // Initialize a list to save the traversal sequence + List res = []; + while (queue.isNotEmpty) { + TreeNode? node = queue.removeFirst(); // Dequeue + res.add(node!.val); // Save node value + if (node.left != null) queue.add(node.left); // Left child node enqueue + if (node.right != null) queue.add(node.right); // Right child node enqueue + } + return res; +} + +/* Driver Code */ +void main() { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + TreeNode? root = listToTree([1, 2, 3, 4, 5, 6, 7]); + print("\nInitialize binary tree\n"); + printTree(root); + + // Level-order traversal + List res = levelOrder(root); + print("\nLevel-order traversal node print sequence = $res"); +} diff --git a/en/codes/dart/chapter_tree/binary_tree_dfs.dart b/en/codes/dart/chapter_tree/binary_tree_dfs.dart new file mode 100644 index 000000000..73d0253bc --- /dev/null +++ b/en/codes/dart/chapter_tree/binary_tree_dfs.dart @@ -0,0 +1,62 @@ +/** + * File: binary_tree_dfs.dart + * Created Time: 2023-04-04 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +// Initialize list for storing traversal sequence +List list = []; + +/* Preorder traversal */ +void preOrder(TreeNode? node) { + if (node == null) return; + // Visit priority: root node -> left subtree -> right subtree + list.add(node.val); + preOrder(node.left); + preOrder(node.right); +} + +/* Inorder traversal */ +void inOrder(TreeNode? node) { + if (node == null) return; + // Visit priority: left subtree -> root node -> right subtree + inOrder(node.left); + list.add(node.val); + inOrder(node.right); +} + +/* Postorder traversal */ +void postOrder(TreeNode? node) { + if (node == null) return; + // Visit priority: left subtree -> right subtree -> root node + postOrder(node.left); + postOrder(node.right); + list.add(node.val); +} + +/* Driver Code */ +void main() { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + TreeNode? root = listToTree([1, 2, 3, 4, 5, 6, 7]); + print("\nInitialize binary tree\n"); + printTree(root); + + /* Preorder traversal */ + list.clear(); + preOrder(root); + print("\nPre-order traversal node print sequence = $list"); + + /* Inorder traversal */ + list.clear(); + inOrder(root); + print("\nIn-order traversal node print sequence = $list"); + + /* Postorder traversal */ + list.clear(); + postOrder(root); + print("\nPost-order traversal node print sequence = $list"); +} diff --git a/en/codes/dart/utils/list_node.dart b/en/codes/dart/utils/list_node.dart new file mode 100644 index 000000000..96783670b --- /dev/null +++ b/en/codes/dart/utils/list_node.dart @@ -0,0 +1,24 @@ +/** + * File: list_node.dart + * Created Time: 2023-01-23 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +/* Linked list node */ +class ListNode { + int val; + ListNode? next; + + ListNode(this.val, [this.next]); +} + +/* Deserialize a list into a linked list */ +ListNode? listToLinkedList(List list) { + ListNode dum = ListNode(0); + ListNode? head = dum; + for (int val in list) { + head?.next = ListNode(val); + head = head?.next; + } + return dum.next; +} diff --git a/en/codes/dart/utils/print_util.dart b/en/codes/dart/utils/print_util.dart new file mode 100644 index 000000000..a976f93c5 --- /dev/null +++ b/en/codes/dart/utils/print_util.dart @@ -0,0 +1,90 @@ +/** + * File: print_util.dart + * Created Time: 2023-01-23 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +import 'dart:io'; + +import 'list_node.dart'; +import 'tree_node.dart'; + +class Trunk { + Trunk? prev; + String str; + + Trunk(this.prev, this.str); +} + +/* Print matrix (Array) */ +void printMatrix(List> matrix) { + print("["); + for (List row in matrix) { + print(" $row,"); + } + print("]"); +} + +/* Print linked list */ +void printLinkedList(ListNode? head) { + List list = []; + + while (head != null) { + list.add('${head.val}'); + head = head.next; + } + + print(list.join(' -> ')); +} + +/** + * Print binary tree + * This tree printer is borrowed from TECHIE DELIGHT + * https://www.techiedelight.com/c-program-print-binary-tree/ + */ +void printTree(TreeNode? root, [Trunk? prev = null, bool isRight = false]) { + if (root == null) { + return; + } + + String prev_str = ' '; + Trunk trunk = Trunk(prev, prev_str); + + printTree(root.right, trunk, true); + + if (prev == null) { + trunk.str = '———'; + } else if (isRight) { + trunk.str = '/———'; + prev_str = ' |'; + } else { + trunk.str = '\\———'; + prev.str = prev_str; + } + showTrunks(trunk); + print(' ${root.val}'); + + if (prev != null) { + prev.str = prev_str; + } + trunk.str = ' |'; + + printTree(root.left, trunk, false); +} + +void showTrunks(Trunk? p) { + if (p == null) { + return; + } + + showTrunks(p.prev); + stdout.write(p.str); +} + +/* Print heap */ +void printHeap(List heap) { + print("Array representation of heap: $heap"); + print("Heap tree representation:"); + TreeNode? root = listToTree(heap); + printTree(root); +} diff --git a/en/codes/dart/utils/tree_node.dart b/en/codes/dart/utils/tree_node.dart new file mode 100644 index 000000000..43ddcee21 --- /dev/null +++ b/en/codes/dart/utils/tree_node.dart @@ -0,0 +1,50 @@ +/** + * File: tree_node.dart + * Created Time: 2023-2-12 + * Author: Jefferson (JeffersonHuang77@gmail.com) + */ + +/* Binary tree node class */ +class TreeNode { + int val; // Node value + int height; // Node height + TreeNode? left; // Reference to left child node + TreeNode? right; // Reference to right child node + + /* Constructor */ + TreeNode(this.val, [this.height = 0, this.left, this.right]); +} + +/* Deserialize a list into a binary tree: recursion */ +TreeNode? listToTreeDFS(List arr, int i) { + if (i < 0 || i >= arr.length || arr[i] == null) { + return null; + } + TreeNode? root = TreeNode(arr[i]!); + root.left = listToTreeDFS(arr, 2 * i + 1); + root.right = listToTreeDFS(arr, 2 * i + 2); + return root; +} + +/* Deserialize a list into a binary tree */ +TreeNode? listToTree(List arr) { + return listToTreeDFS(arr, 0); +} + +/* Serialize a binary tree into a list: recursion */ +void treeToListDFS(TreeNode? root, int i, List res) { + if (root == null) return; + while (i >= res.length) { + res.add(null); + } + res[i] = root.val; + treeToListDFS(root.left, 2 * i + 1, res); + treeToListDFS(root.right, 2 * i + 2, res); +} + +/* Serialize a binary tree into a list */ +List treeToList(TreeNode? root) { + List res = []; + treeToListDFS(root, 0, res); + return res; +} diff --git a/en/codes/dart/utils/vertex.dart b/en/codes/dart/utils/vertex.dart new file mode 100644 index 000000000..c685040b4 --- /dev/null +++ b/en/codes/dart/utils/vertex.dart @@ -0,0 +1,29 @@ +/** + * File: Vertex.dart + * Created Time: 2023-05-15 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +/* Vertex class */ +class Vertex { + int val; + Vertex(this.val); + + /* Input value list vals, return vertex list vets */ + static List valsToVets(List vals) { + List vets = []; + for (int i in vals) { + vets.add(Vertex(i)); + } + return vets; + } + + /* Input vertex list vets, return value list vals */ + static List vetsToVals(List vets) { + List vals = []; + for (Vertex vet in vets) { + vals.add(vet.val); + } + return vals; + } +} diff --git a/en/codes/go/chapter_array_and_linkedlist/array.go b/en/codes/go/chapter_array_and_linkedlist/array.go new file mode 100644 index 000000000..b2d58f7bd --- /dev/null +++ b/en/codes/go/chapter_array_and_linkedlist/array.go @@ -0,0 +1,79 @@ +// File: array.go +// Created Time: 2022-12-29 +// Author: GuoWei (gongguowei01@gmail.com), cathay (cathaycchen@gmail.com) + +package chapter_array_and_linkedlist + +import ( + "math/rand" +) + +/* Random access to element */ +func randomAccess(nums []int) (randomNum int) { + // Randomly select a number in the interval [0, nums.length) + randomIndex := rand.Intn(len(nums)) + // Retrieve and return the random element + randomNum = nums[randomIndex] + return +} + +/* Extend array length */ +func extend(nums []int, enlarge int) []int { + // Initialize an array with extended length + res := make([]int, len(nums)+enlarge) + // Copy all elements from the original array to the new array + for i, num := range nums { + res[i] = num + } + // Return the extended new array + return res +} + +/* Insert element num at index index in the array */ +func insert(nums []int, num int, index int) { + // Move all elements at and after index index backward by one position + for i := len(nums) - 1; i > index; i-- { + nums[i] = nums[i-1] + } + // Assign num to the element at index index + nums[index] = num +} + +/* Remove the element at index index */ +func remove(nums []int, index int) { + // Move all elements after index index forward by one position + for i := index; i < len(nums)-1; i++ { + nums[i] = nums[i+1] + } +} + +/* Traverse array */ +func traverse(nums []int) { + count := 0 + // Traverse array by index + for i := 0; i < len(nums); i++ { + count += nums[i] + } + count = 0 + // Direct traversal of array elements + for _, num := range nums { + count += num + } + // Traverse simultaneously data index and elements + for i, num := range nums { + count += nums[i] + count += num + } +} + +/* Find the specified element in the array */ +func find(nums []int, target int) (index int) { + index = -1 + for i := 0; i < len(nums); i++ { + if nums[i] == target { + index = i + break + } + } + return +} diff --git a/en/codes/go/chapter_array_and_linkedlist/array_test.go b/en/codes/go/chapter_array_and_linkedlist/array_test.go new file mode 100644 index 000000000..fe696379b --- /dev/null +++ b/en/codes/go/chapter_array_and_linkedlist/array_test.go @@ -0,0 +1,50 @@ +// File: array_test.go +// Created Time: 2022-12-29 +// Author: GuoWei (gongguowei01@gmail.com), cathay (cathaycchen@gmail.com) + +package chapter_array_and_linkedlist + +/** +We treat Go Slice as Array here. This reduces +the learning cost and allows us to focus on data structures and algorithms. +*/ + +import ( + "fmt" + "testing" +) + +/* Driver Code */ +func TestArray(t *testing.T) { + /* Initialize array */ + var arr [5]int + fmt.Println("Array arr =", arr) + // In Go, specifying length ([5]int) creates an array, not specifying length ([]int) creates a slice + // Since Go arrays are designed to have their length determined at compile time, only constants can be used to specify the length + // For convenience in implementing the extend() function, slices are treated as arrays below + nums := []int{1, 3, 2, 5, 4} + fmt.Println("Array nums =", nums) + + /* Insert element */ + randomNum := randomAccess(nums) + fmt.Println("Get random element in nums", randomNum) + + /* Traverse array */ + nums = extend(nums, 3) + fmt.Println("Extend array length to 8, get nums =", nums) + + /* Insert element */ + insert(nums, 6, 3) + fmt.Println("Insert number 6 at index 3, get nums =", nums) + + /* Remove element */ + remove(nums, 2) + fmt.Println("Remove element at index 2, get nums =", nums) + + /* Traverse array */ + traverse(nums) + + /* Find element */ + index := find(nums, 3) + fmt.Println("Find element 3 in nums, get index =", index) +} diff --git a/en/codes/go/chapter_array_and_linkedlist/linked_list.go b/en/codes/go/chapter_array_and_linkedlist/linked_list.go new file mode 100644 index 000000000..00b4d36e3 --- /dev/null +++ b/en/codes/go/chapter_array_and_linkedlist/linked_list.go @@ -0,0 +1,51 @@ +// File: linked_list.go +// Created Time: 2022-12-29 +// Author: cathay (cathaycchen@gmail.com) + +package chapter_array_and_linkedlist + +import ( + . "github.com/krahets/hello-algo/pkg" +) + +/* Insert node P after node n0 in the linked list */ +func insertNode(n0 *ListNode, P *ListNode) { + n1 := n0.Next + P.Next = n1 + n0.Next = P +} + +/* Remove the first node after node n0 in the linked list */ +func removeItem(n0 *ListNode) { + if n0.Next == nil { + return + } + // n0 -> P -> n1 + P := n0.Next + n1 := P.Next + n0.Next = n1 +} + +/* Access the node at index index in the linked list */ +func access(head *ListNode, index int) *ListNode { + for i := 0; i < index; i++ { + if head == nil { + return nil + } + head = head.Next + } + return head +} + +/* Find the first node with value target in the linked list */ +func findNode(head *ListNode, target int) int { + index := 0 + for head != nil { + if head.Val == target { + return index + } + head = head.Next + index++ + } + return -1 +} diff --git a/en/codes/go/chapter_array_and_linkedlist/linked_list_test.go b/en/codes/go/chapter_array_and_linkedlist/linked_list_test.go new file mode 100644 index 000000000..d8a088214 --- /dev/null +++ b/en/codes/go/chapter_array_and_linkedlist/linked_list_test.go @@ -0,0 +1,48 @@ +// File: linked_list_test.go +// Created Time: 2022-12-29 +// Author: cathay (cathaycchen@gmail.com) + +package chapter_array_and_linkedlist + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestLinkedList(t *testing.T) { + /* Initialize linked list 1 -> 3 -> 2 -> 5 -> 4 */ + // Initialize each node + n0 := NewListNode(1) + n1 := NewListNode(3) + n2 := NewListNode(2) + n3 := NewListNode(5) + n4 := NewListNode(4) + + // Build references between nodes + n0.Next = n1 + n1.Next = n2 + n2.Next = n3 + n3.Next = n4 + fmt.Println("Initialized linked list is") + PrintLinkedList(n0) + + /* Insert node */ + insertNode(n0, NewListNode(0)) + fmt.Println("Linked list after inserting node is") + PrintLinkedList(n0) + + /* Remove node */ + removeItem(n0) + fmt.Println("Linked list after removing node is") + PrintLinkedList(n0) + + /* Access node */ + node := access(n0, 3) + fmt.Println("Value of node at index 3 in linked list =", node) + + /* Search node */ + index := findNode(n0, 2) + fmt.Println("Index of node with value 2 in linked list =", index) +} diff --git a/en/codes/go/chapter_array_and_linkedlist/list_test.go b/en/codes/go/chapter_array_and_linkedlist/list_test.go new file mode 100644 index 000000000..69824e2c6 --- /dev/null +++ b/en/codes/go/chapter_array_and_linkedlist/list_test.go @@ -0,0 +1,66 @@ +// File: list_test.go +// Created Time: 2022-12-18 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_array_and_linkedlist + +import ( + "fmt" + "sort" + "testing" +) + +/* Driver Code */ +func TestList(t *testing.T) { + /* Initialize list */ + nums := []int{1, 3, 2, 5, 4} + fmt.Println("List nums =", nums) + + /* Update element */ + num := nums[1] // Access element at index 1 + fmt.Println("Access element at index 1, get num =", num) + + /* Add elements at the end */ + nums[1] = 0 // Update element at index 1 to 0 + fmt.Println("Update element at index 1 to 0, get nums =", nums) + + /* Remove element */ + nums = nil + fmt.Println("After clearing list, nums =", nums) + + /* Direct traversal of list elements */ + nums = append(nums, 1) + nums = append(nums, 3) + nums = append(nums, 2) + nums = append(nums, 5) + nums = append(nums, 4) + fmt.Println("After adding elements, nums =", nums) + + /* Sort list */ + nums = append(nums[:3], append([]int{6}, nums[3:]...)...) // Insert number 6 at index 3 + fmt.Println("Insert number 6 at index 3, get nums =", nums) + + /* Remove element */ + nums = append(nums[:3], nums[4:]...) // Remove element at index 3 + fmt.Println("Remove element at index 3, get nums =", nums) + + /* Traverse list by index */ + count := 0 + for i := 0; i < len(nums); i++ { + count += nums[i] + } + /* Directly traverse list elements */ + count = 0 + for _, x := range nums { + count += x + } + + /* Concatenate two lists */ + nums1 := []int{6, 8, 7, 10, 9} + nums = append(nums, nums1...) // Concatenate list nums1 to nums + fmt.Println("Concatenate list nums1 to nums, get nums =", nums) + + /* Sort list */ + sort.Ints(nums) // After sorting, list elements are arranged from smallest to largest + fmt.Println("After sorting list, nums =", nums) +} diff --git a/en/codes/go/chapter_array_and_linkedlist/my_list.go b/en/codes/go/chapter_array_and_linkedlist/my_list.go new file mode 100644 index 000000000..b61a2bee5 --- /dev/null +++ b/en/codes/go/chapter_array_and_linkedlist/my_list.go @@ -0,0 +1,109 @@ +// File: my_list.go +// Created Time: 2022-12-18 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_array_and_linkedlist + +/* List class */ +type myList struct { + arrCapacity int + arr []int + arrSize int + extendRatio int +} + +/* Constructor */ +func newMyList() *myList { + return &myList{ + arrCapacity: 10, // List capacity + arr: make([]int, 10), // Array (stores list elements) + arrSize: 0, // List length (current number of elements) + extendRatio: 2, // Multiple by which the list capacity is extended each time + } +} + +/* Get list length (current number of elements) */ +func (l *myList) size() int { + return l.arrSize +} + +/* Get list capacity */ +func (l *myList) capacity() int { + return l.arrCapacity +} + +/* Update element */ +func (l *myList) get(index int) int { + // If the index is out of bounds, throw an exception, as below + if index < 0 || index >= l.arrSize { + panic("Index out of bounds") + } + return l.arr[index] +} + +/* Add elements at the end */ +func (l *myList) set(num, index int) { + if index < 0 || index >= l.arrSize { + panic("Index out of bounds") + } + l.arr[index] = num +} + +/* Direct traversal of list elements */ +func (l *myList) add(num int) { + // When the number of elements exceeds capacity, trigger the extension mechanism + if l.arrSize == l.arrCapacity { + l.extendCapacity() + } + l.arr[l.arrSize] = num + // Update the number of elements + l.arrSize++ +} + +/* Sort list */ +func (l *myList) insert(num, index int) { + if index < 0 || index >= l.arrSize { + panic("Index out of bounds") + } + // When the number of elements exceeds capacity, trigger the extension mechanism + if l.arrSize == l.arrCapacity { + l.extendCapacity() + } + // Move all elements after index index forward by one position + for j := l.arrSize - 1; j >= index; j-- { + l.arr[j+1] = l.arr[j] + } + l.arr[index] = num + // Update the number of elements + l.arrSize++ +} + +/* Remove element */ +func (l *myList) remove(index int) int { + if index < 0 || index >= l.arrSize { + panic("Index out of bounds") + } + num := l.arr[index] + // Create a new array with length _extend_ratio times the original array, and copy the original array to the new array + for j := index; j < l.arrSize-1; j++ { + l.arr[j] = l.arr[j+1] + } + // Update the number of elements + l.arrSize-- + // Return the removed element + return num +} + +/* Driver Code */ +func (l *myList) extendCapacity() { + // Create a new array with length extendRatio times the original array and copy the original array to the new array + l.arr = append(l.arr, make([]int, l.arrCapacity*(l.extendRatio-1))...) + // Add elements at the end + l.arrCapacity = len(l.arr) +} + +/* Return list with valid length */ +func (l *myList) toArray() []int { + // Elements enqueue + return l.arr[:l.arrSize] +} diff --git a/en/codes/go/chapter_array_and_linkedlist/my_list_test.go b/en/codes/go/chapter_array_and_linkedlist/my_list_test.go new file mode 100644 index 000000000..a7f31fc9d --- /dev/null +++ b/en/codes/go/chapter_array_and_linkedlist/my_list_test.go @@ -0,0 +1,46 @@ +// File: my_list_test.go +// Created Time: 2022-12-18 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_array_and_linkedlist + +import ( + "fmt" + "testing" +) + +/* Driver Code */ +func TestMyList(t *testing.T) { + /* Initialize list */ + nums := newMyList() + /* Direct traversal of list elements */ + nums.add(1) + nums.add(3) + nums.add(2) + nums.add(5) + nums.add(4) + fmt.Printf("List nums = %v, capacity = %v, length = %v\n", nums.toArray(), nums.capacity(), nums.size()) + + /* Sort list */ + nums.insert(6, 3) + fmt.Printf("Insert number 6 at index 3, get nums = %v\n", nums.toArray()) + + /* Remove element */ + nums.remove(3) + fmt.Printf("Remove element at index 3, get nums = %v\n", nums.toArray()) + + /* Update element */ + num := nums.get(1) + fmt.Printf("Access element at index 1, get num = %v\n", num) + + /* Add elements at the end */ + nums.set(0, 1) + fmt.Printf("Update element at index 1 to 0, get nums = %v\n", nums.toArray()) + + /* Test capacity expansion mechanism */ + for i := 0; i < 10; i++ { + // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + nums.add(i) + } + fmt.Printf("After expansion, list nums = %v, capacity = %v, length = %v\n", nums.toArray(), nums.capacity(), nums.size()) +} diff --git a/en/codes/go/chapter_backtracking/n_queens.go b/en/codes/go/chapter_backtracking/n_queens.go new file mode 100644 index 000000000..7cab2ce36 --- /dev/null +++ b/en/codes/go/chapter_backtracking/n_queens.go @@ -0,0 +1,57 @@ +// File: n_queens.go +// Created Time: 2023-05-09 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +/* Backtracking algorithm: N queens */ +func backtrack(row, n int, state *[][]string, res *[][][]string, cols, diags1, diags2 *[]bool) { + // When all rows are placed, record the solution + if row == n { + newState := make([][]string, len(*state)) + for i, _ := range newState { + newState[i] = make([]string, len((*state)[0])) + copy(newState[i], (*state)[i]) + + } + *res = append(*res, newState) + return + } + // Traverse all columns + for col := 0; col < n; col++ { + // Calculate the main diagonal and anti-diagonal corresponding to this cell + diag1 := row - col + n - 1 + diag2 := row + col + // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + if !(*cols)[col] && !(*diags1)[diag1] && !(*diags2)[diag2] { + // Attempt: place the queen in this cell + (*state)[row][col] = "Q" + (*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = true, true, true + // Place the next row + backtrack(row+1, n, state, res, cols, diags1, diags2) + // Backtrack: restore this cell to an empty cell + (*state)[row][col] = "#" + (*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = false, false, false + } + } +} + +/* Solve N queens */ +func nQueens(n int) [][][]string { + // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + state := make([][]string, n) + for i := 0; i < n; i++ { + row := make([]string, n) + for i := 0; i < n; i++ { + row[i] = "#" + } + state[i] = row + } + // Record whether there is a queen in the column + cols := make([]bool, n) + diags1 := make([]bool, 2*n-1) + diags2 := make([]bool, 2*n-1) + res := make([][][]string, 0) + backtrack(0, n, &state, &res, &cols, &diags1, &diags2) + return res +} diff --git a/en/codes/go/chapter_backtracking/n_queens_test.go b/en/codes/go/chapter_backtracking/n_queens_test.go new file mode 100644 index 000000000..903c2bd8e --- /dev/null +++ b/en/codes/go/chapter_backtracking/n_queens_test.go @@ -0,0 +1,24 @@ +// File: n_queens_test.go +// Created Time: 2023-05-14 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import ( + "fmt" + "testing" +) + +func TestNQueens(t *testing.T) { + n := 4 + res := nQueens(n) + + fmt.Println("Input board size is ", n) + fmt.Println("Total queen placement solutions: ", len(res), " solutions") + for _, state := range res { + fmt.Println("--------------------") + for _, row := range state { + fmt.Println(row) + } + } +} diff --git a/en/codes/go/chapter_backtracking/permutation_test.go b/en/codes/go/chapter_backtracking/permutation_test.go new file mode 100644 index 000000000..0d7d660d0 --- /dev/null +++ b/en/codes/go/chapter_backtracking/permutation_test.go @@ -0,0 +1,33 @@ +// File: permutation_test.go +// Created Time: 2023-05-09 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestPermutationI(t *testing.T) { + /* Permutations I */ + nums := []int{1, 2, 3} + fmt.Printf("Input array nums = ") + PrintSlice(nums) + + res := permutationsI(nums) + fmt.Printf("All permutations res = ") + fmt.Println(res) +} + +func TestPermutationII(t *testing.T) { + nums := []int{1, 2, 2} + fmt.Printf("Input array nums = ") + PrintSlice(nums) + + res := permutationsII(nums) + fmt.Printf("All permutations res = ") + fmt.Println(res) +} diff --git a/en/codes/go/chapter_backtracking/permutations_i.go b/en/codes/go/chapter_backtracking/permutations_i.go new file mode 100644 index 000000000..3983036d8 --- /dev/null +++ b/en/codes/go/chapter_backtracking/permutations_i.go @@ -0,0 +1,38 @@ +// File: permutations_i.go +// Created Time: 2023-05-14 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +/* Backtracking algorithm: Permutations I */ +func backtrackI(state *[]int, choices *[]int, selected *[]bool, res *[][]int) { + // When the state length equals the number of elements, record the solution + if len(*state) == len(*choices) { + newState := append([]int{}, *state...) + *res = append(*res, newState) + } + // Traverse all choices + for i := 0; i < len(*choices); i++ { + choice := (*choices)[i] + // Pruning: do not allow repeated selection of elements + if !(*selected)[i] { + // Attempt: make choice, update state + (*selected)[i] = true + *state = append(*state, choice) + // Proceed to the next round of selection + backtrackI(state, choices, selected, res) + // Backtrack: undo choice, restore to previous state + (*selected)[i] = false + *state = (*state)[:len(*state)-1] + } + } +} + +/* Permutations I */ +func permutationsI(nums []int) [][]int { + res := make([][]int, 0) + state := make([]int, 0) + selected := make([]bool, len(nums)) + backtrackI(&state, &nums, &selected, &res) + return res +} diff --git a/en/codes/go/chapter_backtracking/permutations_ii.go b/en/codes/go/chapter_backtracking/permutations_ii.go new file mode 100644 index 000000000..d8fd5767d --- /dev/null +++ b/en/codes/go/chapter_backtracking/permutations_ii.go @@ -0,0 +1,41 @@ +// File: permutations_ii.go +// Created Time: 2023-05-14 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +/* Backtracking algorithm: Permutations II */ +func backtrackII(state *[]int, choices *[]int, selected *[]bool, res *[][]int) { + // When the state length equals the number of elements, record the solution + if len(*state) == len(*choices) { + newState := append([]int{}, *state...) + *res = append(*res, newState) + } + // Traverse all choices + duplicated := make(map[int]struct{}, 0) + for i := 0; i < len(*choices); i++ { + choice := (*choices)[i] + // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if _, ok := duplicated[choice]; !ok && !(*selected)[i] { + // Attempt: make choice, update state + // Record the selected element value + duplicated[choice] = struct{}{} + (*selected)[i] = true + *state = append(*state, choice) + // Proceed to the next round of selection + backtrackII(state, choices, selected, res) + // Backtrack: undo choice, restore to previous state + (*selected)[i] = false + *state = (*state)[:len(*state)-1] + } + } +} + +/* Permutations II */ +func permutationsII(nums []int) [][]int { + res := make([][]int, 0) + state := make([]int, 0) + selected := make([]bool, len(nums)) + backtrackII(&state, &nums, &selected, &res) + return res +} diff --git a/en/codes/go/chapter_backtracking/preorder_traversal_i_compact.go b/en/codes/go/chapter_backtracking/preorder_traversal_i_compact.go new file mode 100644 index 000000000..930bc67a2 --- /dev/null +++ b/en/codes/go/chapter_backtracking/preorder_traversal_i_compact.go @@ -0,0 +1,22 @@ +// File: preorder_traversal_i_compact.go +// Created Time: 2023-05-09 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import ( + . "github.com/krahets/hello-algo/pkg" +) + +/* Preorder traversal: Example 1 */ +func preOrderI(root *TreeNode, res *[]*TreeNode) { + if root == nil { + return + } + if (root.Val).(int) == 7 { + // Record solution + *res = append(*res, root) + } + preOrderI(root.Left, res) + preOrderI(root.Right, res) +} diff --git a/en/codes/go/chapter_backtracking/preorder_traversal_ii_compact.go b/en/codes/go/chapter_backtracking/preorder_traversal_ii_compact.go new file mode 100644 index 000000000..c54bcf3dd --- /dev/null +++ b/en/codes/go/chapter_backtracking/preorder_traversal_ii_compact.go @@ -0,0 +1,26 @@ +// File: preorder_traversal_ii_compact.go +// Created Time: 2023-05-09 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import ( + . "github.com/krahets/hello-algo/pkg" +) + +/* Preorder traversal: Example 2 */ +func preOrderII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) { + if root == nil { + return + } + // Attempt + *path = append(*path, root) + if root.Val.(int) == 7 { + // Record solution + *res = append(*res, append([]*TreeNode{}, *path...)) + } + preOrderII(root.Left, res, path) + preOrderII(root.Right, res, path) + // Backtrack + *path = (*path)[:len(*path)-1] +} diff --git a/en/codes/go/chapter_backtracking/preorder_traversal_iii_compact.go b/en/codes/go/chapter_backtracking/preorder_traversal_iii_compact.go new file mode 100644 index 000000000..e13942ce5 --- /dev/null +++ b/en/codes/go/chapter_backtracking/preorder_traversal_iii_compact.go @@ -0,0 +1,27 @@ +// File: preorder_traversal_iii_compact.go +// Created Time: 2023-05-09 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import ( + . "github.com/krahets/hello-algo/pkg" +) + +/* Preorder traversal: Example 3 */ +func preOrderIII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) { + // Pruning + if root == nil || root.Val == 3 { + return + } + // Attempt + *path = append(*path, root) + if root.Val.(int) == 7 { + // Record solution + *res = append(*res, append([]*TreeNode{}, *path...)) + } + preOrderIII(root.Left, res, path) + preOrderIII(root.Right, res, path) + // Backtrack + *path = (*path)[:len(*path)-1] +} diff --git a/en/codes/go/chapter_backtracking/preorder_traversal_iii_template.go b/en/codes/go/chapter_backtracking/preorder_traversal_iii_template.go new file mode 100644 index 000000000..38efa8046 --- /dev/null +++ b/en/codes/go/chapter_backtracking/preorder_traversal_iii_template.go @@ -0,0 +1,57 @@ +// File: preorder_traversal_iii_template.go +// Created Time: 2023-05-09 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import ( + . "github.com/krahets/hello-algo/pkg" +) + +/* Check if the current state is a solution */ +func isSolution(state *[]*TreeNode) bool { + return len(*state) != 0 && (*state)[len(*state)-1].Val == 7 +} + +/* Record solution */ +func recordSolution(state *[]*TreeNode, res *[][]*TreeNode) { + *res = append(*res, append([]*TreeNode{}, *state...)) +} + +/* Check if the choice is valid under the current state */ +func isValid(state *[]*TreeNode, choice *TreeNode) bool { + return choice != nil && choice.Val != 3 +} + +/* Update state */ +func makeChoice(state *[]*TreeNode, choice *TreeNode) { + *state = append(*state, choice) +} + +/* Restore state */ +func undoChoice(state *[]*TreeNode, choice *TreeNode) { + *state = (*state)[:len(*state)-1] +} + +/* Backtracking algorithm: Example 3 */ +func backtrackIII(state *[]*TreeNode, choices *[]*TreeNode, res *[][]*TreeNode) { + // Check if it is a solution + if isSolution(state) { + // Record solution + recordSolution(state, res) + } + // Traverse all choices + for _, choice := range *choices { + // Pruning: check if the choice is valid + if isValid(state, choice) { + // Attempt: make choice, update state + makeChoice(state, choice) + // Proceed to the next round of selection + temp := make([]*TreeNode, 0) + temp = append(temp, choice.Left, choice.Right) + backtrackIII(state, &temp, res) + // Backtrack: undo choice, restore to previous state + undoChoice(state, choice) + } + } +} diff --git a/en/codes/go/chapter_backtracking/preorder_traversal_test.go b/en/codes/go/chapter_backtracking/preorder_traversal_test.go new file mode 100644 index 000000000..c95d05bf3 --- /dev/null +++ b/en/codes/go/chapter_backtracking/preorder_traversal_test.go @@ -0,0 +1,91 @@ +// File: preorder_traversal_i_compact_test.go +// Created Time: 2023-05-09 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestPreorderTraversalICompact(t *testing.T) { + /* Initialize binary tree */ + root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7}) + fmt.Println("\nInitialize binary tree") + PrintTree(root) + + // Preorder traversal + res := make([]*TreeNode, 0) + preOrderI(root, &res) + + fmt.Println("\nOutput all nodes with value 7") + for _, node := range res { + fmt.Printf("%v ", node.Val) + } + fmt.Println() +} + +func TestPreorderTraversalIICompact(t *testing.T) { + /* Initialize binary tree */ + root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7}) + fmt.Println("\nInitialize binary tree") + PrintTree(root) + + // Preorder traversal + path := make([]*TreeNode, 0) + res := make([][]*TreeNode, 0) + preOrderII(root, &res, &path) + + fmt.Println("\nOutput all paths from root node to node 7") + for _, path := range res { + for _, node := range path { + fmt.Printf("%v ", node.Val) + } + fmt.Println() + } +} + +func TestPreorderTraversalIIICompact(t *testing.T) { + /* Initialize binary tree */ + root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7}) + fmt.Println("\nInitialize binary tree") + PrintTree(root) + + // Preorder traversal + path := make([]*TreeNode, 0) + res := make([][]*TreeNode, 0) + preOrderIII(root, &res, &path) + + fmt.Println("\nOutput all paths from root node to node 7, paths do not include nodes with value 3") + for _, path := range res { + for _, node := range path { + fmt.Printf("%v ", node.Val) + } + fmt.Println() + } +} + +func TestPreorderTraversalIIITemplate(t *testing.T) { + /* Initialize binary tree */ + root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7}) + fmt.Println("\nInitialize binary tree") + PrintTree(root) + + // Backtracking algorithm + res := make([][]*TreeNode, 0) + state := make([]*TreeNode, 0) + choices := make([]*TreeNode, 0) + choices = append(choices, root) + backtrackIII(&state, &choices, &res) + + fmt.Println("\nOutput all paths from root node to node 7, paths do not include nodes with value 3") + for _, path := range res { + for _, node := range path { + fmt.Printf("%v ", node.Val) + } + fmt.Println() + } +} diff --git a/en/codes/go/chapter_backtracking/subset_sum_i.go b/en/codes/go/chapter_backtracking/subset_sum_i.go new file mode 100644 index 000000000..6a293bdf8 --- /dev/null +++ b/en/codes/go/chapter_backtracking/subset_sum_i.go @@ -0,0 +1,42 @@ +// File: subset_sum_i.go +// Created Time: 2023-06-24 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import "sort" + +/* Backtracking algorithm: Subset sum I */ +func backtrackSubsetSumI(start, target int, state, choices *[]int, res *[][]int) { + // When the subset sum equals target, record the solution + if target == 0 { + newState := append([]int{}, *state...) + *res = append(*res, newState) + return + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + for i := start; i < len(*choices); i++ { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if target-(*choices)[i] < 0 { + break + } + // Attempt: make choice, update target, start + *state = append(*state, (*choices)[i]) + // Proceed to the next round of selection + backtrackSubsetSumI(i, target-(*choices)[i], state, choices, res) + // Backtrack: undo choice, restore to previous state + *state = (*state)[:len(*state)-1] + } +} + +/* Solve subset sum I */ +func subsetSumI(nums []int, target int) [][]int { + state := make([]int, 0) // State (subset) + sort.Ints(nums) // Sort nums + start := 0 // Start point for traversal + res := make([][]int, 0) // Result list (subset list) + backtrackSubsetSumI(start, target, &state, &nums, &res) + return res +} diff --git a/en/codes/go/chapter_backtracking/subset_sum_i_naive.go b/en/codes/go/chapter_backtracking/subset_sum_i_naive.go new file mode 100644 index 000000000..ff207f7c7 --- /dev/null +++ b/en/codes/go/chapter_backtracking/subset_sum_i_naive.go @@ -0,0 +1,37 @@ +// File: subset_sum_i_naive.go +// Created Time: 2023-06-24 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +/* Backtracking algorithm: Subset sum I */ +func backtrackSubsetSumINaive(total, target int, state, choices *[]int, res *[][]int) { + // When the subset sum equals target, record the solution + if target == total { + newState := append([]int{}, *state...) + *res = append(*res, newState) + return + } + // Traverse all choices + for i := 0; i < len(*choices); i++ { + // Pruning: if the subset sum exceeds target, skip this choice + if total+(*choices)[i] > target { + continue + } + // Attempt: make choice, update element sum total + *state = append(*state, (*choices)[i]) + // Proceed to the next round of selection + backtrackSubsetSumINaive(total+(*choices)[i], target, state, choices, res) + // Backtrack: undo choice, restore to previous state + *state = (*state)[:len(*state)-1] + } +} + +/* Solve subset sum I (including duplicate subsets) */ +func subsetSumINaive(nums []int, target int) [][]int { + state := make([]int, 0) // State (subset) + total := 0 // Subset sum + res := make([][]int, 0) // Result list (subset list) + backtrackSubsetSumINaive(total, target, &state, &nums, &res) + return res +} diff --git a/en/codes/go/chapter_backtracking/subset_sum_ii.go b/en/codes/go/chapter_backtracking/subset_sum_ii.go new file mode 100644 index 000000000..ec172251b --- /dev/null +++ b/en/codes/go/chapter_backtracking/subset_sum_ii.go @@ -0,0 +1,47 @@ +// File: subset_sum_ii.go +// Created Time: 2023-06-24 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import "sort" + +/* Backtracking algorithm: Subset sum II */ +func backtrackSubsetSumII(start, target int, state, choices *[]int, res *[][]int) { + // When the subset sum equals target, record the solution + if target == 0 { + newState := append([]int{}, *state...) + *res = append(*res, newState) + return + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + for i := start; i < len(*choices); i++ { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if target-(*choices)[i] < 0 { + break + } + // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + if i > start && (*choices)[i] == (*choices)[i-1] { + continue + } + // Attempt: make choice, update target, start + *state = append(*state, (*choices)[i]) + // Proceed to the next round of selection + backtrackSubsetSumII(i+1, target-(*choices)[i], state, choices, res) + // Backtrack: undo choice, restore to previous state + *state = (*state)[:len(*state)-1] + } +} + +/* Solve subset sum II */ +func subsetSumII(nums []int, target int) [][]int { + state := make([]int, 0) // State (subset) + sort.Ints(nums) // Sort nums + start := 0 // Start point for traversal + res := make([][]int, 0) // Result list (subset list) + backtrackSubsetSumII(start, target, &state, &nums, &res) + return res +} diff --git a/en/codes/go/chapter_backtracking/subset_sum_test.go b/en/codes/go/chapter_backtracking/subset_sum_test.go new file mode 100644 index 000000000..0f8d58031 --- /dev/null +++ b/en/codes/go/chapter_backtracking/subset_sum_test.go @@ -0,0 +1,56 @@ +// File: subset_sum_test.go +// Created Time: 2023-06-24 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import ( + "fmt" + "strconv" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestSubsetSumINaive(t *testing.T) { + nums := []int{3, 4, 5} + target := 9 + res := subsetSumINaive(nums, target) + + fmt.Printf("target = " + strconv.Itoa(target) + ", input array nums = ") + PrintSlice(nums) + + fmt.Println("All subsets with sum equal to " + strconv.Itoa(target) + " are res = ") + for i := range res { + PrintSlice(res[i]) + } + fmt.Println("Please note that this method outputs results containing duplicate sets") +} + +func TestSubsetSumI(t *testing.T) { + nums := []int{3, 4, 5} + target := 9 + res := subsetSumI(nums, target) + + fmt.Printf("target = " + strconv.Itoa(target) + ", input array nums = ") + PrintSlice(nums) + + fmt.Println("All subsets with sum equal to " + strconv.Itoa(target) + " are res = ") + for i := range res { + PrintSlice(res[i]) + } +} + +func TestSubsetSumII(t *testing.T) { + nums := []int{4, 4, 5} + target := 9 + res := subsetSumII(nums, target) + + fmt.Printf("target = " + strconv.Itoa(target) + ", input array nums = ") + PrintSlice(nums) + + fmt.Println("All subsets with sum equal to " + strconv.Itoa(target) + " are res = ") + for i := range res { + PrintSlice(res[i]) + } +} diff --git a/en/codes/go/chapter_computational_complexity/iteration.go b/en/codes/go/chapter_computational_complexity/iteration.go new file mode 100644 index 000000000..2335932c0 --- /dev/null +++ b/en/codes/go/chapter_computational_complexity/iteration.go @@ -0,0 +1,59 @@ +// File: iteration.go +// Created Time: 2023-08-28 +// Author: Reanon (793584285@qq.com) + +package chapter_computational_complexity + +import "fmt" + +/* for loop */ +func forLoop(n int) int { + res := 0 + // Sum 1, 2, ..., n-1, n + for i := 1; i <= n; i++ { + res += i + } + return res +} + +/* while loop */ +func whileLoop(n int) int { + res := 0 + // Initialize condition variable + i := 1 + // Sum 1, 2, ..., n-1, n + for i <= n { + res += i + // Update condition variable + i++ + } + return res +} + +/* while loop (two updates) */ +func whileLoopII(n int) int { + res := 0 + // Initialize condition variable + i := 1 + // Sum 1, 4, 10, ... + for i <= n { + res += i + // Update condition variable + i++ + i *= 2 + } + return res +} + +/* Nested for loop */ +func nestedForLoop(n int) string { + res := "" + // Loop i = 1, 2, ..., n-1, n + for i := 1; i <= n; i++ { + for j := 1; j <= n; j++ { + // Loop j = 1, 2, ..., n-1, n + res += fmt.Sprintf("(%d, %d), ", i, j) + } + } + return res +} diff --git a/en/codes/go/chapter_computational_complexity/iteration_test.go b/en/codes/go/chapter_computational_complexity/iteration_test.go new file mode 100644 index 000000000..fa01e9a99 --- /dev/null +++ b/en/codes/go/chapter_computational_complexity/iteration_test.go @@ -0,0 +1,26 @@ +// File: iteration_test.go +// Created Time: 2023-08-28 +// Author: Reanon (793584285@qq.com) + +package chapter_computational_complexity + +import ( + "fmt" + "testing" +) + +/* Driver Code */ +func TestIteration(t *testing.T) { + n := 5 + res := forLoop(n) + fmt.Println("\nfor loop sum result res = ", res) + + res = whileLoop(n) + fmt.Println("\nwhile loop sum result res = ", res) + + res = whileLoopII(n) + fmt.Println("\nwhile loop (two updates) sum result res = ", res) + + resStr := nestedForLoop(n) + fmt.Println("\nDouble for loop traversal result ", resStr) +} diff --git a/en/codes/go/chapter_computational_complexity/recursion.go b/en/codes/go/chapter_computational_complexity/recursion.go new file mode 100644 index 000000000..776ca2535 --- /dev/null +++ b/en/codes/go/chapter_computational_complexity/recursion.go @@ -0,0 +1,61 @@ +// File: recursion.go +// Created Time: 2023-08-28 +// Author: Reanon (793584285@qq.com) + +package chapter_computational_complexity + +import "container/list" + +/* Recursion */ +func recur(n int) int { + // Termination condition + if n == 1 { + return 1 + } + // Recurse: recursive call + res := recur(n - 1) + // Return: return result + return n + res +} + +/* Simulate recursion using iteration */ +func forLoopRecur(n int) int { + // Use an explicit stack to simulate the system call stack + stack := list.New() + res := 0 + // Recurse: recursive call + for i := n; i > 0; i-- { + // Simulate "recurse" with "push" + stack.PushBack(i) + } + // Return: return result + for stack.Len() != 0 { + // Simulate "return" with "pop" + res += stack.Back().Value.(int) + stack.Remove(stack.Back()) + } + // res = 1+2+3+...+n + return res +} + +/* Tail recursion */ +func tailRecur(n int, res int) int { + // Termination condition + if n == 0 { + return res + } + // Tail recursive call + return tailRecur(n-1, res+n) +} + +/* Fibonacci sequence: recursion */ +func fib(n int) int { + // Termination condition f(1) = 0, f(2) = 1 + if n == 1 || n == 2 { + return n - 1 + } + // Recursive call f(n) = f(n-1) + f(n-2) + res := fib(n-1) + fib(n-2) + // Return result f(n) + return res +} diff --git a/en/codes/go/chapter_computational_complexity/recursion_test.go b/en/codes/go/chapter_computational_complexity/recursion_test.go new file mode 100644 index 000000000..2322c5bc5 --- /dev/null +++ b/en/codes/go/chapter_computational_complexity/recursion_test.go @@ -0,0 +1,26 @@ +// File: recursion_test.go +// Created Time: 2023-08-28 +// Author: Reanon (793584285@qq.com) + +package chapter_computational_complexity + +import ( + "fmt" + "testing" +) + +/* Driver Code */ +func TestRecursion(t *testing.T) { + n := 5 + res := recur(n) + fmt.Println("\nRecursive function sum result res = ", res) + + res = forLoopRecur(n) + fmt.Println("\nUsing iteration to simulate recursive sum result res = ", res) + + res = tailRecur(n, 0) + fmt.Println("\nTail recursive function sum result res = ", res) + + res = fib(n) + fmt.Println("\nThe ", n, "th term of Fibonacci sequence is", res) +} diff --git a/en/codes/go/chapter_computational_complexity/space_complexity.go b/en/codes/go/chapter_computational_complexity/space_complexity.go new file mode 100644 index 000000000..98551c19d --- /dev/null +++ b/en/codes/go/chapter_computational_complexity/space_complexity.go @@ -0,0 +1,106 @@ +// File: space_complexity.go +// Created Time: 2022-12-15 +// Author: cathay (cathaycchen@gmail.com) + +package chapter_computational_complexity + +import ( + "fmt" + "strconv" + + . "github.com/krahets/hello-algo/pkg" +) + +/* Struct */ +type node struct { + val int + next *node +} + +/* Create node struct */ +func newNode(val int) *node { + return &node{val: val} +} + +/* Function */ +func function() int { + // Perform some operations... + return 0 +} + +/* Constant order */ +func spaceConstant(n int) { + // Constants, variables, objects occupy O(1) space + const a = 0 + b := 0 + nums := make([]int, 10000) + node := newNode(0) + // Variables in the loop occupy O(1) space + var c int + for i := 0; i < n; i++ { + c = 0 + } + // Functions in the loop occupy O(1) space + for i := 0; i < n; i++ { + function() + } + b += 0 + c += 0 + nums[0] = 0 + node.val = 0 +} + +/* Linear order */ +func spaceLinear(n int) { + // Array of length n uses O(n) space + _ = make([]int, n) + // A list of length n occupies O(n) space + var nodes []*node + for i := 0; i < n; i++ { + nodes = append(nodes, newNode(i)) + } + // A hash table of length n occupies O(n) space + m := make(map[int]string, n) + for i := 0; i < n; i++ { + m[i] = strconv.Itoa(i) + } +} + +/* Linear order (recursive implementation) */ +func spaceLinearRecur(n int) { + fmt.Println("Recursion n =", n) + if n == 1 { + return + } + spaceLinearRecur(n - 1) +} + +/* Exponential order */ +func spaceQuadratic(n int) { + // Matrix uses O(n^2) space + numMatrix := make([][]int, n) + for i := 0; i < n; i++ { + numMatrix[i] = make([]int, n) + } +} + +/* Quadratic order (recursive implementation) */ +func spaceQuadraticRecur(n int) int { + if n <= 0 { + return 0 + } + nums := make([]int, n) + fmt.Printf("In recursion n = %d, nums length = %d \n", n, len(nums)) + return spaceQuadraticRecur(n - 1) +} + +/* Driver Code */ +func buildTree(n int) *TreeNode { + if n == 0 { + return nil + } + root := NewTreeNode(0) + root.Left = buildTree(n - 1) + root.Right = buildTree(n - 1) + return root +} diff --git a/en/codes/go/chapter_computational_complexity/space_complexity_test.go b/en/codes/go/chapter_computational_complexity/space_complexity_test.go new file mode 100644 index 000000000..b0a3f50a2 --- /dev/null +++ b/en/codes/go/chapter_computational_complexity/space_complexity_test.go @@ -0,0 +1,26 @@ +// File: space_complexity_test.go +// Created Time: 2022-12-15 +// Author: cathay (cathaycchen@gmail.com) + +package chapter_computational_complexity + +import ( + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestSpaceComplexity(t *testing.T) { + n := 5 + // Constant order + spaceConstant(n) + // Linear order + spaceLinear(n) + spaceLinearRecur(n) + // Exponential order + spaceQuadratic(n) + spaceQuadraticRecur(n) + // Exponential order + root := buildTree(n) + PrintTree(root) +} diff --git a/en/codes/go/chapter_computational_complexity/time_complexity.go b/en/codes/go/chapter_computational_complexity/time_complexity.go new file mode 100644 index 000000000..70211935c --- /dev/null +++ b/en/codes/go/chapter_computational_complexity/time_complexity.go @@ -0,0 +1,130 @@ +// File: time_complexity.go +// Created Time: 2022-12-13 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_computational_complexity + +/* Constant order */ +func constant(n int) int { + count := 0 + size := 100000 + for i := 0; i < size; i++ { + count++ + } + return count +} + +/* Linear order */ +func linear(n int) int { + count := 0 + for i := 0; i < n; i++ { + count++ + } + return count +} + +/* Linear order (traversing array) */ +func arrayTraversal(nums []int) int { + count := 0 + // Number of iterations is proportional to the array length + for range nums { + count++ + } + return count +} + +/* Exponential order */ +func quadratic(n int) int { + count := 0 + // Number of iterations is quadratically related to the data size n + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + count++ + } + } + return count +} + +/* Quadratic order (bubble sort) */ +func bubbleSort(nums []int) int { + count := 0 // Counter + // Outer loop: unsorted range is [0, i] + for i := len(nums) - 1; i > 0; i-- { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for j := 0; j < i; j++ { + if nums[j] > nums[j+1] { + // Swap nums[j] and nums[j + 1] + tmp := nums[j] + nums[j] = nums[j+1] + nums[j+1] = tmp + count += 3 // Element swap includes 3 unit operations + } + } + } + return count +} + +/* Exponential order (loop implementation) */ +func exponential(n int) int { + count, base := 0, 1 + // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + for i := 0; i < n; i++ { + for j := 0; j < base; j++ { + count++ + } + base *= 2 + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count +} + +/* Exponential order (recursive implementation) */ +func expRecur(n int) int { + if n == 1 { + return 1 + } + return expRecur(n-1) + expRecur(n-1) + 1 +} + +/* Logarithmic order (loop implementation) */ +func logarithmic(n int) int { + count := 0 + for n > 1 { + n = n / 2 + count++ + } + return count +} + +/* Logarithmic order (recursive implementation) */ +func logRecur(n int) int { + if n <= 1 { + return 0 + } + return logRecur(n/2) + 1 +} + +/* Linearithmic order */ +func linearLogRecur(n int) int { + if n <= 1 { + return 1 + } + count := linearLogRecur(n/2) + linearLogRecur(n/2) + for i := 0; i < n; i++ { + count++ + } + return count +} + +/* Factorial order (recursive implementation) */ +func factorialRecur(n int) int { + if n == 0 { + return 1 + } + count := 0 + // Split from 1 into n + for i := 0; i < n; i++ { + count += factorialRecur(n - 1) + } + return count +} diff --git a/en/codes/go/chapter_computational_complexity/time_complexity_test.go b/en/codes/go/chapter_computational_complexity/time_complexity_test.go new file mode 100644 index 000000000..0f74bfe0c --- /dev/null +++ b/en/codes/go/chapter_computational_complexity/time_complexity_test.go @@ -0,0 +1,48 @@ +// File: time_complexity_test.go +// Created Time: 2022-12-13 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_computational_complexity + +import ( + "fmt" + "testing" +) + +func TestTimeComplexity(t *testing.T) { + n := 8 + fmt.Println("Input data size n =", n) + + count := constant(n) + fmt.Println("Number of constant-order operations =", count) + + count = linear(n) + fmt.Println("Number of linear-order operations =", count) + count = arrayTraversal(make([]int, n)) + fmt.Println("Number of linear-order (array traversal) operations =", count) + + count = quadratic(n) + fmt.Println("Number of quadratic-order operations =", count) + nums := make([]int, n) + for i := 0; i < n; i++ { + nums[i] = n - i + } + count = bubbleSort(nums) + fmt.Println("Number of quadratic-order (bubble sort) operations =", count) + + count = exponential(n) + fmt.Println("Number of exponential-order (loop implementation) operations =", count) + count = expRecur(n) + fmt.Println("Number of exponential-order (recursive implementation) operations =", count) + + count = logarithmic(n) + fmt.Println("Number of logarithmic-order (loop implementation) operations =", count) + count = logRecur(n) + fmt.Println("Number of logarithmic-order (recursive implementation) operations =", count) + + count = linearLogRecur(n) + fmt.Println("Number of linearithmic-order (recursive implementation) operations =", count) + + count = factorialRecur(n) + fmt.Println("Number of factorial-order (recursive implementation) operations =", count) +} diff --git a/en/codes/go/chapter_computational_complexity/worst_best_time_complexity.go b/en/codes/go/chapter_computational_complexity/worst_best_time_complexity.go new file mode 100644 index 000000000..e3c4c1c30 --- /dev/null +++ b/en/codes/go/chapter_computational_complexity/worst_best_time_complexity.go @@ -0,0 +1,35 @@ +// File: worst_best_time_complexity.go +// Created Time: 2022-12-13 +// Author: msk397 (machangxinq@gmail.com), cathay (cathaycchen@gmail.com) + +package chapter_computational_complexity + +import ( + "math/rand" +) + +/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +func randomNumbers(n int) []int { + nums := make([]int, n) + // Generate array nums = { 1, 2, 3, ..., n } + for i := 0; i < n; i++ { + nums[i] = i + 1 + } + // Randomly shuffle array elements + rand.Shuffle(len(nums), func(i, j int) { + nums[i], nums[j] = nums[j], nums[i] + }) + return nums +} + +/* Find the index of number 1 in array nums */ +func findOne(nums []int) int { + for i := 0; i < len(nums); i++ { + // When element 1 is at the head of the array, best time complexity O(1) is achieved + // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + if nums[i] == 1 { + return i + } + } + return -1 +} diff --git a/en/codes/go/chapter_computational_complexity/worst_best_time_complexity_test.go b/en/codes/go/chapter_computational_complexity/worst_best_time_complexity_test.go new file mode 100644 index 000000000..a45d445bc --- /dev/null +++ b/en/codes/go/chapter_computational_complexity/worst_best_time_complexity_test.go @@ -0,0 +1,20 @@ +// File: worst_best_time_complexity_test.go +// Created Time: 2022-12-13 +// Author: msk397 (machangxinq@gmail.com), cathay (cathaycchen@gmail.com) + +package chapter_computational_complexity + +import ( + "fmt" + "testing" +) + +func TestWorstBestTimeComplexity(t *testing.T) { + for i := 0; i < 10; i++ { + n := 100 + nums := randomNumbers(n) + index := findOne(nums) + fmt.Println("\nAfter shuffling array [ 1, 2, ..., n ] =", nums) + fmt.Println("Index of number 1 is", index) + } +} diff --git a/en/codes/go/chapter_divide_and_conquer/binary_search_recur.go b/en/codes/go/chapter_divide_and_conquer/binary_search_recur.go new file mode 100644 index 000000000..74d14a373 --- /dev/null +++ b/en/codes/go/chapter_divide_and_conquer/binary_search_recur.go @@ -0,0 +1,34 @@ +// File: binary_search_recur.go +// Created Time: 2023-07-19 +// Author: hongyun-robot (1836017030@qq.com) + +package chapter_divide_and_conquer + +/* Binary search: problem f(i, j) */ +func dfs(nums []int, target, i, j int) int { + // If interval is empty, indicating no target element, return -1 + if i > j { + return -1 + } + // Calculate midpoint index + m := i + ((j - i) >> 1) + // Compare midpoint with target element + if nums[m] < target { + // If smaller, recurse on right half of array + // Recursion subproblem f(m+1, j) + return dfs(nums, target, m+1, j) + } else if nums[m] > target { + // If larger, recurse on left half of array + // Recursion subproblem f(i, m-1) + return dfs(nums, target, i, m-1) + } else { + // Found the target element, return its index + return m + } +} + +/* Binary search */ +func binarySearch(nums []int, target int) int { + n := len(nums) + return dfs(nums, target, 0, n-1) +} diff --git a/en/codes/go/chapter_divide_and_conquer/binary_search_recur_test.go b/en/codes/go/chapter_divide_and_conquer/binary_search_recur_test.go new file mode 100644 index 000000000..10d4b9876 --- /dev/null +++ b/en/codes/go/chapter_divide_and_conquer/binary_search_recur_test.go @@ -0,0 +1,20 @@ +// File: binary_search_recur_test.go +// Created Time: 2023-07-19 +// Author: hongyun-robot (1836017030@qq.com) + +package chapter_divide_and_conquer + +import ( + "fmt" + "testing" +) + +func TestBinarySearch(t *testing.T) { + nums := []int{1, 3, 6, 8, 12, 15, 23, 26, 31, 35} + target := 6 + noTarget := 99 + targetIndex := binarySearch(nums, target) + fmt.Println("Index of target element 6 = ", targetIndex) + noTargetIndex := binarySearch(nums, noTarget) + fmt.Println("Index of non-existent target element = ", noTargetIndex) +} diff --git a/en/codes/go/chapter_divide_and_conquer/build_tree.go b/en/codes/go/chapter_divide_and_conquer/build_tree.go new file mode 100644 index 000000000..8939988aa --- /dev/null +++ b/en/codes/go/chapter_divide_and_conquer/build_tree.go @@ -0,0 +1,37 @@ +// File: build_tree.go +// Created Time: 2023-07-20 +// Author: hongyun-robot (1836017030@qq.com) + +package chapter_divide_and_conquer + +import . "github.com/krahets/hello-algo/pkg" + +/* Build binary tree: divide and conquer */ +func dfsBuildTree(preorder []int, inorderMap map[int]int, i, l, r int) *TreeNode { + // Terminate when the subtree interval is empty + if r-l < 0 { + return nil + } + // Initialize the root node + root := NewTreeNode(preorder[i]) + // Query m to divide the left and right subtrees + m := inorderMap[preorder[i]] + // Subproblem: build the left subtree + root.Left = dfsBuildTree(preorder, inorderMap, i+1, l, m-1) + // Subproblem: build the right subtree + root.Right = dfsBuildTree(preorder, inorderMap, i+1+m-l, m+1, r) + // Return the root node + return root +} + +/* Build binary tree */ +func buildTree(preorder, inorder []int) *TreeNode { + // Initialize hash map, storing the mapping from inorder elements to indices + inorderMap := make(map[int]int, len(inorder)) + for i := 0; i < len(inorder); i++ { + inorderMap[inorder[i]] = i + } + + root := dfsBuildTree(preorder, inorderMap, 0, 0, len(inorder)-1) + return root +} diff --git a/en/codes/go/chapter_divide_and_conquer/build_tree_test.go b/en/codes/go/chapter_divide_and_conquer/build_tree_test.go new file mode 100644 index 000000000..bd2926abd --- /dev/null +++ b/en/codes/go/chapter_divide_and_conquer/build_tree_test.go @@ -0,0 +1,25 @@ +// File: build_tree_test.go +// Created Time: 2023-07-20 +// Author: hongyun-robot (1836017030@qq.com) + +package chapter_divide_and_conquer + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestBuildTree(t *testing.T) { + preorder := []int{3, 9, 2, 1, 7} + inorder := []int{9, 3, 1, 2, 7} + fmt.Print("Preorder traversal = ") + PrintSlice(preorder) + fmt.Print("Inorder traversal = ") + PrintSlice(inorder) + + root := buildTree(preorder, inorder) + fmt.Println("The constructed binary tree is:") + PrintTree(root) +} diff --git a/en/codes/go/chapter_divide_and_conquer/hanota.go b/en/codes/go/chapter_divide_and_conquer/hanota.go new file mode 100644 index 000000000..759aecd3a --- /dev/null +++ b/en/codes/go/chapter_divide_and_conquer/hanota.go @@ -0,0 +1,39 @@ +// File: hanota.go +// Created Time: 2023-07-21 +// Author: hongyun-robot (1836017030@qq.com) + +package chapter_divide_and_conquer + +import "container/list" + +/* Move a disk */ +func move(src, tar *list.List) { + // Take out a disk from the top of src + pan := src.Back() + // Place the disk on top of tar + tar.PushBack(pan.Value) + // Remove top disk from src + src.Remove(pan) +} + +/* Solve the Tower of Hanoi problem f(i) */ +func dfsHanota(i int, src, buf, tar *list.List) { + // If there is only one disk left in src, move it directly to tar + if i == 1 { + move(src, tar) + return + } + // Subproblem f(i-1): move the top i-1 disks from src to buf using tar + dfsHanota(i-1, src, tar, buf) + // Subproblem f(1): move the remaining disk from src to tar + move(src, tar) + // Subproblem f(i-1): move the top i-1 disks from buf to tar using src + dfsHanota(i-1, buf, src, tar) +} + +/* Solve the Tower of Hanoi problem */ +func solveHanota(A, B, C *list.List) { + n := A.Len() + // Move the top n disks from A to C using B + dfsHanota(n, A, B, C) +} diff --git a/en/codes/go/chapter_divide_and_conquer/hanota_test.go b/en/codes/go/chapter_divide_and_conquer/hanota_test.go new file mode 100644 index 000000000..db68512de --- /dev/null +++ b/en/codes/go/chapter_divide_and_conquer/hanota_test.go @@ -0,0 +1,40 @@ +// File: hanota_test.go +// Created Time: 2023-07-21 +// Author: hongyun-robot (1836017030@qq.com) + +package chapter_divide_and_conquer + +import ( + "container/list" + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestHanota(t *testing.T) { + // The tail of the list is the top of the rod + A := list.New() + for i := 5; i > 0; i-- { + A.PushBack(i) + } + B := list.New() + C := list.New() + fmt.Println("In initial state:") + fmt.Print("A = ") + PrintList(A) + fmt.Print("B = ") + PrintList(B) + fmt.Print("C = ") + PrintList(C) + + solveHanota(A, B, C) + + fmt.Println("After disk movement is complete:") + fmt.Print("A = ") + PrintList(A) + fmt.Print("B = ") + PrintList(B) + fmt.Print("C = ") + PrintList(C) +} diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_backtrack.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_backtrack.go new file mode 100644 index 000000000..998b968ca --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_backtrack.go @@ -0,0 +1,36 @@ +// File: climbing_stairs_backtrack.go +// Created Time: 2023-07-18 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +/* Backtracking */ +func backtrack(choices []int, state, n int, res []int) { + // When climbing to the n-th stair, add 1 to the solution count + if state == n { + res[0] = res[0] + 1 + } + // Traverse all choices + for _, choice := range choices { + // Pruning: not allowed to go beyond the n-th stair + if state+choice > n { + continue + } + // Attempt: make choice, update state + backtrack(choices, state+choice, n, res) + // Backtrack + } +} + +/* Climbing stairs: Backtracking */ +func climbingStairsBacktrack(n int) int { + // Can choose to climb up 1 or 2 stairs + choices := []int{1, 2} + // Start climbing from the 0-th stair + state := 0 + res := make([]int, 1) + // Use res[0] to record the solution count + res[0] = 0 + backtrack(choices, state, n, res) + return res[0] +} diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_constraint_dp.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_constraint_dp.go new file mode 100644 index 000000000..e9d006962 --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_constraint_dp.go @@ -0,0 +1,25 @@ +// File: climbing_stairs_constraint_dp.go +// Created Time: 2023-07-18 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +/* Climbing stairs with constraint: Dynamic programming */ +func climbingStairsConstraintDP(n int) int { + if n == 1 || n == 2 { + return 1 + } + // Initialize dp table, used to store solutions to subproblems + dp := make([][3]int, n+1) + // Initial state: preset the solution to the smallest subproblem + dp[1][1] = 1 + dp[1][2] = 0 + dp[2][1] = 0 + dp[2][2] = 1 + // State transition: gradually solve larger subproblems from smaller ones + for i := 3; i <= n; i++ { + dp[i][1] = dp[i-1][2] + dp[i][2] = dp[i-2][1] + dp[i-2][2] + } + return dp[n][1] + dp[n][2] +} diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs.go new file mode 100644 index 000000000..31b5c3722 --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs.go @@ -0,0 +1,21 @@ +// File: climbing_stairs_dfs.go +// Created Time: 2023-07-18 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +/* Search */ +func dfs(i int) int { + // Known dp[1] and dp[2], return them + if i == 1 || i == 2 { + return i + } + // dp[i] = dp[i-1] + dp[i-2] + count := dfs(i-1) + dfs(i-2) + return count +} + +/* Climbing stairs: Search */ +func climbingStairsDFS(n int) int { + return dfs(n) +} diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs_mem.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs_mem.go new file mode 100644 index 000000000..8036389ac --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dfs_mem.go @@ -0,0 +1,32 @@ +// File: climbing_stairs_dfs_mem.go +// Created Time: 2023-07-18 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +/* Memoization search */ +func dfsMem(i int, mem []int) int { + // Known dp[1] and dp[2], return them + if i == 1 || i == 2 { + return i + } + // If record dp[i] exists, return it directly + if mem[i] != -1 { + return mem[i] + } + // dp[i] = dp[i-1] + dp[i-2] + count := dfsMem(i-1, mem) + dfsMem(i-2, mem) + // Record dp[i] + mem[i] = count + return count +} + +/* Climbing stairs: Memoization search */ +func climbingStairsDFSMem(n int) int { + // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + mem := make([]int, n+1) + for i := range mem { + mem[i] = -1 + } + return dfsMem(n, mem) +} diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_dp.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dp.go new file mode 100644 index 000000000..83d3f07df --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_dp.go @@ -0,0 +1,35 @@ +// File: climbing_stairs_dp.go +// Created Time: 2023-07-18 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +/* Climbing stairs: Dynamic programming */ +func climbingStairsDP(n int) int { + if n == 1 || n == 2 { + return n + } + // Initialize dp table, used to store solutions to subproblems + dp := make([]int, n+1) + // Initial state: preset the solution to the smallest subproblem + dp[1] = 1 + dp[2] = 2 + // State transition: gradually solve larger subproblems from smaller ones + for i := 3; i <= n; i++ { + dp[i] = dp[i-1] + dp[i-2] + } + return dp[n] +} + +/* Climbing stairs: Space-optimized dynamic programming */ +func climbingStairsDPComp(n int) int { + if n == 1 || n == 2 { + return n + } + a, b := 1, 2 + // State transition: gradually solve larger subproblems from smaller ones + for i := 3; i <= n; i++ { + a, b = b, a+b + } + return b +} diff --git a/en/codes/go/chapter_dynamic_programming/climbing_stairs_test.go b/en/codes/go/chapter_dynamic_programming/climbing_stairs_test.go new file mode 100644 index 000000000..dd42e83ae --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/climbing_stairs_test.go @@ -0,0 +1,57 @@ +// File: climbing_stairs_test.go +// Created Time: 2023-07-18 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +import ( + "fmt" + "testing" +) + +func TestClimbingStairsBacktrack(t *testing.T) { + n := 9 + res := climbingStairsBacktrack(n) + fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) +} + +func TestClimbingStairsDFS(t *testing.T) { + n := 9 + res := climbingStairsDFS(n) + fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) +} + +func TestClimbingStairsDFSMem(t *testing.T) { + n := 9 + res := climbingStairsDFSMem(n) + fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) +} + +func TestClimbingStairsDP(t *testing.T) { + n := 9 + res := climbingStairsDP(n) + fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) +} + +func TestClimbingStairsDPComp(t *testing.T) { + n := 9 + res := climbingStairsDPComp(n) + fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) +} + +func TestClimbingStairsConstraintDP(t *testing.T) { + n := 9 + res := climbingStairsConstraintDP(n) + fmt.Printf("Climbing %d stairs has %d solutions\n", n, res) +} + +func TestMinCostClimbingStairsDPComp(t *testing.T) { + cost := []int{0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1} + fmt.Printf("Input stair cost list is %v\n", cost) + + res := minCostClimbingStairsDP(cost) + fmt.Printf("Minimum cost to climb stairs is %d\n", res) + + res = minCostClimbingStairsDPComp(cost) + fmt.Printf("Minimum cost to climb stairs is %d\n", res) +} diff --git a/en/codes/go/chapter_dynamic_programming/coin_change.go b/en/codes/go/chapter_dynamic_programming/coin_change.go new file mode 100644 index 000000000..73d2c42ca --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/coin_change.go @@ -0,0 +1,66 @@ +// File: coin_change.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +import "math" + +/* Coin change: Dynamic programming */ +func coinChangeDP(coins []int, amt int) int { + n := len(coins) + max := amt + 1 + // Initialize dp table + dp := make([][]int, n+1) + for i := 0; i <= n; i++ { + dp[i] = make([]int, amt+1) + } + // State transition: first row and first column + for a := 1; a <= amt; a++ { + dp[0][a] = max + } + // State transition: rest of the rows and columns + for i := 1; i <= n; i++ { + for a := 1; a <= amt; a++ { + if coins[i-1] > a { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i-1][a] + } else { + // The smaller value between not selecting and selecting coin i + dp[i][a] = int(math.Min(float64(dp[i-1][a]), float64(dp[i][a-coins[i-1]]+1))) + } + } + } + if dp[n][amt] != max { + return dp[n][amt] + } + return -1 +} + +/* Coin change: Dynamic programming */ +func coinChangeDPComp(coins []int, amt int) int { + n := len(coins) + max := amt + 1 + // Initialize dp table + dp := make([]int, amt+1) + for i := 1; i <= amt; i++ { + dp[i] = max + } + // State transition + for i := 1; i <= n; i++ { + // Traverse in forward order + for a := 1; a <= amt; a++ { + if coins[i-1] > a { + // If exceeds target amount, don't select coin i + dp[a] = dp[a] + } else { + // The smaller value between not selecting and selecting coin i + dp[a] = int(math.Min(float64(dp[a]), float64(dp[a-coins[i-1]]+1))) + } + } + } + if dp[amt] != max { + return dp[amt] + } + return -1 +} diff --git a/en/codes/go/chapter_dynamic_programming/coin_change_ii.go b/en/codes/go/chapter_dynamic_programming/coin_change_ii.go new file mode 100644 index 000000000..89c4a246a --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/coin_change_ii.go @@ -0,0 +1,54 @@ +// File: coin_change_ii.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +/* Coin change II: Dynamic programming */ +func coinChangeIIDP(coins []int, amt int) int { + n := len(coins) + // Initialize dp table + dp := make([][]int, n+1) + for i := 0; i <= n; i++ { + dp[i] = make([]int, amt+1) + } + // Initialize first column + for i := 0; i <= n; i++ { + dp[i][0] = 1 + } + // State transition: rest of the rows and columns + for i := 1; i <= n; i++ { + for a := 1; a <= amt; a++ { + if coins[i-1] > a { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i-1][a] + } else { + // Sum of the two options: not selecting and selecting coin i + dp[i][a] = dp[i-1][a] + dp[i][a-coins[i-1]] + } + } + } + return dp[n][amt] +} + +/* Coin change II: Space-optimized dynamic programming */ +func coinChangeIIDPComp(coins []int, amt int) int { + n := len(coins) + // Initialize dp table + dp := make([]int, amt+1) + dp[0] = 1 + // State transition + for i := 1; i <= n; i++ { + // Traverse in forward order + for a := 1; a <= amt; a++ { + if coins[i-1] > a { + // If exceeds target amount, don't select coin i + dp[a] = dp[a] + } else { + // Sum of the two options: not selecting and selecting coin i + dp[a] = dp[a] + dp[a-coins[i-1]] + } + } + } + return dp[amt] +} diff --git a/en/codes/go/chapter_dynamic_programming/coin_change_test.go b/en/codes/go/chapter_dynamic_programming/coin_change_test.go new file mode 100644 index 000000000..1f9de913a --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/coin_change_test.go @@ -0,0 +1,23 @@ +// File: coin_change_test.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +import ( + "fmt" + "testing" +) + +func TestCoinChange(t *testing.T) { + coins := []int{1, 2, 5} + amt := 4 + + // Dynamic programming + res := coinChangeDP(coins, amt) + fmt.Printf("Minimum number of coins needed to make target amount is %d\n", res) + + // Space-optimized dynamic programming + res = coinChangeDPComp(coins, amt) + fmt.Printf("Minimum number of coins needed to make target amount is %d\n", res) +} diff --git a/en/codes/go/chapter_dynamic_programming/edit_distance.go b/en/codes/go/chapter_dynamic_programming/edit_distance.go new file mode 100644 index 000000000..b4b93434a --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/edit_distance.go @@ -0,0 +1,129 @@ +// File: edit_distance.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +/* Edit distance: Brute-force search */ +func editDistanceDFS(s string, t string, i int, j int) int { + // If both s and t are empty, return 0 + if i == 0 && j == 0 { + return 0 + } + // If s is empty, return length of t + if i == 0 { + return j + } + // If t is empty, return length of s + if j == 0 { + return i + } + // If two characters are equal, skip both characters + if s[i-1] == t[j-1] { + return editDistanceDFS(s, t, i-1, j-1) + } + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + insert := editDistanceDFS(s, t, i, j-1) + deleted := editDistanceDFS(s, t, i-1, j) + replace := editDistanceDFS(s, t, i-1, j-1) + // Return minimum edit steps + return MinInt(MinInt(insert, deleted), replace) + 1 +} + +/* Edit distance: Memoization search */ +func editDistanceDFSMem(s string, t string, mem [][]int, i int, j int) int { + // If both s and t are empty, return 0 + if i == 0 && j == 0 { + return 0 + } + // If s is empty, return length of t + if i == 0 { + return j + } + // If t is empty, return length of s + if j == 0 { + return i + } + // If there's a record, return it directly + if mem[i][j] != -1 { + return mem[i][j] + } + // If two characters are equal, skip both characters + if s[i-1] == t[j-1] { + return editDistanceDFSMem(s, t, mem, i-1, j-1) + } + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + insert := editDistanceDFSMem(s, t, mem, i, j-1) + deleted := editDistanceDFSMem(s, t, mem, i-1, j) + replace := editDistanceDFSMem(s, t, mem, i-1, j-1) + // Record and return minimum edit steps + mem[i][j] = MinInt(MinInt(insert, deleted), replace) + 1 + return mem[i][j] +} + +/* Edit distance: Dynamic programming */ +func editDistanceDP(s string, t string) int { + n := len(s) + m := len(t) + dp := make([][]int, n+1) + for i := 0; i <= n; i++ { + dp[i] = make([]int, m+1) + } + // State transition: first row and first column + for i := 1; i <= n; i++ { + dp[i][0] = i + } + for j := 1; j <= m; j++ { + dp[0][j] = j + } + // State transition: rest of the rows and columns + for i := 1; i <= n; i++ { + for j := 1; j <= m; j++ { + if s[i-1] == t[j-1] { + // If two characters are equal, skip both characters + dp[i][j] = dp[i-1][j-1] + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[i][j] = MinInt(MinInt(dp[i][j-1], dp[i-1][j]), dp[i-1][j-1]) + 1 + } + } + } + return dp[n][m] +} + +/* Edit distance: Space-optimized dynamic programming */ +func editDistanceDPComp(s string, t string) int { + n := len(s) + m := len(t) + dp := make([]int, m+1) + // State transition: first row + for j := 1; j <= m; j++ { + dp[j] = j + } + // State transition: rest of the rows + for i := 1; i <= n; i++ { + // State transition: first column + leftUp := dp[0] // Temporarily store dp[i-1, j-1] + dp[0] = i + // State transition: rest of the columns + for j := 1; j <= m; j++ { + temp := dp[j] + if s[i-1] == t[j-1] { + // If two characters are equal, skip both characters + dp[j] = leftUp + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[j] = MinInt(MinInt(dp[j-1], dp[j]), leftUp) + 1 + } + leftUp = temp // Update for next round's dp[i-1, j-1] + } + } + return dp[m] +} + +func MinInt(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/en/codes/go/chapter_dynamic_programming/edit_distance_test.go b/en/codes/go/chapter_dynamic_programming/edit_distance_test.go new file mode 100644 index 000000000..bf403cb37 --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/edit_distance_test.go @@ -0,0 +1,40 @@ +// File: edit_distance_test.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +import ( + "fmt" + "testing" +) + +func TestEditDistanceDFS(test *testing.T) { + s := "bag" + t := "pack" + n := len(s) + m := len(t) + + // Brute-force search + res := editDistanceDFS(s, t, n, m) + fmt.Printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res) + + // Memoization search + mem := make([][]int, n+1) + for i := 0; i <= n; i++ { + mem[i] = make([]int, m+1) + for j := 0; j <= m; j++ { + mem[i][j] = -1 + } + } + res = editDistanceDFSMem(s, t, mem, n, m) + fmt.Printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res) + + // Dynamic programming + res = editDistanceDP(s, t) + fmt.Printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res) + + // Space-optimized dynamic programming + res = editDistanceDPComp(s, t) + fmt.Printf("Changing %s to %s requires a minimum of %d edits\n", s, t, res) +} diff --git a/en/codes/go/chapter_dynamic_programming/knapsack.go b/en/codes/go/chapter_dynamic_programming/knapsack.go new file mode 100644 index 000000000..c49337c36 --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/knapsack.go @@ -0,0 +1,87 @@ +// File: knapsack.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +import "math" + +/* 0-1 knapsack: Brute-force search */ +func knapsackDFS(wgt, val []int, i, c int) int { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if i == 0 || c == 0 { + return 0 + } + // If exceeds knapsack capacity, can only choose not to put it in + if wgt[i-1] > c { + return knapsackDFS(wgt, val, i-1, c) + } + // Calculate the maximum value of not putting in and putting in item i + no := knapsackDFS(wgt, val, i-1, c) + yes := knapsackDFS(wgt, val, i-1, c-wgt[i-1]) + val[i-1] + // Return the larger value of the two options + return int(math.Max(float64(no), float64(yes))) +} + +/* 0-1 knapsack: Memoization search */ +func knapsackDFSMem(wgt, val []int, mem [][]int, i, c int) int { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if i == 0 || c == 0 { + return 0 + } + // If there's a record, return it directly + if mem[i][c] != -1 { + return mem[i][c] + } + // If exceeds knapsack capacity, can only choose not to put it in + if wgt[i-1] > c { + return knapsackDFSMem(wgt, val, mem, i-1, c) + } + // Calculate the maximum value of not putting in and putting in item i + no := knapsackDFSMem(wgt, val, mem, i-1, c) + yes := knapsackDFSMem(wgt, val, mem, i-1, c-wgt[i-1]) + val[i-1] + // Return the larger value of the two options + mem[i][c] = int(math.Max(float64(no), float64(yes))) + return mem[i][c] +} + +/* 0-1 knapsack: Dynamic programming */ +func knapsackDP(wgt, val []int, cap int) int { + n := len(wgt) + // Initialize dp table + dp := make([][]int, n+1) + for i := 0; i <= n; i++ { + dp[i] = make([]int, cap+1) + } + // State transition + for i := 1; i <= n; i++ { + for c := 1; c <= cap; c++ { + if wgt[i-1] > c { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i-1][c] + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = int(math.Max(float64(dp[i-1][c]), float64(dp[i-1][c-wgt[i-1]]+val[i-1]))) + } + } + } + return dp[n][cap] +} + +/* 0-1 knapsack: Space-optimized dynamic programming */ +func knapsackDPComp(wgt, val []int, cap int) int { + n := len(wgt) + // Initialize dp table + dp := make([]int, cap+1) + // State transition + for i := 1; i <= n; i++ { + // Traverse in reverse order + for c := cap; c >= 1; c-- { + if wgt[i-1] <= c { + // The larger value between not selecting and selecting item i + dp[c] = int(math.Max(float64(dp[c]), float64(dp[c-wgt[i-1]]+val[i-1]))) + } + } + } + return dp[cap] +} diff --git a/en/codes/go/chapter_dynamic_programming/knapsack_test.go b/en/codes/go/chapter_dynamic_programming/knapsack_test.go new file mode 100644 index 000000000..dcff496e8 --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/knapsack_test.go @@ -0,0 +1,54 @@ +// File: knapsack_test.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +import ( + "fmt" + "testing" +) + +func TestKnapsack(t *testing.T) { + wgt := []int{10, 20, 30, 40, 50} + val := []int{50, 120, 150, 210, 240} + c := 50 + n := len(wgt) + + // Brute-force search + res := knapsackDFS(wgt, val, n, c) + fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) + + // Memoization search + mem := make([][]int, n+1) + for i := 0; i <= n; i++ { + mem[i] = make([]int, c+1) + for j := 0; j <= c; j++ { + mem[i][j] = -1 + } + } + res = knapsackDFSMem(wgt, val, mem, n, c) + fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) + + // Dynamic programming + res = knapsackDP(wgt, val, c) + fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) + + // Space-optimized dynamic programming + res = knapsackDPComp(wgt, val, c) + fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) +} + +func TestUnboundedKnapsack(t *testing.T) { + wgt := []int{1, 2, 3} + val := []int{5, 11, 15} + c := 4 + + // Dynamic programming + res := unboundedKnapsackDP(wgt, val, c) + fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) + + // Space-optimized dynamic programming + res = unboundedKnapsackDPComp(wgt, val, c) + fmt.Printf("Maximum item value not exceeding knapsack capacity is %d\n", res) +} diff --git a/en/codes/go/chapter_dynamic_programming/min_cost_climbing_stairs_dp.go b/en/codes/go/chapter_dynamic_programming/min_cost_climbing_stairs_dp.go new file mode 100644 index 000000000..b35e58796 --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/min_cost_climbing_stairs_dp.go @@ -0,0 +1,52 @@ +// File: min_cost_climbing_stairs_dp.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +/* Minimum cost climbing stairs: Dynamic programming */ +func minCostClimbingStairsDP(cost []int) int { + n := len(cost) - 1 + if n == 1 || n == 2 { + return cost[n] + } + min := func(a, b int) int { + if a < b { + return a + } + return b + } + // Initialize dp table, used to store solutions to subproblems + dp := make([]int, n+1) + // Initial state: preset the solution to the smallest subproblem + dp[1] = cost[1] + dp[2] = cost[2] + // State transition: gradually solve larger subproblems from smaller ones + for i := 3; i <= n; i++ { + dp[i] = min(dp[i-1], dp[i-2]) + cost[i] + } + return dp[n] +} + +/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +func minCostClimbingStairsDPComp(cost []int) int { + n := len(cost) - 1 + if n == 1 || n == 2 { + return cost[n] + } + min := func(a, b int) int { + if a < b { + return a + } + return b + } + // Initial state: preset the solution to the smallest subproblem + a, b := cost[1], cost[2] + // State transition: gradually solve larger subproblems from smaller ones + for i := 3; i <= n; i++ { + tmp := b + b = min(a, tmp) + cost[i] + a = tmp + } + return b +} diff --git a/en/codes/go/chapter_dynamic_programming/min_path_sum.go b/en/codes/go/chapter_dynamic_programming/min_path_sum.go new file mode 100644 index 000000000..a09008eef --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/min_path_sum.go @@ -0,0 +1,94 @@ +// File: min_path_sum.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +import "math" + +/* Minimum path sum: Brute-force search */ +func minPathSumDFS(grid [][]int, i, j int) int { + // If it's the top-left cell, terminate the search + if i == 0 && j == 0 { + return grid[0][0] + } + // If row or column index is out of bounds, return +∞ cost + if i < 0 || j < 0 { + return math.MaxInt + } + // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + up := minPathSumDFS(grid, i-1, j) + left := minPathSumDFS(grid, i, j-1) + // Return the minimum path cost from top-left to (i, j) + return int(math.Min(float64(left), float64(up))) + grid[i][j] +} + +/* Minimum path sum: Memoization search */ +func minPathSumDFSMem(grid, mem [][]int, i, j int) int { + // If it's the top-left cell, terminate the search + if i == 0 && j == 0 { + return grid[0][0] + } + // If row or column index is out of bounds, return +∞ cost + if i < 0 || j < 0 { + return math.MaxInt + } + // If there's a record, return it directly + if mem[i][j] != -1 { + return mem[i][j] + } + // Minimum path cost for left and upper cells + up := minPathSumDFSMem(grid, mem, i-1, j) + left := minPathSumDFSMem(grid, mem, i, j-1) + // Record and return the minimum path cost from top-left to (i, j) + mem[i][j] = int(math.Min(float64(left), float64(up))) + grid[i][j] + return mem[i][j] +} + +/* Minimum path sum: Dynamic programming */ +func minPathSumDP(grid [][]int) int { + n, m := len(grid), len(grid[0]) + // Initialize dp table + dp := make([][]int, n) + for i := 0; i < n; i++ { + dp[i] = make([]int, m) + } + dp[0][0] = grid[0][0] + // State transition: first row + for j := 1; j < m; j++ { + dp[0][j] = dp[0][j-1] + grid[0][j] + } + // State transition: first column + for i := 1; i < n; i++ { + dp[i][0] = dp[i-1][0] + grid[i][0] + } + // State transition: rest of the rows and columns + for i := 1; i < n; i++ { + for j := 1; j < m; j++ { + dp[i][j] = int(math.Min(float64(dp[i][j-1]), float64(dp[i-1][j]))) + grid[i][j] + } + } + return dp[n-1][m-1] +} + +/* Minimum path sum: Space-optimized dynamic programming */ +func minPathSumDPComp(grid [][]int) int { + n, m := len(grid), len(grid[0]) + // Initialize dp table + dp := make([]int, m) + // State transition: first row + dp[0] = grid[0][0] + for j := 1; j < m; j++ { + dp[j] = dp[j-1] + grid[0][j] + } + // State transition: rest of the rows and columns + for i := 1; i < n; i++ { + // State transition: first column + dp[0] = dp[0] + grid[i][0] + // State transition: rest of the columns + for j := 1; j < m; j++ { + dp[j] = int(math.Min(float64(dp[j-1]), float64(dp[j]))) + grid[i][j] + } + } + return dp[m-1] +} diff --git a/en/codes/go/chapter_dynamic_programming/min_path_sum_test.go b/en/codes/go/chapter_dynamic_programming/min_path_sum_test.go new file mode 100644 index 000000000..2c9e4c0a0 --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/min_path_sum_test.go @@ -0,0 +1,43 @@ +// File: min_path_sum_test.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +import ( + "fmt" + "testing" +) + +func TestMinPathSum(t *testing.T) { + grid := [][]int{ + {1, 3, 1, 5}, + {2, 2, 4, 2}, + {5, 3, 2, 1}, + {4, 3, 5, 2}, + } + n, m := len(grid), len(grid[0]) + + // Brute-force search + res := minPathSumDFS(grid, n-1, m-1) + fmt.Printf("Minimum path sum from top-left to bottom-right is %d\n", res) + + // Memoization search + mem := make([][]int, n) + for i := 0; i < n; i++ { + mem[i] = make([]int, m) + for j := 0; j < m; j++ { + mem[i][j] = -1 + } + } + res = minPathSumDFSMem(grid, mem, n-1, m-1) + fmt.Printf("Minimum path sum from top-left to bottom-right is %d\n", res) + + // Dynamic programming + res = minPathSumDP(grid) + fmt.Printf("Minimum path sum from top-left to bottom-right is %d\n", res) + + // Space-optimized dynamic programming + res = minPathSumDPComp(grid) + fmt.Printf("Minimum path sum from top-left to bottom-right is %d\n", res) +} diff --git a/en/codes/go/chapter_dynamic_programming/unbounded_knapsack.go b/en/codes/go/chapter_dynamic_programming/unbounded_knapsack.go new file mode 100644 index 000000000..a717eec7f --- /dev/null +++ b/en/codes/go/chapter_dynamic_programming/unbounded_knapsack.go @@ -0,0 +1,50 @@ +// File: unbounded_knapsack.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_dynamic_programming + +import "math" + +/* Unbounded knapsack: Dynamic programming */ +func unboundedKnapsackDP(wgt, val []int, cap int) int { + n := len(wgt) + // Initialize dp table + dp := make([][]int, n+1) + for i := 0; i <= n; i++ { + dp[i] = make([]int, cap+1) + } + // State transition + for i := 1; i <= n; i++ { + for c := 1; c <= cap; c++ { + if wgt[i-1] > c { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i-1][c] + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = int(math.Max(float64(dp[i-1][c]), float64(dp[i][c-wgt[i-1]]+val[i-1]))) + } + } + } + return dp[n][cap] +} + +/* Unbounded knapsack: Space-optimized dynamic programming */ +func unboundedKnapsackDPComp(wgt, val []int, cap int) int { + n := len(wgt) + // Initialize dp table + dp := make([]int, cap+1) + // State transition + for i := 1; i <= n; i++ { + for c := 1; c <= cap; c++ { + if wgt[i-1] > c { + // If exceeds knapsack capacity, don't select item i + dp[c] = dp[c] + } else { + // The larger value between not selecting and selecting item i + dp[c] = int(math.Max(float64(dp[c]), float64(dp[c-wgt[i-1]]+val[i-1]))) + } + } + } + return dp[cap] +} diff --git a/en/codes/go/chapter_graph/graph_adjacency_list.go b/en/codes/go/chapter_graph/graph_adjacency_list.go new file mode 100644 index 000000000..e1339424b --- /dev/null +++ b/en/codes/go/chapter_graph/graph_adjacency_list.go @@ -0,0 +1,100 @@ +// File: graph_adjacency_list.go +// Created Time: 2023-01-31 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import ( + "fmt" + "strconv" + "strings" + + . "github.com/krahets/hello-algo/pkg" +) + +/* Undirected graph class based on adjacency list */ +type graphAdjList struct { + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + adjList map[Vertex][]Vertex +} + +/* Constructor */ +func newGraphAdjList(edges [][]Vertex) *graphAdjList { + g := &graphAdjList{ + adjList: make(map[Vertex][]Vertex), + } + // Add all vertices and edges + for _, edge := range edges { + g.addVertex(edge[0]) + g.addVertex(edge[1]) + g.addEdge(edge[0], edge[1]) + } + return g +} + +/* Get the number of vertices */ +func (g *graphAdjList) size() int { + return len(g.adjList) +} + +/* Add edge */ +func (g *graphAdjList) addEdge(vet1 Vertex, vet2 Vertex) { + _, ok1 := g.adjList[vet1] + _, ok2 := g.adjList[vet2] + if !ok1 || !ok2 || vet1 == vet2 { + panic("error") + } + // Add edge vet1 - vet2, add anonymous struct{}, + g.adjList[vet1] = append(g.adjList[vet1], vet2) + g.adjList[vet2] = append(g.adjList[vet2], vet1) +} + +/* Remove edge */ +func (g *graphAdjList) removeEdge(vet1 Vertex, vet2 Vertex) { + _, ok1 := g.adjList[vet1] + _, ok2 := g.adjList[vet2] + if !ok1 || !ok2 || vet1 == vet2 { + panic("error") + } + // Remove edge vet1 - vet2 + g.adjList[vet1] = DeleteSliceElms(g.adjList[vet1], vet2) + g.adjList[vet2] = DeleteSliceElms(g.adjList[vet2], vet1) +} + +/* Add vertex */ +func (g *graphAdjList) addVertex(vet Vertex) { + _, ok := g.adjList[vet] + if ok { + return + } + // Add a new linked list in the adjacency list + g.adjList[vet] = make([]Vertex, 0) +} + +/* Remove vertex */ +func (g *graphAdjList) removeVertex(vet Vertex) { + _, ok := g.adjList[vet] + if !ok { + panic("error") + } + // Remove the linked list corresponding to vertex vet in the adjacency list + delete(g.adjList, vet) + // Traverse the linked lists of other vertices and remove all edges containing vet + for v, list := range g.adjList { + g.adjList[v] = DeleteSliceElms(list, vet) + } +} + +/* Print adjacency list */ +func (g *graphAdjList) print() { + var builder strings.Builder + fmt.Printf("Adjacency list = \n") + for k, v := range g.adjList { + builder.WriteString("\t\t" + strconv.Itoa(k.Val) + ": ") + for _, vet := range v { + builder.WriteString(strconv.Itoa(vet.Val) + " ") + } + fmt.Println(builder.String()) + builder.Reset() + } +} diff --git a/en/codes/go/chapter_graph/graph_adjacency_list_test.go b/en/codes/go/chapter_graph/graph_adjacency_list_test.go new file mode 100644 index 000000000..2efef7efb --- /dev/null +++ b/en/codes/go/chapter_graph/graph_adjacency_list_test.go @@ -0,0 +1,45 @@ +// File: graph_adjacency_list_test.go +// Created Time: 2023-01-31 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestGraphAdjList(t *testing.T) { + /* Add edge */ + v := ValsToVets([]int{1, 3, 2, 5, 4}) + edges := [][]Vertex{{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[3]}, {v[2], v[4]}, {v[3], v[4]}} + graph := newGraphAdjList(edges) + fmt.Println("After initialization, graph is:") + graph.print() + + /* Add edge */ + // Vertices 1, 3 are v[0], v[1] + graph.addEdge(v[0], v[2]) + fmt.Println("\nAfter adding edge 1-2, graph is") + graph.print() + + /* Remove edge */ + // Vertex 3 is v[1] + graph.removeEdge(v[0], v[1]) + fmt.Println("\nAfter removing edge 1-3, graph is") + graph.print() + + /* Add vertex */ + v5 := NewVertex(6) + graph.addVertex(v5) + fmt.Println("\nAfter adding vertex 6, graph is") + graph.print() + + /* Remove vertex */ + // Vertex 3 is v[1] + graph.removeVertex(v[1]) + fmt.Println("\nAfter removing vertex 3, graph is") + graph.print() +} diff --git a/en/codes/go/chapter_graph/graph_adjacency_matrix.go b/en/codes/go/chapter_graph/graph_adjacency_matrix.go new file mode 100644 index 000000000..219bd7b70 --- /dev/null +++ b/en/codes/go/chapter_graph/graph_adjacency_matrix.go @@ -0,0 +1,102 @@ +// File: graph_adjacency_matrix.go +// Created Time: 2023-01-31 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import "fmt" + +/* Undirected graph class based on adjacency matrix */ +type graphAdjMat struct { + // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + vertices []int + // Adjacency matrix, where the row and column indices correspond to the "vertex index" + adjMat [][]int +} + +/* Constructor */ +func newGraphAdjMat(vertices []int, edges [][]int) *graphAdjMat { + // Add vertex + n := len(vertices) + adjMat := make([][]int, n) + for i := range adjMat { + adjMat[i] = make([]int, n) + } + // Initialize graph + g := &graphAdjMat{ + vertices: vertices, + adjMat: adjMat, + } + // Add edge + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + for i := range edges { + g.addEdge(edges[i][0], edges[i][1]) + } + return g +} + +/* Get the number of vertices */ +func (g *graphAdjMat) size() int { + return len(g.vertices) +} + +/* Add vertex */ +func (g *graphAdjMat) addVertex(val int) { + n := g.size() + // Add the value of the new vertex to the vertex list + g.vertices = append(g.vertices, val) + // Add a row to the adjacency matrix + newRow := make([]int, n) + g.adjMat = append(g.adjMat, newRow) + // Add a column to the adjacency matrix + for i := range g.adjMat { + g.adjMat[i] = append(g.adjMat[i], 0) + } +} + +/* Remove vertex */ +func (g *graphAdjMat) removeVertex(index int) { + if index >= g.size() { + return + } + // Remove the vertex at index from the vertex list + g.vertices = append(g.vertices[:index], g.vertices[index+1:]...) + // Remove the row at index from the adjacency matrix + g.adjMat = append(g.adjMat[:index], g.adjMat[index+1:]...) + // Remove the column at index from the adjacency matrix + for i := range g.adjMat { + g.adjMat[i] = append(g.adjMat[i][:index], g.adjMat[i][index+1:]...) + } +} + +/* Add edge */ +// Parameters i, j correspond to the vertices element indices +func (g *graphAdjMat) addEdge(i, j int) { + // Handle index out of bounds and equality + if i < 0 || j < 0 || i >= g.size() || j >= g.size() || i == j { + fmt.Errorf("%s", "Index Out Of Bounds Exception") + } + // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + g.adjMat[i][j] = 1 + g.adjMat[j][i] = 1 +} + +/* Remove edge */ +// Parameters i, j correspond to the vertices element indices +func (g *graphAdjMat) removeEdge(i, j int) { + // Handle index out of bounds and equality + if i < 0 || j < 0 || i >= g.size() || j >= g.size() || i == j { + fmt.Errorf("%s", "Index Out Of Bounds Exception") + } + g.adjMat[i][j] = 0 + g.adjMat[j][i] = 0 +} + +/* Print adjacency matrix */ +func (g *graphAdjMat) print() { + fmt.Printf("\tVertex list = %v\n", g.vertices) + fmt.Printf("\tAdjacency matrix = \n") + for i := range g.adjMat { + fmt.Printf("\t\t\t%v\n", g.adjMat[i]) + } +} diff --git a/en/codes/go/chapter_graph/graph_adjacency_matrix_test.go b/en/codes/go/chapter_graph/graph_adjacency_matrix_test.go new file mode 100644 index 000000000..b9ce439f1 --- /dev/null +++ b/en/codes/go/chapter_graph/graph_adjacency_matrix_test.go @@ -0,0 +1,43 @@ +// File: graph_adjacency_matrix_test.go +// Created Time: 2023-01-31 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import ( + "fmt" + "testing" +) + +func TestGraphAdjMat(t *testing.T) { + /* Add edge */ + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + vertices := []int{1, 3, 2, 5, 4} + edges := [][]int{{0, 1}, {1, 2}, {2, 3}, {0, 3}, {2, 4}, {3, 4}} + graph := newGraphAdjMat(vertices, edges) + fmt.Println("After initialization, graph is:") + graph.print() + + /* Add edge */ + // Add vertex + graph.addEdge(0, 2) + fmt.Println("After adding edge 1-2, graph is") + graph.print() + + /* Remove edge */ + // Vertices 1, 3 have indices 0, 1 respectively + graph.removeEdge(0, 1) + fmt.Println("After removing edge 1-3, graph is") + graph.print() + + /* Add vertex */ + graph.addVertex(6) + fmt.Println("After adding vertex 6, graph is") + graph.print() + + /* Remove vertex */ + // Vertex 3 has index 1 + graph.removeVertex(1) + fmt.Println("After removing vertex 3, graph is") + graph.print() +} diff --git a/en/codes/go/chapter_graph/graph_bfs.go b/en/codes/go/chapter_graph/graph_bfs.go new file mode 100644 index 000000000..41d4b771c --- /dev/null +++ b/en/codes/go/chapter_graph/graph_bfs.go @@ -0,0 +1,41 @@ +// File: graph_bfs.go +// Created Time: 2023-02-18 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import ( + . "github.com/krahets/hello-algo/pkg" +) + +/* Breadth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +func graphBFS(g *graphAdjList, startVet Vertex) []Vertex { + // Vertex traversal sequence + res := make([]Vertex, 0) + // Hash set for recording vertices that have been visited + visited := make(map[Vertex]struct{}) + visited[startVet] = struct{}{} + // Queue used to implement BFS, using slice to simulate queue + queue := make([]Vertex, 0) + queue = append(queue, startVet) + // Starting from vertex vet, loop until all vertices are visited + for len(queue) > 0 { + // Dequeue the front vertex + vet := queue[0] + queue = queue[1:] + // Record visited vertex + res = append(res, vet) + // Traverse all adjacent vertices of this vertex + for _, adjVet := range g.adjList[vet] { + _, isExist := visited[adjVet] + // Only enqueue unvisited vertices + if !isExist { + queue = append(queue, adjVet) + visited[adjVet] = struct{}{} + } + } + } + // Return vertex traversal sequence + return res +} diff --git a/en/codes/go/chapter_graph/graph_bfs_test.go b/en/codes/go/chapter_graph/graph_bfs_test.go new file mode 100644 index 000000000..bdcfc7fe5 --- /dev/null +++ b/en/codes/go/chapter_graph/graph_bfs_test.go @@ -0,0 +1,29 @@ +// File: graph_bfs_test.go +// Created Time: 2023-02-18 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestGraphBFS(t *testing.T) { + /* Add edge */ + vets := ValsToVets([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) + edges := [][]Vertex{ + {vets[0], vets[1]}, {vets[0], vets[3]}, {vets[1], vets[2]}, {vets[1], vets[4]}, + {vets[2], vets[5]}, {vets[3], vets[4]}, {vets[3], vets[6]}, {vets[4], vets[5]}, + {vets[4], vets[7]}, {vets[5], vets[8]}, {vets[6], vets[7]}, {vets[7], vets[8]}} + graph := newGraphAdjList(edges) + fmt.Println("After initialization, graph is:") + graph.print() + + /* Breadth-first traversal */ + res := graphBFS(graph, vets[0]) + fmt.Println("Breadth-first traversal (BFS) vertex sequence is:") + PrintSlice(VetsToVals(res)) +} diff --git a/en/codes/go/chapter_graph/graph_dfs.go b/en/codes/go/chapter_graph/graph_dfs.go new file mode 100644 index 000000000..60658f26d --- /dev/null +++ b/en/codes/go/chapter_graph/graph_dfs.go @@ -0,0 +1,36 @@ +// File: graph_dfs.go +// Created Time: 2023-02-18 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import ( + . "github.com/krahets/hello-algo/pkg" +) + +/* Depth-first traversal helper function */ +func dfs(g *graphAdjList, visited map[Vertex]struct{}, res *[]Vertex, vet Vertex) { + // append operation returns a new reference, must reassign original reference to new slice's reference + *res = append(*res, vet) + visited[vet] = struct{}{} + // Traverse all adjacent vertices of this vertex + for _, adjVet := range g.adjList[vet] { + _, isExist := visited[adjVet] + // Recursively visit adjacent vertices + if !isExist { + dfs(g, visited, res, adjVet) + } + } +} + +/* Depth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +func graphDFS(g *graphAdjList, startVet Vertex) []Vertex { + // Vertex traversal sequence + res := make([]Vertex, 0) + // Hash set for recording vertices that have been visited + visited := make(map[Vertex]struct{}) + dfs(g, visited, &res, startVet) + // Return vertex traversal sequence + return res +} diff --git a/en/codes/go/chapter_graph/graph_dfs_test.go b/en/codes/go/chapter_graph/graph_dfs_test.go new file mode 100644 index 000000000..694c24c89 --- /dev/null +++ b/en/codes/go/chapter_graph/graph_dfs_test.go @@ -0,0 +1,28 @@ +// File: graph_dfs_test.go +// Created Time: 2023-02-18 +// Author: Reanon (793584285@qq.com) + +package chapter_graph + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestGraphDFS(t *testing.T) { + /* Add edge */ + vets := ValsToVets([]int{0, 1, 2, 3, 4, 5, 6}) + edges := [][]Vertex{ + {vets[0], vets[1]}, {vets[0], vets[3]}, {vets[1], vets[2]}, + {vets[2], vets[5]}, {vets[4], vets[5]}, {vets[5], vets[6]}} + graph := newGraphAdjList(edges) + fmt.Println("After initialization, graph is:") + graph.print() + + /* Depth-first traversal */ + res := graphDFS(graph, vets[0]) + fmt.Println("Depth-first traversal (DFS) vertex sequence is:") + PrintSlice(VetsToVals(res)) +} diff --git a/en/codes/go/chapter_greedy/coin_change_greedy.go b/en/codes/go/chapter_greedy/coin_change_greedy.go new file mode 100644 index 000000000..635c08e1a --- /dev/null +++ b/en/codes/go/chapter_greedy/coin_change_greedy.go @@ -0,0 +1,27 @@ +// File: coin_change_greedy.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_greedy + +/* Coin change: Greedy algorithm */ +func coinChangeGreedy(coins []int, amt int) int { + // Assume coins list is sorted + i := len(coins) - 1 + count := 0 + // Loop to make greedy choices until no remaining amount + for amt > 0 { + // Find the coin that is less than and closest to the remaining amount + for i > 0 && coins[i] > amt { + i-- + } + // Choose coins[i] + amt -= coins[i] + count++ + } + // If no feasible solution is found, return -1 + if amt != 0 { + return -1 + } + return count +} diff --git a/en/codes/go/chapter_greedy/coin_change_greedy_test.go b/en/codes/go/chapter_greedy/coin_change_greedy_test.go new file mode 100644 index 000000000..be4f5272f --- /dev/null +++ b/en/codes/go/chapter_greedy/coin_change_greedy_test.go @@ -0,0 +1,35 @@ +// File: coin_change_greedy_test.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_greedy + +import ( + "fmt" + "testing" +) + +func TestCoinChangeGreedy(t *testing.T) { + // Greedy algorithm: Can guarantee finding the global optimal solution + coins := []int{1, 5, 10, 20, 50, 100} + amt := 186 + res := coinChangeGreedy(coins, amt) + fmt.Printf("coins = %v, amt = %d\n", coins, amt) + fmt.Printf("Minimum number of coins needed to make %d is %d\n", amt, res) + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = []int{1, 20, 50} + amt = 60 + res = coinChangeGreedy(coins, amt) + fmt.Printf("coins = %v, amt = %d\n", coins, amt) + fmt.Printf("Minimum number of coins needed to make %d is %d\n", amt, res) + fmt.Println("Actually the minimum number needed is 3, i.e., 20 + 20 + 20") + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = []int{1, 49, 50} + amt = 98 + res = coinChangeGreedy(coins, amt) + fmt.Printf("coins = %v, amt = %d\n", coins, amt) + fmt.Printf("Minimum number of coins needed to make %d is %d\n", amt, res) + fmt.Println("Actually the minimum number needed is 2, i.e., 49 + 49") +} diff --git a/en/codes/go/chapter_greedy/fractional_knapsack.go b/en/codes/go/chapter_greedy/fractional_knapsack.go new file mode 100644 index 000000000..f54efaf9a --- /dev/null +++ b/en/codes/go/chapter_greedy/fractional_knapsack.go @@ -0,0 +1,41 @@ +// File: fractional_knapsack.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_greedy + +import "sort" + +/* Item */ +type Item struct { + w int // Item weight + v int // Item value +} + +/* Fractional knapsack: Greedy algorithm */ +func fractionalKnapsack(wgt []int, val []int, cap int) float64 { + // Create item list with two attributes: weight, value + items := make([]Item, len(wgt)) + for i := 0; i < len(wgt); i++ { + items[i] = Item{wgt[i], val[i]} + } + // Sort by unit value item.v / item.w from high to low + sort.Slice(items, func(i, j int) bool { + return float64(items[i].v)/float64(items[i].w) > float64(items[j].v)/float64(items[j].w) + }) + // Loop for greedy selection + res := 0.0 + for _, item := range items { + if item.w <= cap { + // If remaining capacity is sufficient, put the entire current item into the knapsack + res += float64(item.v) + cap -= item.w + } else { + // If remaining capacity is insufficient, put part of the current item into the knapsack + res += float64(item.v) / float64(item.w) * float64(cap) + // No remaining capacity, so break out of the loop + break + } + } + return res +} diff --git a/en/codes/go/chapter_greedy/fractional_knapsack_test.go b/en/codes/go/chapter_greedy/fractional_knapsack_test.go new file mode 100644 index 000000000..3b2d80747 --- /dev/null +++ b/en/codes/go/chapter_greedy/fractional_knapsack_test.go @@ -0,0 +1,20 @@ +// File: fractional_knapsack_test.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_greedy + +import ( + "fmt" + "testing" +) + +func TestFractionalKnapsack(t *testing.T) { + wgt := []int{10, 20, 30, 40, 50} + val := []int{50, 120, 150, 210, 240} + capacity := 50 + + // Greedy algorithm + res := fractionalKnapsack(wgt, val, capacity) + fmt.Println("Maximum item value not exceeding knapsack capacity is", res) +} diff --git a/en/codes/go/chapter_greedy/max_capacity.go b/en/codes/go/chapter_greedy/max_capacity.go new file mode 100644 index 000000000..0997c0c61 --- /dev/null +++ b/en/codes/go/chapter_greedy/max_capacity.go @@ -0,0 +1,28 @@ +// File: max_capacity.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_greedy + +import "math" + +/* Max capacity: Greedy algorithm */ +func maxCapacity(ht []int) int { + // Initialize i, j to be at both ends of the array + i, j := 0, len(ht)-1 + // Initial max capacity is 0 + res := 0 + // Loop for greedy selection until the two boards meet + for i < j { + // Update max capacity + capacity := int(math.Min(float64(ht[i]), float64(ht[j]))) * (j - i) + res = int(math.Max(float64(res), float64(capacity))) + // Move the shorter board inward + if ht[i] < ht[j] { + i++ + } else { + j-- + } + } + return res +} diff --git a/en/codes/go/chapter_greedy/max_capacity_test.go b/en/codes/go/chapter_greedy/max_capacity_test.go new file mode 100644 index 000000000..f1e2dfe51 --- /dev/null +++ b/en/codes/go/chapter_greedy/max_capacity_test.go @@ -0,0 +1,18 @@ +// File: max_capacity_test.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_greedy + +import ( + "fmt" + "testing" +) + +func TestMaxCapacity(t *testing.T) { + ht := []int{3, 8, 5, 2, 7, 7, 3, 4} + + // Greedy algorithm + res := maxCapacity(ht) + fmt.Println("Maximum capacity is", res) +} diff --git a/en/codes/go/chapter_greedy/max_product_cutting.go b/en/codes/go/chapter_greedy/max_product_cutting.go new file mode 100644 index 000000000..9e2893690 --- /dev/null +++ b/en/codes/go/chapter_greedy/max_product_cutting.go @@ -0,0 +1,28 @@ +// File: max_product_cutting.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_greedy + +import "math" + +/* Max product cutting: Greedy algorithm */ +func maxProductCutting(n int) int { + // When n <= 3, must cut out a 1 + if n <= 3 { + return 1 * (n - 1) + } + // Greedily cut out 3, a is the number of 3s, b is the remainder + a := n / 3 + b := n % 3 + if b == 1 { + // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + return int(math.Pow(3, float64(a-1))) * 2 * 2 + } + if b == 2 { + // When the remainder is 2, do nothing + return int(math.Pow(3, float64(a))) * 2 + } + // When the remainder is 0, do nothing + return int(math.Pow(3, float64(a))) +} diff --git a/en/codes/go/chapter_greedy/max_product_cutting_test.go b/en/codes/go/chapter_greedy/max_product_cutting_test.go new file mode 100644 index 000000000..fef8f1ee3 --- /dev/null +++ b/en/codes/go/chapter_greedy/max_product_cutting_test.go @@ -0,0 +1,17 @@ +// File: max_product_cutting_test.go +// Created Time: 2023-07-23 +// Author: Reanon (793584285@qq.com) + +package chapter_greedy + +import ( + "fmt" + "testing" +) + +func TestMaxProductCutting(t *testing.T) { + n := 58 + // Greedy algorithm + res := maxProductCutting(n) + fmt.Println("Maximum cutting product is", res) +} diff --git a/en/codes/go/chapter_hashing/array_hash_map.go b/en/codes/go/chapter_hashing/array_hash_map.go new file mode 100644 index 000000000..703d302e3 --- /dev/null +++ b/en/codes/go/chapter_hashing/array_hash_map.go @@ -0,0 +1,97 @@ +// File: array_hash_map.go +// Created Time: 2022-12-14 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_hashing + +import "fmt" + +/* Key-value pair */ +type pair struct { + key int + val string +} + +/* Hash table based on array implementation */ +type arrayHashMap struct { + buckets []*pair +} + +/* Initialize hash table */ +func newArrayHashMap() *arrayHashMap { + // Initialize array with 100 buckets + buckets := make([]*pair, 100) + return &arrayHashMap{buckets: buckets} +} + +/* Hash function */ +func (a *arrayHashMap) hashFunc(key int) int { + index := key % 100 + return index +} + +/* Query operation */ +func (a *arrayHashMap) get(key int) string { + index := a.hashFunc(key) + pair := a.buckets[index] + if pair == nil { + return "Not Found" + } + return pair.val +} + +/* Add operation */ +func (a *arrayHashMap) put(key int, val string) { + pair := &pair{key: key, val: val} + index := a.hashFunc(key) + a.buckets[index] = pair +} + +/* Remove operation */ +func (a *arrayHashMap) remove(key int) { + index := a.hashFunc(key) + // Set to nil to delete + a.buckets[index] = nil +} + +/* Get all key pairs */ +func (a *arrayHashMap) pairSet() []*pair { + var pairs []*pair + for _, pair := range a.buckets { + if pair != nil { + pairs = append(pairs, pair) + } + } + return pairs +} + +/* Get all keys */ +func (a *arrayHashMap) keySet() []int { + var keys []int + for _, pair := range a.buckets { + if pair != nil { + keys = append(keys, pair.key) + } + } + return keys +} + +/* Get all values */ +func (a *arrayHashMap) valueSet() []string { + var values []string + for _, pair := range a.buckets { + if pair != nil { + values = append(values, pair.val) + } + } + return values +} + +/* Print hash table */ +func (a *arrayHashMap) print() { + for _, pair := range a.buckets { + if pair != nil { + fmt.Println(pair.key, "->", pair.val) + } + } +} diff --git a/en/codes/go/chapter_hashing/array_hash_map_test.go b/en/codes/go/chapter_hashing/array_hash_map_test.go new file mode 100644 index 000000000..aec79c22a --- /dev/null +++ b/en/codes/go/chapter_hashing/array_hash_map_test.go @@ -0,0 +1,52 @@ +// File: array_hash_map_test.go +// Created Time: 2022-12-14 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_hashing + +import ( + "fmt" + "testing" +) + +func TestArrayHashMap(t *testing.T) { + /* Initialize hash table */ + hmap := newArrayHashMap() + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + hmap.put(12836, "Xiao Ha") + hmap.put(15937, "Xiao Luo") + hmap.put(16750, "Xiao Suan") + hmap.put(13276, "Xiao Fa") + hmap.put(10583, "Xiao Ya") + fmt.Println("\nAfter adding is complete, hash table is\nKey -> Value") + hmap.print() + + /* Query operation */ + // Input key into hash table to get value + name := hmap.get(15937) + fmt.Println("\nInput student ID 15937, query name " + name) + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + hmap.remove(10583) + fmt.Println("\nAfter removing 10583, hash table is\nKey -> Value") + hmap.print() + + /* Traverse hash table */ + fmt.Println("\nTraverse key-value pairs Key->Value") + for _, kv := range hmap.pairSet() { + fmt.Println(kv.key, " -> ", kv.val) + } + + fmt.Println("\nTraverse keys only Key") + for _, key := range hmap.keySet() { + fmt.Println(key) + } + + fmt.Println("\nTraverse values only Value") + for _, val := range hmap.valueSet() { + fmt.Println(val) + } +} diff --git a/en/codes/go/chapter_hashing/hash_collision_test.go b/en/codes/go/chapter_hashing/hash_collision_test.go new file mode 100644 index 000000000..f2dbfe197 --- /dev/null +++ b/en/codes/go/chapter_hashing/hash_collision_test.go @@ -0,0 +1,62 @@ +// File: hash_collision_test.go +// Created Time: 2022-12-14 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_hashing + +import ( + "fmt" + "testing" +) + +func TestHashMapChaining(t *testing.T) { + /* Initialize hash table */ + hmap := newHashMapChaining() + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + hmap.put(12836, "Xiao Ha") + hmap.put(15937, "Xiao Luo") + hmap.put(16750, "Xiao Suan") + hmap.put(13276, "Xiao Fa") + hmap.put(10583, "Xiao Ya") + fmt.Println("\nAfter adding is complete, hash table is\nKey -> Value") + hmap.print() + + /* Query operation */ + // Input key into hash table to get value + name := hmap.get(15937) + fmt.Println("\nInput student ID 15937, found name", name) + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + hmap.remove(12836) + fmt.Println("\nAfter removing 12836, hash table is\nKey -> Value") + hmap.print() +} + +func TestHashMapOpenAddressing(t *testing.T) { + /* Initialize hash table */ + hmap := newHashMapOpenAddressing() + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + hmap.put(12836, "Xiao Ha") + hmap.put(15937, "Xiao Luo") + hmap.put(16750, "Xiao Suan") + hmap.put(13276, "Xiao Fa") + hmap.put(10583, "Xiao Ya") + fmt.Println("\nAfter adding is complete, hash table is\nKey -> Value") + hmap.print() + + /* Query operation */ + // Input key into hash table to get value + name := hmap.get(13276) + fmt.Println("\nInput student ID 13276, query name ", name) + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + hmap.remove(16750) + fmt.Println("\nAfter removing 16750, hash table is\nKey -> Value") + hmap.print() +} diff --git a/en/codes/go/chapter_hashing/hash_map_chaining.go b/en/codes/go/chapter_hashing/hash_map_chaining.go new file mode 100644 index 000000000..4e4dfbac5 --- /dev/null +++ b/en/codes/go/chapter_hashing/hash_map_chaining.go @@ -0,0 +1,134 @@ +// File: hash_map_chaining.go +// Created Time: 2023-06-23 +// Author: Reanon (793584285@qq.com) + +package chapter_hashing + +import ( + "fmt" + "strconv" + "strings" +) + +/* Hash table with separate chaining */ +type hashMapChaining struct { + size int // Number of key-value pairs + capacity int // Hash table capacity + loadThres float64 // Load factor threshold for triggering expansion + extendRatio int // Expansion multiplier + buckets [][]pair // Bucket array +} + +/* Constructor */ +func newHashMapChaining() *hashMapChaining { + buckets := make([][]pair, 4) + for i := 0; i < 4; i++ { + buckets[i] = make([]pair, 0) + } + return &hashMapChaining{ + size: 0, + capacity: 4, + loadThres: 2.0 / 3.0, + extendRatio: 2, + buckets: buckets, + } +} + +/* Hash function */ +func (m *hashMapChaining) hashFunc(key int) int { + return key % m.capacity +} + +/* Load factor */ +func (m *hashMapChaining) loadFactor() float64 { + return float64(m.size) / float64(m.capacity) +} + +/* Query operation */ +func (m *hashMapChaining) get(key int) string { + idx := m.hashFunc(key) + bucket := m.buckets[idx] + // Traverse bucket, if key is found, return corresponding val + for _, p := range bucket { + if p.key == key { + return p.val + } + } + // Return empty string if key not found + return "" +} + +/* Add operation */ +func (m *hashMapChaining) put(key int, val string) { + // When load factor exceeds threshold, perform expansion + if m.loadFactor() > m.loadThres { + m.extend() + } + idx := m.hashFunc(key) + // Traverse bucket, if specified key is encountered, update corresponding val and return + for i := range m.buckets[idx] { + if m.buckets[idx][i].key == key { + m.buckets[idx][i].val = val + return + } + } + // If key does not exist, append key-value pair to the end + p := pair{ + key: key, + val: val, + } + m.buckets[idx] = append(m.buckets[idx], p) + m.size += 1 +} + +/* Remove operation */ +func (m *hashMapChaining) remove(key int) { + idx := m.hashFunc(key) + // Traverse bucket and remove key-value pair from it + for i, p := range m.buckets[idx] { + if p.key == key { + // Slice deletion + m.buckets[idx] = append(m.buckets[idx][:i], m.buckets[idx][i+1:]...) + m.size -= 1 + break + } + } +} + +/* Expand hash table */ +func (m *hashMapChaining) extend() { + // Temporarily store the original hash table + tmpBuckets := make([][]pair, len(m.buckets)) + for i := 0; i < len(m.buckets); i++ { + tmpBuckets[i] = make([]pair, len(m.buckets[i])) + copy(tmpBuckets[i], m.buckets[i]) + } + // Initialize expanded new hash table + m.capacity *= m.extendRatio + m.buckets = make([][]pair, m.capacity) + for i := 0; i < m.capacity; i++ { + m.buckets[i] = make([]pair, 0) + } + m.size = 0 + // Move key-value pairs from original hash table to new hash table + for _, bucket := range tmpBuckets { + for _, p := range bucket { + m.put(p.key, p.val) + } + } +} + +/* Print hash table */ +func (m *hashMapChaining) print() { + var builder strings.Builder + + for _, bucket := range m.buckets { + builder.WriteString("[") + for _, p := range bucket { + builder.WriteString(strconv.Itoa(p.key) + " -> " + p.val + " ") + } + builder.WriteString("]") + fmt.Println(builder.String()) + builder.Reset() + } +} diff --git a/en/codes/go/chapter_hashing/hash_map_open_addressing.go b/en/codes/go/chapter_hashing/hash_map_open_addressing.go new file mode 100644 index 000000000..d2523a157 --- /dev/null +++ b/en/codes/go/chapter_hashing/hash_map_open_addressing.go @@ -0,0 +1,126 @@ +// File: hash_map_open_addressing.go +// Created Time: 2023-06-23 +// Author: Reanon (793584285@qq.com) + +package chapter_hashing + +import ( + "fmt" +) + +/* Hash table with open addressing */ +type hashMapOpenAddressing struct { + size int // Number of key-value pairs + capacity int // Hash table capacity + loadThres float64 // Load factor threshold for triggering expansion + extendRatio int // Expansion multiplier + buckets []*pair // Bucket array + TOMBSTONE *pair // Removal marker +} + +/* Constructor */ +func newHashMapOpenAddressing() *hashMapOpenAddressing { + return &hashMapOpenAddressing{ + size: 0, + capacity: 4, + loadThres: 2.0 / 3.0, + extendRatio: 2, + buckets: make([]*pair, 4), + TOMBSTONE: &pair{-1, "-1"}, + } +} + +/* Hash function */ +func (h *hashMapOpenAddressing) hashFunc(key int) int { + return key % h.capacity // Calculate hash value based on key +} + +/* Load factor */ +func (h *hashMapOpenAddressing) loadFactor() float64 { + return float64(h.size) / float64(h.capacity) // Calculate current load factor +} + +/* Search for bucket index corresponding to key */ +func (h *hashMapOpenAddressing) findBucket(key int) int { + index := h.hashFunc(key) // Get initial index + firstTombstone := -1 // Record position of first TOMBSTONE encountered + for h.buckets[index] != nil { + if h.buckets[index].key == key { + if firstTombstone != -1 { + // If a removal marker was encountered before, move the key-value pair to that index + h.buckets[firstTombstone] = h.buckets[index] + h.buckets[index] = h.TOMBSTONE + return firstTombstone // Return the moved bucket index + } + return index // Return found index + } + if firstTombstone == -1 && h.buckets[index] == h.TOMBSTONE { + firstTombstone = index // Record position of first deletion marker encountered + } + index = (index + 1) % h.capacity // Linear probing, wrap around to head if past tail + } + // If key does not exist, return the index for insertion + if firstTombstone != -1 { + return firstTombstone + } + return index +} + +/* Query operation */ +func (h *hashMapOpenAddressing) get(key int) string { + index := h.findBucket(key) // Search for bucket index corresponding to key + if h.buckets[index] != nil && h.buckets[index] != h.TOMBSTONE { + return h.buckets[index].val // If key-value pair is found, return corresponding val + } + return "" // Return "" if key-value pair does not exist +} + +/* Add operation */ +func (h *hashMapOpenAddressing) put(key int, val string) { + if h.loadFactor() > h.loadThres { + h.extend() // When load factor exceeds threshold, perform expansion + } + index := h.findBucket(key) // Search for bucket index corresponding to key + if h.buckets[index] == nil || h.buckets[index] == h.TOMBSTONE { + h.buckets[index] = &pair{key, val} // If key-value pair does not exist, add the key-value pair + h.size++ + } else { + h.buckets[index].val = val // If key-value pair found, overwrite val + } +} + +/* Remove operation */ +func (h *hashMapOpenAddressing) remove(key int) { + index := h.findBucket(key) // Search for bucket index corresponding to key + if h.buckets[index] != nil && h.buckets[index] != h.TOMBSTONE { + h.buckets[index] = h.TOMBSTONE // If key-value pair is found, overwrite it with removal marker + h.size-- + } +} + +/* Expand hash table */ +func (h *hashMapOpenAddressing) extend() { + oldBuckets := h.buckets // Temporarily store the original hash table + h.capacity *= h.extendRatio // Update capacity + h.buckets = make([]*pair, h.capacity) // Initialize expanded new hash table + h.size = 0 // Reset size + // Move key-value pairs from original hash table to new hash table + for _, pair := range oldBuckets { + if pair != nil && pair != h.TOMBSTONE { + h.put(pair.key, pair.val) + } + } +} + +/* Print hash table */ +func (h *hashMapOpenAddressing) print() { + for _, pair := range h.buckets { + if pair == nil { + fmt.Println("nil") + } else if pair == h.TOMBSTONE { + fmt.Println("TOMBSTONE") + } else { + fmt.Printf("%d -> %s\n", pair.key, pair.val) + } + } +} diff --git a/en/codes/go/chapter_hashing/hash_map_test.go b/en/codes/go/chapter_hashing/hash_map_test.go new file mode 100644 index 000000000..ae88ba5a5 --- /dev/null +++ b/en/codes/go/chapter_hashing/hash_map_test.go @@ -0,0 +1,74 @@ +// File: hash_map_test.go +// Created Time: 2022-12-14 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_hashing + +import ( + "fmt" + "strconv" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestHashMap(t *testing.T) { + /* Initialize hash table */ + hmap := make(map[int]string) + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + hmap[12836] = "Xiao Ha" + hmap[15937] = "Xiao Luo" + hmap[16750] = "Xiao Suan" + hmap[13276] = "Xiao Fa" + hmap[10583] = "Xiao Ya" + fmt.Println("\nAfter adding is complete, hash table is\nKey -> Value") + PrintMap(hmap) + + /* Query operation */ + // Input key into hash table to get value + name := hmap[15937] + fmt.Println("\nInput student ID 15937, query name ", name) + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + delete(hmap, 10583) + fmt.Println("\nAfter removing 10583, hash table is\nKey -> Value") + PrintMap(hmap) + + /* Traverse hash table */ + // Traverse key-value pairs + fmt.Println("\nTraverse key-value pairs Key->Value") + for key, value := range hmap { + fmt.Println(key, "->", value) + } + // Traverse keys only + fmt.Println("\nTraverse keys only Key") + for key := range hmap { + fmt.Println(key) + } + // Traverse values only + fmt.Println("\nTraverse values only Value") + for _, value := range hmap { + fmt.Println(value) + } +} + +func TestSimpleHash(t *testing.T) { + var hash int + + key := "Hello Algo" + + hash = addHash(key) + fmt.Println("Additive hash value is " + strconv.Itoa(hash)) + + hash = mulHash(key) + fmt.Println("Multiplicative hash value is " + strconv.Itoa(hash)) + + hash = xorHash(key) + fmt.Println("XOR hash value is " + strconv.Itoa(hash)) + + hash = rotHash(key) + fmt.Println("Rotational hash value is " + strconv.Itoa(hash)) +} diff --git a/en/codes/go/chapter_hashing/simple_hash.go b/en/codes/go/chapter_hashing/simple_hash.go new file mode 100644 index 000000000..23ec12db0 --- /dev/null +++ b/en/codes/go/chapter_hashing/simple_hash.go @@ -0,0 +1,55 @@ +// File: simple_hash.go +// Created Time: 2023-06-23 +// Author: Reanon (793584285@qq.com) + +package chapter_hashing + +import "fmt" + +/* Additive hash */ +func addHash(key string) int { + var hash int64 + var modulus int64 + + modulus = 1000000007 + for _, b := range []byte(key) { + hash = (hash + int64(b)) % modulus + } + return int(hash) +} + +/* Multiplicative hash */ +func mulHash(key string) int { + var hash int64 + var modulus int64 + + modulus = 1000000007 + for _, b := range []byte(key) { + hash = (31*hash + int64(b)) % modulus + } + return int(hash) +} + +/* XOR hash */ +func xorHash(key string) int { + hash := 0 + modulus := 1000000007 + for _, b := range []byte(key) { + fmt.Println(int(b)) + hash ^= int(b) + hash = (31*hash + int(b)) % modulus + } + return hash & modulus +} + +/* Rotational hash */ +func rotHash(key string) int { + var hash int64 + var modulus int64 + + modulus = 1000000007 + for _, b := range []byte(key) { + hash = ((hash << 4) ^ (hash >> 28) ^ int64(b)) % modulus + } + return int(hash) +} diff --git a/en/codes/go/chapter_heap/heap.go b/en/codes/go/chapter_heap/heap.go new file mode 100644 index 000000000..f614fad6b --- /dev/null +++ b/en/codes/go/chapter_heap/heap.go @@ -0,0 +1,45 @@ +// File: heap.go +// Created Time: 2023-01-12 +// Author: Reanon (793584285@qq.com) + +package chapter_heap + +// In Go, integer max heap can be built by implementing heap.Interface +// Implementing heap.Interface requires also implementing sort.Interface +type intHeap []any + +// Push function of heap.Interface, implements pushing element to heap +func (h *intHeap) Push(x any) { + // Push and Pop use pointer receiver as parameter + // Because they not only adjust the slice content, but also modify the slice length. + *h = append(*h, x.(int)) +} + +// Pop function of heap.Interface, implements popping heap top element +func (h *intHeap) Pop() any { + // Element to be popped is stored at the end + last := (*h)[len(*h)-1] + *h = (*h)[:len(*h)-1] + return last +} + +// Len function of sort.Interface +func (h *intHeap) Len() int { + return len(*h) +} + +// Less function of sort.Interface +func (h *intHeap) Less(i, j int) bool { + // If implementing min heap, need to change to less than sign + return (*h)[i].(int) > (*h)[j].(int) +} + +// Swap function of sort.Interface +func (h *intHeap) Swap(i, j int) { + (*h)[i], (*h)[j] = (*h)[j], (*h)[i] +} + +// Top gets heap top element +func (h *intHeap) Top() any { + return (*h)[0] +} diff --git a/en/codes/go/chapter_heap/heap_test.go b/en/codes/go/chapter_heap/heap_test.go new file mode 100644 index 000000000..5c49aa335 --- /dev/null +++ b/en/codes/go/chapter_heap/heap_test.go @@ -0,0 +1,101 @@ +// File: heap_test.go +// Created Time: 2023-01-12 +// Author: Reanon (793584285@qq.com) + +package chapter_heap + +import ( + "container/heap" + "fmt" + "strconv" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func testPush(h *intHeap, val int) { + // Call heap.Interface function to add element + heap.Push(h, val) + fmt.Printf("\nAfter element %d pushes to heap \n", val) + PrintHeap(*h) +} + +func testPop(h *intHeap) { + // Call heap.Interface function to remove element + val := heap.Pop(h) + fmt.Printf("\nAfter heap top element %d pops from heap \n", val) + PrintHeap(*h) +} + +func TestHeap(t *testing.T) { + /* Initialize heap */ + // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + maxHeap := &intHeap{} + heap.Init(maxHeap) + /* Element enters heap */ + testPush(maxHeap, 1) + testPush(maxHeap, 3) + testPush(maxHeap, 2) + testPush(maxHeap, 5) + testPush(maxHeap, 4) + + /* Check if heap is empty */ + top := maxHeap.Top() + fmt.Printf("Heap top element is %d\n", top) + + /* Time complexity is O(n), not O(nlogn) */ + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + + /* Get heap size */ + size := len(*maxHeap) + fmt.Printf("Heap size is %d\n", size) + + /* Check if heap is empty */ + isEmpty := len(*maxHeap) == 0 + fmt.Printf("Is heap empty %t\n", isEmpty) +} + +func TestMyHeap(t *testing.T) { + /* Initialize heap */ + // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + maxHeap := newMaxHeap([]any{9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2}) + fmt.Printf("After input array and building heap\n") + maxHeap.print() + + /* Check if heap is empty */ + peek := maxHeap.peek() + fmt.Printf("\nHeap top element is %d\n", peek) + + /* Element enters heap */ + val := 7 + maxHeap.push(val) + fmt.Printf("\nAfter element %d enters heap\n", val) + maxHeap.print() + + /* Time complexity is O(n), not O(nlogn) */ + peek = maxHeap.pop() + fmt.Printf("\nAfter heap top element %d exits heap\n", peek) + maxHeap.print() + + /* Get heap size */ + size := maxHeap.size() + fmt.Printf("\nHeap element count is %d\n", size) + + /* Check if heap is empty */ + isEmpty := maxHeap.isEmpty() + fmt.Printf("\nIs heap empty %t\n", isEmpty) +} + +func TestTopKHeap(t *testing.T) { + /* Initialize heap */ + // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + nums := []int{1, 7, 6, 3, 2} + k := 3 + res := topKHeap(nums, k) + fmt.Printf("The largest " + strconv.Itoa(k) + " elements are") + PrintHeap(*res) +} diff --git a/en/codes/go/chapter_heap/my_heap.go b/en/codes/go/chapter_heap/my_heap.go new file mode 100644 index 000000000..4c7a8cc52 --- /dev/null +++ b/en/codes/go/chapter_heap/my_heap.go @@ -0,0 +1,140 @@ +// File: my_heap.go +// Created Time: 2023-01-12 +// Author: Reanon (793584285@qq.com) + +package chapter_heap + +import ( + "fmt" + + . "github.com/krahets/hello-algo/pkg" +) + +type maxHeap struct { + // Use slice instead of array to avoid expansion issues + data []any +} + +/* Constructor, build empty heap */ +func newHeap() *maxHeap { + return &maxHeap{ + data: make([]any, 0), + } +} + +/* Constructor, build heap from slice */ +func newMaxHeap(nums []any) *maxHeap { + // Add list elements to heap as is + h := &maxHeap{data: nums} + for i := h.parent(len(h.data) - 1); i >= 0; i-- { + // Heapify all nodes except leaf nodes + h.siftDown(i) + } + return h +} + +/* Get index of left child node */ +func (h *maxHeap) left(i int) int { + return 2*i + 1 +} + +/* Get index of right child node */ +func (h *maxHeap) right(i int) int { + return 2*i + 2 +} + +/* Get index of parent node */ +func (h *maxHeap) parent(i int) int { + // Floor division + return (i - 1) / 2 +} + +/* Swap elements */ +func (h *maxHeap) swap(i, j int) { + h.data[i], h.data[j] = h.data[j], h.data[i] +} + +/* Get heap size */ +func (h *maxHeap) size() int { + return len(h.data) +} + +/* Check if heap is empty */ +func (h *maxHeap) isEmpty() bool { + return len(h.data) == 0 +} + +/* Access top element */ +func (h *maxHeap) peek() any { + return h.data[0] +} + +/* Element enters heap */ +func (h *maxHeap) push(val any) { + // Add node + h.data = append(h.data, val) + // Heapify from bottom to top + h.siftUp(len(h.data) - 1) +} + +/* Starting from node i, heapify from bottom to top */ +func (h *maxHeap) siftUp(i int) { + for true { + // Get parent node of node i + p := h.parent(i) + // When "crossing root node" or "node needs no repair", end heapify + if p < 0 || h.data[i].(int) <= h.data[p].(int) { + break + } + // Swap two nodes + h.swap(i, p) + // Loop upward heapify + i = p + } +} + +/* Element exits heap */ +func (h *maxHeap) pop() any { + // Handle empty case + if h.isEmpty() { + fmt.Println("error") + return nil + } + // Delete node + h.swap(0, h.size()-1) + // Remove node + val := h.data[len(h.data)-1] + h.data = h.data[:len(h.data)-1] + // Return top element + h.siftDown(0) + + // Return heap top element + return val +} + +/* Starting from node i, heapify from top to bottom */ +func (h *maxHeap) siftDown(i int) { + for true { + // Find node with maximum value among nodes i, l, r, denoted as max + l, r, max := h.left(i), h.right(i), i + if l < h.size() && h.data[l].(int) > h.data[max].(int) { + max = l + } + if r < h.size() && h.data[r].(int) > h.data[max].(int) { + max = r + } + // Swap two nodes + if max == i { + break + } + // Swap two nodes + h.swap(i, max) + // Loop downwards heapification + i = max + } +} + +/* Driver Code */ +func (h *maxHeap) print() { + PrintHeap(h.data) +} diff --git a/en/codes/go/chapter_heap/top_k.go b/en/codes/go/chapter_heap/top_k.go new file mode 100644 index 000000000..75adf5a93 --- /dev/null +++ b/en/codes/go/chapter_heap/top_k.go @@ -0,0 +1,51 @@ +// File: top_k.go +// Created Time: 2023-06-24 +// Author: Reanon (793584285@qq.com) + +package chapter_heap + +import "container/heap" + +type minHeap []any + +func (h *minHeap) Len() int { return len(*h) } +func (h *minHeap) Less(i, j int) bool { return (*h)[i].(int) < (*h)[j].(int) } +func (h *minHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } + +// Push method of heap.Interface, implements pushing element to heap +func (h *minHeap) Push(x any) { + *h = append(*h, x.(int)) +} + +// Pop method of heap.Interface, implements popping heap top element +func (h *minHeap) Pop() any { + // Element to be popped is stored at the end + last := (*h)[len(*h)-1] + *h = (*h)[:len(*h)-1] + return last +} + +// Top gets heap top element +func (h *minHeap) Top() any { + return (*h)[0] +} + +/* Find the largest k elements in array based on heap */ +func topKHeap(nums []int, k int) *minHeap { + // Python's heapq module implements min heap by default + h := &minHeap{} + heap.Init(h) + // Enter the first k elements of array into heap + for i := 0; i < k; i++ { + heap.Push(h, nums[i]) + } + // Starting from the (k+1)th element, maintain heap length as k + for i := k; i < len(nums); i++ { + // If current element is greater than top element, top element exits heap, current element enters heap + if nums[i] > h.Top().(int) { + heap.Pop(h) + heap.Push(h, nums[i]) + } + } + return h +} diff --git a/en/codes/go/chapter_searching/binary_search.go b/en/codes/go/chapter_searching/binary_search.go new file mode 100644 index 000000000..16217c985 --- /dev/null +++ b/en/codes/go/chapter_searching/binary_search.go @@ -0,0 +1,43 @@ +// File: binary_search.go +// Created Time: 2022-12-05 +// Author: Slone123c (274325721@qq.com) + +package chapter_searching + +/* Binary search (closed interval on both sides) */ +func binarySearch(nums []int, target int) int { + // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + i, j := 0, len(nums)-1 + // Loop, exit when the search interval is empty (empty when i > j) + for i <= j { + m := i + (j-i)/2 // Calculate the midpoint index m + if nums[m] < target { // This means target is in the interval [m+1, j] + i = m + 1 + } else if nums[m] > target { // This means target is in the interval [i, m-1] + j = m - 1 + } else { // Found the target element, return its index + return m + } + } + // Target element not found, return -1 + return -1 +} + +/* Binary search (left-closed right-open interval) */ +func binarySearchLCRO(nums []int, target int) int { + // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + i, j := 0, len(nums) + // Loop, exit when the search interval is empty (empty when i = j) + for i < j { + m := i + (j-i)/2 // Calculate the midpoint index m + if nums[m] < target { // This means target is in the interval [m+1, j) + i = m + 1 + } else if nums[m] > target { // This means target is in the interval [i, m) + j = m + } else { // Found the target element, return its index + return m + } + } + // Target element not found, return -1 + return -1 +} diff --git a/en/codes/go/chapter_searching/binary_search_edge.go b/en/codes/go/chapter_searching/binary_search_edge.go new file mode 100644 index 000000000..5725fbf43 --- /dev/null +++ b/en/codes/go/chapter_searching/binary_search_edge.go @@ -0,0 +1,31 @@ +// File: binary_search_edge.go +// Created Time: 2023-08-23 +// Author: Reanon (793584285@qq.com) + +package chapter_searching + +/* Binary search for the leftmost target */ +func binarySearchLeftEdge(nums []int, target int) int { + // Equivalent to finding the insertion point of target + i := binarySearchInsertion(nums, target) + // Target not found, return -1 + if i == len(nums) || nums[i] != target { + return -1 + } + // Found target, return index i + return i +} + +/* Binary search for the rightmost target */ +func binarySearchRightEdge(nums []int, target int) int { + // Convert to finding the leftmost target + 1 + i := binarySearchInsertion(nums, target+1) + // j points to the rightmost target, i points to the first element greater than target + j := i - 1 + // Target not found, return -1 + if j == -1 || nums[j] != target { + return -1 + } + // Found target, return index j + return j +} diff --git a/en/codes/go/chapter_searching/binary_search_insertion.go b/en/codes/go/chapter_searching/binary_search_insertion.go new file mode 100644 index 000000000..d01fc1dd7 --- /dev/null +++ b/en/codes/go/chapter_searching/binary_search_insertion.go @@ -0,0 +1,49 @@ +// File: binary_search_insertion.go +// Created Time: 2023-08-23 +// Author: Reanon (793584285@qq.com) + +package chapter_searching + +/* Binary search for insertion point (no duplicate elements) */ +func binarySearchInsertionSimple(nums []int, target int) int { + // Initialize closed interval [0, n-1] + i, j := 0, len(nums)-1 + for i <= j { + // Calculate the midpoint index m + m := i + (j-i)/2 + if nums[m] < target { + // target is in the interval [m+1, j] + i = m + 1 + } else if nums[m] > target { + // target is in the interval [i, m-1] + j = m - 1 + } else { + // Found target, return insertion point m + return m + } + } + // Target not found, return insertion point i + return i +} + +/* Binary search for insertion point (with duplicate elements) */ +func binarySearchInsertion(nums []int, target int) int { + // Initialize closed interval [0, n-1] + i, j := 0, len(nums)-1 + for i <= j { + // Calculate the midpoint index m + m := i + (j-i)/2 + if nums[m] < target { + // target is in the interval [m+1, j] + i = m + 1 + } else if nums[m] > target { + // target is in the interval [i, m-1] + j = m - 1 + } else { + // The first element less than target is in the interval [i, m-1] + j = m - 1 + } + } + // Return insertion point i + return i +} diff --git a/en/codes/go/chapter_searching/binary_search_test.go b/en/codes/go/chapter_searching/binary_search_test.go new file mode 100644 index 000000000..59f674b8c --- /dev/null +++ b/en/codes/go/chapter_searching/binary_search_test.go @@ -0,0 +1,61 @@ +// File: binary_search_test.go +// Created Time: 2022-12-05 +// Author: Slone123c (274325721@qq.com) + +package chapter_searching + +import ( + "fmt" + "testing" +) + +func TestBinarySearch(t *testing.T) { + var ( + target = 6 + nums = []int{1, 3, 6, 8, 12, 15, 23, 26, 31, 35} + expected = 2 + ) + // Perform binary search in array + actual := binarySearch(nums, target) + fmt.Println("Index of target element 6 =", actual) + if actual != expected { + t.Errorf("Index of target element 6 = %d, should be %d", actual, expected) + } +} + +func TestBinarySearchEdge(t *testing.T) { + // Array with duplicate elements + nums := []int{1, 3, 6, 8, 12, 15, 23, 26, 31, 35} + fmt.Println("\nArray nums = ", nums) + + // Binary search left and right boundaries + for _, target := range []int{6, 7} { + index := binarySearchLeftEdge(nums, target) + fmt.Println("Leftmost element", target, " index is", index) + + index = binarySearchRightEdge(nums, target) + fmt.Println("Rightmost element", target, " index is", index) + } +} + +func TestBinarySearchInsertion(t *testing.T) { + // Array without duplicate elements + nums := []int{1, 3, 6, 8, 12, 15, 23, 26, 31, 35} + fmt.Println("Array nums =", nums) + + // Binary search for insertion point + for _, target := range []int{6, 9} { + index := binarySearchInsertionSimple(nums, target) + fmt.Println("Element", target, " insertion point index is", index) + } + + // Array with duplicate elements + nums = []int{1, 3, 6, 6, 6, 6, 6, 10, 12, 15} + fmt.Println("\nArray nums =", nums) + + // Binary search for insertion point + for _, target := range []int{2, 6, 20} { + index := binarySearchInsertion(nums, target) + fmt.Println("Element", target, " insertion point index is", index) + } +} diff --git a/en/codes/go/chapter_searching/hashing_search.go b/en/codes/go/chapter_searching/hashing_search.go new file mode 100644 index 000000000..e98119afa --- /dev/null +++ b/en/codes/go/chapter_searching/hashing_search.go @@ -0,0 +1,29 @@ +// File: hashing_search.go +// Created Time: 2022-12-12 +// Author: Slone123c (274325721@qq.com) + +package chapter_searching + +import . "github.com/krahets/hello-algo/pkg" + +/* Hash search (array) */ +func hashingSearchArray(m map[int]int, target int) int { + // Hash table's key: target element, value: index + // If this key does not exist in the hash table, return -1 + if index, ok := m[target]; ok { + return index + } else { + return -1 + } +} + +/* Hash search (linked list) */ +func hashingSearchLinkedList(m map[int]*ListNode, target int) *ListNode { + // Hash table key: target node value, value: node object + // Return nil if key does not exist in hash table + if node, ok := m[target]; ok { + return node + } else { + return nil + } +} diff --git a/en/codes/go/chapter_searching/hashing_search_test.go b/en/codes/go/chapter_searching/hashing_search_test.go new file mode 100644 index 000000000..54f816208 --- /dev/null +++ b/en/codes/go/chapter_searching/hashing_search_test.go @@ -0,0 +1,36 @@ +// File: hashing_search_test.go +// Created Time: 2022-12-12 +// Author: Slone123c (274325721@qq.com) + +package chapter_searching + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestHashingSearch(t *testing.T) { + target := 3 + /* Hash search (array) */ + nums := []int{1, 5, 3, 2, 4, 7, 5, 9, 10, 8} + // Initialize hash table + m := make(map[int]int) + for i := 0; i < len(nums); i++ { + m[nums[i]] = i + } + index := hashingSearchArray(m, target) + fmt.Println("Index of target element 3 = ", index) + + /* Hash search (linked list) */ + head := ArrayToLinkedList(nums) + // Initialize hash table + m1 := make(map[int]*ListNode) + for head != nil { + m1[head.Val] = head + head = head.Next + } + node := hashingSearchLinkedList(m1, target) + fmt.Println("Node object corresponding to target node value 3 is ", node) +} diff --git a/en/codes/go/chapter_searching/linear_search.go b/en/codes/go/chapter_searching/linear_search.go new file mode 100644 index 000000000..8949af1e2 --- /dev/null +++ b/en/codes/go/chapter_searching/linear_search.go @@ -0,0 +1,36 @@ +// File: linear_search.go +// Created Time: 2022-11-25 +// Author: Reanon (793584285@qq.com) + +package chapter_searching + +import ( + . "github.com/krahets/hello-algo/pkg" +) + +/* Linear search (array) */ +func linearSearchArray(nums []int, target int) int { + // Traverse array + for i := 0; i < len(nums); i++ { + // Found the target element, return its index + if nums[i] == target { + return i + } + } + // Target element not found, return -1 + return -1 +} + +/* Linear search (linked list) */ +func linearSearchLinkedList(node *ListNode, target int) *ListNode { + // Traverse the linked list + for node != nil { + // Found the target node, return it + if node.Val == target { + return node + } + node = node.Next + } + // Target element not found, return nil + return nil +} diff --git a/en/codes/go/chapter_searching/linear_search_test.go b/en/codes/go/chapter_searching/linear_search_test.go new file mode 100644 index 000000000..27beb8e87 --- /dev/null +++ b/en/codes/go/chapter_searching/linear_search_test.go @@ -0,0 +1,26 @@ +// File: linear_search_test.go +// Created Time: 2022-11-25 +// Author: Reanon (793584285@qq.com) + +package chapter_searching + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestLinearSearch(t *testing.T) { + target := 3 + nums := []int{1, 5, 3, 2, 4, 7, 5, 9, 10, 8} + + // Perform linear search in array + index := linearSearchArray(nums, target) + fmt.Println("Index of target element 3 =", index) + + // Perform linear search in linked list + head := ArrayToLinkedList(nums) + node := linearSearchLinkedList(head, target) + fmt.Println("Node object with target value 3 is", node) +} diff --git a/en/codes/go/chapter_searching/two_sum.go b/en/codes/go/chapter_searching/two_sum.go new file mode 100644 index 000000000..d73188a0c --- /dev/null +++ b/en/codes/go/chapter_searching/two_sum.go @@ -0,0 +1,33 @@ +// File: two_sum.go +// Created Time: 2022-11-25 +// Author: reanon (793584285@qq.com) + +package chapter_searching + +/* Method 1: Brute force enumeration */ +func twoSumBruteForce(nums []int, target int) []int { + size := len(nums) + // Two nested loops, time complexity is O(n^2) + for i := 0; i < size-1; i++ { + for j := i + 1; j < size; j++ { + if nums[i]+nums[j] == target { + return []int{i, j} + } + } + } + return nil +} + +/* Method 2: Auxiliary hash table */ +func twoSumHashTable(nums []int, target int) []int { + // Auxiliary hash table, space complexity is O(n) + hashTable := map[int]int{} + // Single loop, time complexity is O(n) + for idx, val := range nums { + if preIdx, ok := hashTable[target-val]; ok { + return []int{preIdx, idx} + } + hashTable[val] = idx + } + return nil +} diff --git a/en/codes/go/chapter_searching/two_sum_test.go b/en/codes/go/chapter_searching/two_sum_test.go new file mode 100644 index 000000000..1f2895bc0 --- /dev/null +++ b/en/codes/go/chapter_searching/two_sum_test.go @@ -0,0 +1,24 @@ +// File: two_sum_test.go +// Created Time: 2022-11-25 +// Author: reanon (793584285@qq.com) + +package chapter_searching + +import ( + "fmt" + "testing" +) + +func TestTwoSum(t *testing.T) { + // ======= Test Case ======= + nums := []int{2, 7, 11, 15} + target := 13 + + // ====== Driver Code ====== + // Method 1: Brute-force approach + res := twoSumBruteForce(nums, target) + fmt.Println("Method 1 res =", res) + // Method 2: Hash table + res = twoSumHashTable(nums, target) + fmt.Println("Method 2 res =", res) +} diff --git a/en/codes/go/chapter_sorting/bubble_sort.go b/en/codes/go/chapter_sorting/bubble_sort.go new file mode 100644 index 000000000..91b62575d --- /dev/null +++ b/en/codes/go/chapter_sorting/bubble_sort.go @@ -0,0 +1,38 @@ +// File: bubble_sort.go +// Created Time: 2022-12-06 +// Author: Slone123c (274325721@qq.com) + +package chapter_sorting + +/* Bubble sort */ +func bubbleSort(nums []int) { + // Outer loop: unsorted range is [0, i] + for i := len(nums) - 1; i > 0; i-- { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for j := 0; j < i; j++ { + if nums[j] > nums[j+1] { + // Swap nums[j] and nums[j + 1] + nums[j], nums[j+1] = nums[j+1], nums[j] + } + } + } +} + +/* Bubble sort (flag optimization) */ +func bubbleSortWithFlag(nums []int) { + // Outer loop: unsorted range is [0, i] + for i := len(nums) - 1; i > 0; i-- { + flag := false // Initialize flag + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for j := 0; j < i; j++ { + if nums[j] > nums[j+1] { + // Swap nums[j] and nums[j + 1] + nums[j], nums[j+1] = nums[j+1], nums[j] + flag = true // Record element swap + } + } + if flag == false { // No elements were swapped in this round of "bubbling", exit directly + break + } + } +} diff --git a/en/codes/go/chapter_sorting/bubble_sort_test.go b/en/codes/go/chapter_sorting/bubble_sort_test.go new file mode 100644 index 000000000..358e46b25 --- /dev/null +++ b/en/codes/go/chapter_sorting/bubble_sort_test.go @@ -0,0 +1,20 @@ +// File: bubble_sort_test.go +// Created Time: 2022-12-06 +// Author: Slone123c (274325721@qq.com) + +package chapter_sorting + +import ( + "fmt" + "testing" +) + +func TestBubbleSort(t *testing.T) { + nums := []int{4, 1, 3, 1, 5, 2} + bubbleSort(nums) + fmt.Println("After bubble sort completes, nums = ", nums) + + nums1 := []int{4, 1, 3, 1, 5, 2} + bubbleSortWithFlag(nums1) + fmt.Println("After bubble sort completes, nums1 = ", nums1) +} diff --git a/en/codes/go/chapter_sorting/bucket_sort.go b/en/codes/go/chapter_sorting/bucket_sort.go new file mode 100644 index 000000000..81dcdcbf5 --- /dev/null +++ b/en/codes/go/chapter_sorting/bucket_sort.go @@ -0,0 +1,37 @@ +// File: bucket_sort.go +// Created Time: 2023-03-27 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +import "sort" + +/* Bucket sort */ +func bucketSort(nums []float64) { + // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + k := len(nums) / 2 + buckets := make([][]float64, k) + for i := 0; i < k; i++ { + buckets[i] = make([]float64, 0) + } + // 1. Distribute array elements into various buckets + for _, num := range nums { + // Input data range is [0, 1), use num * k to map to index range [0, k-1] + i := int(num * float64(k)) + // Add num to bucket i + buckets[i] = append(buckets[i], num) + } + // 2. Sort each bucket + for i := 0; i < k; i++ { + // Use built-in slice sorting function, can also be replaced with other sorting algorithms + sort.Float64s(buckets[i]) + } + // 3. Traverse buckets to merge results + i := 0 + for _, bucket := range buckets { + for _, num := range bucket { + nums[i] = num + i++ + } + } +} diff --git a/en/codes/go/chapter_sorting/bucket_sort_test.go b/en/codes/go/chapter_sorting/bucket_sort_test.go new file mode 100644 index 000000000..95c791f95 --- /dev/null +++ b/en/codes/go/chapter_sorting/bucket_sort_test.go @@ -0,0 +1,17 @@ +// File: bucket_sort_test.go +// Created Time: 2023-03-27 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +import ( + "fmt" + "testing" +) + +func TestBucketSort(t *testing.T) { + // Assume input data is floating point, interval [0, 1) + nums := []float64{0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37} + bucketSort(nums) + fmt.Println("After bucket sort completes, nums = ", nums) +} diff --git a/en/codes/go/chapter_sorting/counting_sort.go b/en/codes/go/chapter_sorting/counting_sort.go new file mode 100644 index 000000000..636d9539c --- /dev/null +++ b/en/codes/go/chapter_sorting/counting_sort.go @@ -0,0 +1,68 @@ +// File: counting_sort.go +// Created Time: 2023-03-20 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +type CountingSort struct{} + +/* Counting sort */ +// Simple implementation, cannot be used for sorting objects +func countingSortNaive(nums []int) { + // 1. Count the maximum element m in the array + m := 0 + for _, num := range nums { + if num > m { + m = num + } + } + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + counter := make([]int, m+1) + for _, num := range nums { + counter[num]++ + } + // 3. Traverse counter, filling each element back into the original array nums + for i, num := 0, 0; num < m+1; num++ { + for j := 0; j < counter[num]; j++ { + nums[i] = num + i++ + } + } +} + +/* Counting sort */ +// Complete implementation, can sort objects and is a stable sort +func countingSort(nums []int) { + // 1. Count the maximum element m in the array + m := 0 + for _, num := range nums { + if num > m { + m = num + } + } + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + counter := make([]int, m+1) + for _, num := range nums { + counter[num]++ + } + // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + // counter[num]-1 is the last index where num appears in res + for i := 0; i < m; i++ { + counter[i+1] += counter[i] + } + // 4. Traverse nums in reverse order, placing each element into the result array res + // Initialize the array res to record results + n := len(nums) + res := make([]int, n) + for i := n - 1; i >= 0; i-- { + num := nums[i] + // Place num at the corresponding index + res[counter[num]-1] = num + // Decrement the prefix sum by 1, getting the next index to place num + counter[num]-- + } + // Use result array res to overwrite the original array nums + copy(nums, res) +} diff --git a/en/codes/go/chapter_sorting/counting_sort_test.go b/en/codes/go/chapter_sorting/counting_sort_test.go new file mode 100644 index 000000000..f3b15ccf7 --- /dev/null +++ b/en/codes/go/chapter_sorting/counting_sort_test.go @@ -0,0 +1,20 @@ +// File: counting_sort_test.go +// Created Time: 2023-03-20 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +import ( + "fmt" + "testing" +) + +func TestCountingSort(t *testing.T) { + nums := []int{1, 0, 1, 2, 0, 4, 0, 2, 2, 4} + countingSortNaive(nums) + fmt.Println("After counting sort (cannot sort objects) completes, nums = ", nums) + + nums1 := []int{1, 0, 1, 2, 0, 4, 0, 2, 2, 4} + countingSort(nums1) + fmt.Println("After counting sort completes, nums1 = ", nums1) +} diff --git a/en/codes/go/chapter_sorting/heap_sort.go b/en/codes/go/chapter_sorting/heap_sort.go new file mode 100644 index 000000000..47b0c7d1b --- /dev/null +++ b/en/codes/go/chapter_sorting/heap_sort.go @@ -0,0 +1,44 @@ +// File: heap_sort.go +// Created Time: 2023-05-29 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +/* Heap length is n, start heapifying node i, from top to bottom */ +func siftDown(nums *[]int, n, i int) { + for true { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + l := 2*i + 1 + r := 2*i + 2 + ma := i + if l < n && (*nums)[l] > (*nums)[ma] { + ma = l + } + if r < n && (*nums)[r] > (*nums)[ma] { + ma = r + } + // Swap two nodes + if ma == i { + break + } + // Swap two nodes + (*nums)[i], (*nums)[ma] = (*nums)[ma], (*nums)[i] + // Loop downwards heapification + i = ma + } +} + +/* Heap sort */ +func heapSort(nums *[]int) { + // Build heap operation: heapify all nodes except leaves + for i := len(*nums)/2 - 1; i >= 0; i-- { + siftDown(nums, len(*nums), i) + } + // Extract the largest element from the heap and repeat for n-1 rounds + for i := len(*nums) - 1; i > 0; i-- { + // Delete node + (*nums)[0], (*nums)[i] = (*nums)[i], (*nums)[0] + // Start heapifying the root node, from top to bottom + siftDown(nums, i, 0) + } +} diff --git a/en/codes/go/chapter_sorting/heap_sort_test.go b/en/codes/go/chapter_sorting/heap_sort_test.go new file mode 100644 index 000000000..123ebfa83 --- /dev/null +++ b/en/codes/go/chapter_sorting/heap_sort_test.go @@ -0,0 +1,16 @@ +// File: heap_sort_test.go +// Created Time: 2023-05-29 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +import ( + "fmt" + "testing" +) + +func TestHeapSort(t *testing.T) { + nums := []int{4, 1, 3, 1, 5, 2} + heapSort(&nums) + fmt.Println("After heap sort completes, nums = ", nums) +} diff --git a/en/codes/go/chapter_sorting/insertion_sort.go b/en/codes/go/chapter_sorting/insertion_sort.go new file mode 100644 index 000000000..6d13bb6ff --- /dev/null +++ b/en/codes/go/chapter_sorting/insertion_sort.go @@ -0,0 +1,20 @@ +// File: insertion_sort.go +// Created Time: 2022-12-12 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_sorting + +/* Insertion sort */ +func insertionSort(nums []int) { + // Outer loop: sorted interval is [0, i-1] + for i := 1; i < len(nums); i++ { + base := nums[i] + j := i - 1 + // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + for j >= 0 && nums[j] > base { + nums[j+1] = nums[j] // Move nums[j] to the right by one position + j-- + } + nums[j+1] = base // Assign base to the correct position + } +} diff --git a/en/codes/go/chapter_sorting/insertion_sort_test.go b/en/codes/go/chapter_sorting/insertion_sort_test.go new file mode 100644 index 000000000..5cf0424b9 --- /dev/null +++ b/en/codes/go/chapter_sorting/insertion_sort_test.go @@ -0,0 +1,16 @@ +// File: insertion_sort_test.go +// Created Time: 2022-12-12 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_sorting + +import ( + "fmt" + "testing" +) + +func TestInsertionSort(t *testing.T) { + nums := []int{4, 1, 3, 1, 5, 2} + insertionSort(nums) + fmt.Println("After insertion sort, nums =", nums) +} diff --git a/en/codes/go/chapter_sorting/merge_sort.go b/en/codes/go/chapter_sorting/merge_sort.go new file mode 100644 index 000000000..a2d9727ea --- /dev/null +++ b/en/codes/go/chapter_sorting/merge_sort.go @@ -0,0 +1,54 @@ +// File: merge_sort.go +// Created Time: 2022-12-13 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_sorting + +/* Merge left subarray and right subarray */ +func merge(nums []int, left, mid, right int) { + // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + // Create a temporary array tmp to store the merged results + tmp := make([]int, right-left+1) + // Initialize the start indices of the left and right subarrays + i, j, k := left, mid+1, 0 + // While both subarrays still have elements, compare and copy the smaller element into the temporary array + for i <= mid && j <= right { + if nums[i] <= nums[j] { + tmp[k] = nums[i] + i++ + } else { + tmp[k] = nums[j] + j++ + } + k++ + } + // Copy the remaining elements of the left and right subarrays into the temporary array + for i <= mid { + tmp[k] = nums[i] + i++ + k++ + } + for j <= right { + tmp[k] = nums[j] + j++ + k++ + } + // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + for k := 0; k < len(tmp); k++ { + nums[left+k] = tmp[k] + } +} + +/* Merge sort */ +func mergeSort(nums []int, left, right int) { + // Termination condition + if left >= right { + return + } + // Divide and conquer stage + mid := left + (right - left) / 2 + mergeSort(nums, left, mid) + mergeSort(nums, mid+1, right) + // Merge stage + merge(nums, left, mid, right) +} diff --git a/en/codes/go/chapter_sorting/merge_sort_test.go b/en/codes/go/chapter_sorting/merge_sort_test.go new file mode 100644 index 000000000..9c4932b91 --- /dev/null +++ b/en/codes/go/chapter_sorting/merge_sort_test.go @@ -0,0 +1,16 @@ +// File: merge_sort_test.go +// Created Time: 2022-12-13 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_sorting + +import ( + "fmt" + "testing" +) + +func TestMergeSort(t *testing.T) { + nums := []int{7, 3, 2, 6, 0, 1, 5, 4} + mergeSort(nums, 0, len(nums)-1) + fmt.Println("After merge sort completes, nums = ", nums) +} diff --git a/en/codes/go/chapter_sorting/quick_sort.go b/en/codes/go/chapter_sorting/quick_sort.go new file mode 100644 index 000000000..b0f0f83aa --- /dev/null +++ b/en/codes/go/chapter_sorting/quick_sort.go @@ -0,0 +1,130 @@ +// File: quick_sort.go +// Created Time: 2022-12-12 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_sorting + +// Quick sort +type quickSort struct{} + +// Quick sort (recursion depth optimization) +type quickSortMedian struct{} + +// Quick sort (recursion depth optimization) +type quickSortTailCall struct{} + +/* Sentinel partition */ +func (q *quickSort) partition(nums []int, left, right int) int { + // Use nums[left] as the pivot + i, j := left, right + for i < j { + for i < j && nums[j] >= nums[left] { + j-- // Search from right to left for the first element smaller than the pivot + } + for i < j && nums[i] <= nums[left] { + i++ // Search from left to right for the first element greater than the pivot + } + // Swap elements + nums[i], nums[j] = nums[j], nums[i] + } + // Swap the pivot to the boundary between the two subarrays + nums[i], nums[left] = nums[left], nums[i] + return i // Return the index of the pivot +} + +/* Quick sort */ +func (q *quickSort) quickSort(nums []int, left, right int) { + // Terminate recursion when subarray length is 1 + if left >= right { + return + } + // Sentinel partition + pivot := q.partition(nums, left, right) + // Recursively process the left subarray and right subarray + q.quickSort(nums, left, pivot-1) + q.quickSort(nums, pivot+1, right) +} + +/* Select the median of three candidate elements */ +func (q *quickSortMedian) medianThree(nums []int, left, mid, right int) int { + l, m, r := nums[left], nums[mid], nums[right] + if (l <= m && m <= r) || (r <= m && m <= l) { + return mid // m is between l and r + } + if (m <= l && l <= r) || (r <= l && l <= m) { + return left // l is between m and r + } + return right +} + +/* Sentinel partition (median of three) */ +func (q *quickSortMedian) partition(nums []int, left, right int) int { + // Use nums[left] as the pivot + med := q.medianThree(nums, left, (left+right)/2, right) + // Swap the median to the array's leftmost position + nums[left], nums[med] = nums[med], nums[left] + // Use nums[left] as the pivot + i, j := left, right + for i < j { + for i < j && nums[j] >= nums[left] { + j-- // Search from right to left for the first element smaller than the pivot + } + for i < j && nums[i] <= nums[left] { + i++ // Search from left to right for the first element greater than the pivot + } + // Swap elements + nums[i], nums[j] = nums[j], nums[i] + } + // Swap the pivot to the boundary between the two subarrays + nums[i], nums[left] = nums[left], nums[i] + return i // Return the index of the pivot +} + +/* Quick sort */ +func (q *quickSortMedian) quickSort(nums []int, left, right int) { + // Terminate recursion when subarray length is 1 + if left >= right { + return + } + // Sentinel partition + pivot := q.partition(nums, left, right) + // Recursively process the left subarray and right subarray + q.quickSort(nums, left, pivot-1) + q.quickSort(nums, pivot+1, right) +} + +/* Sentinel partition */ +func (q *quickSortTailCall) partition(nums []int, left, right int) int { + // Use nums[left] as the pivot + i, j := left, right + for i < j { + for i < j && nums[j] >= nums[left] { + j-- // Search from right to left for the first element smaller than the pivot + } + for i < j && nums[i] <= nums[left] { + i++ // Search from left to right for the first element greater than the pivot + } + // Swap elements + nums[i], nums[j] = nums[j], nums[i] + } + // Swap the pivot to the boundary between the two subarrays + nums[i], nums[left] = nums[left], nums[i] + return i // Return the index of the pivot +} + +/* Quick sort (recursion depth optimization) */ +func (q *quickSortTailCall) quickSort(nums []int, left, right int) { + // Terminate when subarray length is 1 + for left < right { + // Sentinel partition operation + pivot := q.partition(nums, left, right) + // Perform quick sort on the shorter of the two subarrays + if pivot-left < right-pivot { + q.quickSort(nums, left, pivot-1) // Recursively sort the left subarray + left = pivot + 1 // Remaining unsorted interval is [pivot + 1, right] + } else { + q.quickSort(nums, pivot+1, right) // Recursively sort the right subarray + right = pivot - 1 // Remaining unsorted interval is [left, pivot - 1] + } + } +} diff --git a/en/codes/go/chapter_sorting/quick_sort_test.go b/en/codes/go/chapter_sorting/quick_sort_test.go new file mode 100644 index 000000000..1e75185a2 --- /dev/null +++ b/en/codes/go/chapter_sorting/quick_sort_test.go @@ -0,0 +1,34 @@ +// File: quick_sort_test.go +// Created Time: 2022-12-12 +// Author: msk397 (machangxinq@gmail.com) + +package chapter_sorting + +import ( + "fmt" + "testing" +) + +// Quick sort +func TestQuickSort(t *testing.T) { + q := quickSort{} + nums := []int{4, 1, 3, 1, 5, 2} + q.quickSort(nums, 0, len(nums)-1) + fmt.Println("After quick sort completes, nums = ", nums) +} + +// Quick sort (recursion depth optimization) +func TestQuickSortMedian(t *testing.T) { + q := quickSortMedian{} + nums := []int{4, 1, 3, 1, 5, 2} + q.quickSort(nums, 0, len(nums)-1) + fmt.Println("After quick sort (median pivot optimization), nums = ", nums) +} + +// Quick sort (recursion depth optimization) +func TestQuickSortTailCall(t *testing.T) { + q := quickSortTailCall{} + nums := []int{4, 1, 3, 1, 5, 2} + q.quickSort(nums, 0, len(nums)-1) + fmt.Println("After quick sort (recursion depth optimization), nums = ", nums) +} diff --git a/en/codes/go/chapter_sorting/radix_sort.go b/en/codes/go/chapter_sorting/radix_sort.go new file mode 100644 index 000000000..a0cd41855 --- /dev/null +++ b/en/codes/go/chapter_sorting/radix_sort.go @@ -0,0 +1,60 @@ +// File: radix_sort.go +// Created Time: 2023-01-18 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +import "math" + +/* Get the k-th digit of element num, where exp = 10^(k-1) */ +func digit(num, exp int) int { + // Passing exp instead of k can avoid repeated expensive exponentiation here + return (num / exp) % 10 +} + +/* Counting sort (based on nums k-th digit) */ +func countingSortDigit(nums []int, exp int) { + // Decimal digit range is 0~9, therefore need a bucket array of length 10 + counter := make([]int, 10) + n := len(nums) + // Count the occurrence of digits 0~9 + for i := 0; i < n; i++ { + d := digit(nums[i], exp) // Get the k-th digit of nums[i], noted as d + counter[d]++ // Count the occurrence of digit d + } + // Calculate prefix sum, converting "occurrence count" into "array index" + for i := 1; i < 10; i++ { + counter[i] += counter[i-1] + } + // Traverse in reverse, based on bucket statistics, place each element into res + res := make([]int, n) + for i := n - 1; i >= 0; i-- { + d := digit(nums[i], exp) + j := counter[d] - 1 // Get the index j for d in the array + res[j] = nums[i] // Place the current element at index j + counter[d]-- // Decrease the count of d by 1 + } + // Use result to overwrite the original array nums + for i := 0; i < n; i++ { + nums[i] = res[i] + } +} + +/* Radix sort */ +func radixSort(nums []int) { + // Get the maximum element of the array, used to determine the maximum number of digits + max := math.MinInt + for _, num := range nums { + if num > max { + max = num + } + } + // Traverse from the lowest to the highest digit + for exp := 1; max >= exp; exp *= 10 { + // Perform counting sort on the k-th digit of array elements + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // i.e., exp = 10^(k-1) + countingSortDigit(nums, exp) + } +} diff --git a/en/codes/go/chapter_sorting/radix_sort_test.go b/en/codes/go/chapter_sorting/radix_sort_test.go new file mode 100644 index 000000000..b3a550562 --- /dev/null +++ b/en/codes/go/chapter_sorting/radix_sort_test.go @@ -0,0 +1,18 @@ +// File: radix_sort_test.go +// Created Time: 2023-01-18 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +import ( + "fmt" + "testing" +) + +func TestRadixSort(t *testing.T) { + /* Radix sort */ + nums := []int{10546151, 35663510, 42865989, 34862445, 81883077, + 88906420, 72429244, 30524779, 82060337, 63832996} + radixSort(nums) + fmt.Println("After radix sort completes, nums = ", nums) +} diff --git a/en/codes/go/chapter_sorting/selection_sort.go b/en/codes/go/chapter_sorting/selection_sort.go new file mode 100644 index 000000000..ecc83849e --- /dev/null +++ b/en/codes/go/chapter_sorting/selection_sort.go @@ -0,0 +1,24 @@ +// File: selection_sort.go +// Created Time: 2023-05-29 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +/* Selection sort */ +func selectionSort(nums []int) { + n := len(nums) + // Outer loop: unsorted interval is [i, n-1] + for i := 0; i < n-1; i++ { + // Inner loop: find the smallest element within the unsorted interval + k := i + for j := i + 1; j < n; j++ { + if nums[j] < nums[k] { + // Record the index of the smallest element + k = j + } + } + // Swap the smallest element with the first element of the unsorted interval + nums[i], nums[k] = nums[k], nums[i] + + } +} diff --git a/en/codes/go/chapter_sorting/selection_sort_test.go b/en/codes/go/chapter_sorting/selection_sort_test.go new file mode 100644 index 000000000..88e0723b4 --- /dev/null +++ b/en/codes/go/chapter_sorting/selection_sort_test.go @@ -0,0 +1,16 @@ +// File: selection_sort_test.go +// Created Time: 2023-05-29 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +import ( + "fmt" + "testing" +) + +func TestSelectionSort(t *testing.T) { + nums := []int{4, 1, 3, 1, 5, 2} + selectionSort(nums) + fmt.Println("After selection sort completes, nums = ", nums) +} diff --git a/en/codes/go/chapter_stack_and_queue/array_deque.go b/en/codes/go/chapter_stack_and_queue/array_deque.go new file mode 100644 index 000000000..6a5685d0d --- /dev/null +++ b/en/codes/go/chapter_stack_and_queue/array_deque.go @@ -0,0 +1,121 @@ +// File: array_deque.go +// Created Time: 2023-03-13 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import "fmt" + +/* Double-ended queue based on circular array implementation */ +type arrayDeque struct { + nums []int // Array for storing double-ended queue elements + front int // Front pointer, points to the front of the queue element + queSize int // Double-ended queue length + queCapacity int // Queue capacity (maximum number of elements) +} + +/* Access front of the queue element */ +func newArrayDeque(queCapacity int) *arrayDeque { + return &arrayDeque{ + nums: make([]int, queCapacity), + queCapacity: queCapacity, + front: 0, + queSize: 0, + } +} + +/* Get the length of the double-ended queue */ +func (q *arrayDeque) size() int { + return q.queSize +} + +/* Check if the double-ended queue is empty */ +func (q *arrayDeque) isEmpty() bool { + return q.queSize == 0 +} + +/* Calculate circular array index */ +func (q *arrayDeque) index(i int) int { + // Use modulo operation to wrap the array head and tail together + // When i passes the tail of the array, return to the head + // When i passes the head of the array, return to the tail + return (i + q.queCapacity) % q.queCapacity +} + +/* Front of the queue enqueue */ +func (q *arrayDeque) pushFirst(num int) { + if q.queSize == q.queCapacity { + fmt.Println("Double-ended queue is full") + return + } + // Use modulo operation to wrap front around to the tail after passing the head of the array + // Add num to the front of the queue + q.front = q.index(q.front - 1) + // Add num to front of queue + q.nums[q.front] = num + q.queSize++ +} + +/* Rear of the queue enqueue */ +func (q *arrayDeque) pushLast(num int) { + if q.queSize == q.queCapacity { + fmt.Println("Double-ended queue is full") + return + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + rear := q.index(q.front + q.queSize) + // Front pointer moves one position backward + q.nums[rear] = num + q.queSize++ +} + +/* Rear of the queue dequeue */ +func (q *arrayDeque) popFirst() any { + num := q.peekFirst() + if num == nil { + return nil + } + // Move front pointer backward by one position + q.front = q.index(q.front + 1) + q.queSize-- + return num +} + +/* Access rear of the queue element */ +func (q *arrayDeque) popLast() any { + num := q.peekLast() + if num == nil { + return nil + } + q.queSize-- + return num +} + +/* Return list for printing */ +func (q *arrayDeque) peekFirst() any { + if q.isEmpty() { + return nil + } + return q.nums[q.front] +} + +/* Driver Code */ +func (q *arrayDeque) peekLast() any { + if q.isEmpty() { + return nil + } + // Initialize double-ended queue + last := q.index(q.front + q.queSize - 1) + return q.nums[last] +} + +/* Get Slice for printing */ +func (q *arrayDeque) toSlice() []int { + // Elements enqueue + res := make([]int, q.queSize) + for i, j := 0, q.front; i < q.queSize; i++ { + res[i] = q.nums[q.index(j)] + j++ + } + return res +} diff --git a/en/codes/go/chapter_stack_and_queue/array_queue.go b/en/codes/go/chapter_stack_and_queue/array_queue.go new file mode 100644 index 000000000..735adc5d5 --- /dev/null +++ b/en/codes/go/chapter_stack_and_queue/array_queue.go @@ -0,0 +1,78 @@ +// File: array_queue.go +// Created Time: 2022-11-28 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +/* Queue based on circular array implementation */ +type arrayQueue struct { + nums []int // Array for storing queue elements + front int // Front pointer, points to the front of the queue element + queSize int // Queue length + queCapacity int // Queue capacity (maximum number of elements) +} + +/* Access front of the queue element */ +func newArrayQueue(queCapacity int) *arrayQueue { + return &arrayQueue{ + nums: make([]int, queCapacity), + queCapacity: queCapacity, + front: 0, + queSize: 0, + } +} + +/* Get the length of the queue */ +func (q *arrayQueue) size() int { + return q.queSize +} + +/* Check if the queue is empty */ +func (q *arrayQueue) isEmpty() bool { + return q.queSize == 0 +} + +/* Enqueue */ +func (q *arrayQueue) push(num int) { + // When rear == queCapacity, queue is full + if q.queSize == q.queCapacity { + return + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Add num to the rear of the queue + rear := (q.front + q.queSize) % q.queCapacity + // Front pointer moves one position backward + q.nums[rear] = num + q.queSize++ +} + +/* Dequeue */ +func (q *arrayQueue) pop() any { + num := q.peek() + if num == nil { + return nil + } + + // Move front pointer backward by one position, if it passes the tail, return to array head + q.front = (q.front + 1) % q.queCapacity + q.queSize-- + return num +} + +/* Return list for printing */ +func (q *arrayQueue) peek() any { + if q.isEmpty() { + return nil + } + return q.nums[q.front] +} + +/* Get Slice for printing */ +func (q *arrayQueue) toSlice() []int { + rear := (q.front + q.queSize) + if rear >= q.queCapacity { + rear %= q.queCapacity + return append(q.nums[q.front:], q.nums[:rear]...) + } + return q.nums[q.front:rear] +} diff --git a/en/codes/go/chapter_stack_and_queue/array_stack.go b/en/codes/go/chapter_stack_and_queue/array_stack.go new file mode 100644 index 000000000..ca5add85c --- /dev/null +++ b/en/codes/go/chapter_stack_and_queue/array_stack.go @@ -0,0 +1,55 @@ +// File: array_stack.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +/* Stack based on array implementation */ +type arrayStack struct { + data []int // Data +} + +/* Access top of the stack element */ +func newArrayStack() *arrayStack { + return &arrayStack{ + // Set stack length to 0, capacity to 16 + data: make([]int, 0, 16), + } +} + +/* Stack length */ +func (s *arrayStack) size() int { + return len(s.data) +} + +/* Is stack empty */ +func (s *arrayStack) isEmpty() bool { + return s.size() == 0 +} + +/* Push */ +func (s *arrayStack) push(v int) { + // Slice will automatically expand + s.data = append(s.data, v) +} + +/* Pop */ +func (s *arrayStack) pop() any { + val := s.peek() + s.data = s.data[:len(s.data)-1] + return val +} + +/* Get stack top element */ +func (s *arrayStack) peek() any { + if s.isEmpty() { + return nil + } + val := s.data[len(s.data)-1] + return val +} + +/* Get Slice for printing */ +func (s *arrayStack) toSlice() []int { + return s.data +} diff --git a/en/codes/go/chapter_stack_and_queue/deque_test.go b/en/codes/go/chapter_stack_and_queue/deque_test.go new file mode 100644 index 000000000..798eaf184 --- /dev/null +++ b/en/codes/go/chapter_stack_and_queue/deque_test.go @@ -0,0 +1,141 @@ +// File: deque_test.go +// Created Time: 2022-11-29 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import ( + "container/list" + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestDeque(t *testing.T) { + /* Get the length of the double-ended queue */ + // In Go, use list as deque + deque := list.New() + + /* Elements enqueue */ + deque.PushBack(2) + deque.PushBack(5) + deque.PushBack(4) + deque.PushFront(3) + deque.PushFront(1) + fmt.Print("Double-ended queue deque = ") + PrintList(deque) + + /* Update element */ + front := deque.Front() + fmt.Println("Front element front =", front.Value) + rear := deque.Back() + fmt.Println("Rear element rear =", rear.Value) + + /* Element dequeue */ + deque.Remove(front) + fmt.Print("Front dequeue element front = ", front.Value, ", after front dequeue, deque = ") + PrintList(deque) + deque.Remove(rear) + fmt.Print("Rear dequeue element rear = ", rear.Value, ", after rear dequeue, deque = ") + PrintList(deque) + + /* Get the length of the double-ended queue */ + size := deque.Len() + fmt.Println("Deque length size =", size) + + /* Check if the double-ended queue is empty */ + isEmpty := deque.Len() == 0 + fmt.Println("Is deque empty =", isEmpty) +} + +func TestArrayDeque(t *testing.T) { + /* Get the length of the double-ended queue */ + // In Go, use list as deque + deque := newArrayDeque(16) + + /* Elements enqueue */ + deque.pushLast(3) + deque.pushLast(2) + deque.pushLast(5) + fmt.Print("Double-ended queue deque = ") + PrintSlice(deque.toSlice()) + + /* Update element */ + peekFirst := deque.peekFirst() + fmt.Println("Front element peekFirst =", peekFirst) + peekLast := deque.peekLast() + fmt.Println("Rear element peekLast =", peekLast) + + /* Elements enqueue */ + deque.pushLast(4) + fmt.Print("After element 4 enqueues at rear, deque = ") + PrintSlice(deque.toSlice()) + deque.pushFirst(1) + fmt.Print("After element 1 enqueues at front, deque = ") + PrintSlice(deque.toSlice()) + + /* Element dequeue */ + popFirst := deque.popFirst() + fmt.Print("Front dequeue element popFirst = ", popFirst, ", after front dequeue, deque = ") + PrintSlice(deque.toSlice()) + popLast := deque.popLast() + fmt.Print("Back dequeue element popLast = ", popLast, ", after rear dequeue, deque = ") + PrintSlice(deque.toSlice()) + + /* Get the length of the double-ended queue */ + size := deque.size() + fmt.Println("Deque length size =", size) + + /* Check if the double-ended queue is empty */ + isEmpty := deque.isEmpty() + fmt.Println("Is deque empty =", isEmpty) +} + +func TestLinkedListDeque(t *testing.T) { + // Access front of the queue element + deque := newLinkedListDeque() + + // Elements enqueue + deque.pushLast(2) + deque.pushLast(5) + deque.pushLast(4) + deque.pushFirst(3) + deque.pushFirst(1) + fmt.Print("Deque deque = ") + PrintList(deque.toList()) + + // Return list for printing + front := deque.peekFirst() + fmt.Println("Front element front =", front) + rear := deque.peekLast() + fmt.Println("Rear element rear =", rear) + + // Element dequeue + popFirst := deque.popFirst() + fmt.Print("Front dequeue element popFirst = ", popFirst, ", after front dequeue, deque = ") + PrintList(deque.toList()) + popLast := deque.popLast() + fmt.Print("Back dequeue element popLast = ", popLast, ", after rear dequeue, deque = ") + PrintList(deque.toList()) + + // Get queue length + size := deque.size() + fmt.Println("Queue length size =", size) + + // Check if empty + isEmpty := deque.isEmpty() + fmt.Println("Is queue empty =", isEmpty) +} + +// BenchmarkLinkedListDeque 67.92 ns/op in Mac M1 Pro +func BenchmarkLinkedListDeque(b *testing.B) { + deque := newLinkedListDeque() + // use b.N for looping + for i := 0; i < b.N; i++ { + deque.pushLast(777) + } + for i := 0; i < b.N; i++ { + deque.popFirst() + } +} diff --git a/en/codes/go/chapter_stack_and_queue/linkedlist_deque.go b/en/codes/go/chapter_stack_and_queue/linkedlist_deque.go new file mode 100644 index 000000000..d93d04023 --- /dev/null +++ b/en/codes/go/chapter_stack_and_queue/linkedlist_deque.go @@ -0,0 +1,85 @@ +// File: linkedlist_deque.go +// Created Time: 2022-11-29 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import ( + "container/list" +) + +/* Double-ended queue based on doubly linked list implementation */ +type linkedListDeque struct { + // Use built-in package list + data *list.List +} + +/* Initialize deque */ +func newLinkedListDeque() *linkedListDeque { + return &linkedListDeque{ + data: list.New(), + } +} + +/* Front element enqueue */ +func (s *linkedListDeque) pushFirst(value any) { + s.data.PushFront(value) +} + +/* Rear element enqueue */ +func (s *linkedListDeque) pushLast(value any) { + s.data.PushBack(value) +} + +/* Check if the double-ended queue is empty */ +func (s *linkedListDeque) popFirst() any { + if s.isEmpty() { + return nil + } + e := s.data.Front() + s.data.Remove(e) + return e.Value +} + +/* Rear element dequeue */ +func (s *linkedListDeque) popLast() any { + if s.isEmpty() { + return nil + } + e := s.data.Back() + s.data.Remove(e) + return e.Value +} + +/* Return list for printing */ +func (s *linkedListDeque) peekFirst() any { + if s.isEmpty() { + return nil + } + e := s.data.Front() + return e.Value +} + +/* Driver Code */ +func (s *linkedListDeque) peekLast() any { + if s.isEmpty() { + return nil + } + e := s.data.Back() + return e.Value +} + +/* Get the length of the queue */ +func (s *linkedListDeque) size() int { + return s.data.Len() +} + +/* Check if the queue is empty */ +func (s *linkedListDeque) isEmpty() bool { + return s.data.Len() == 0 +} + +/* Get List for printing */ +func (s *linkedListDeque) toList() *list.List { + return s.data +} diff --git a/en/codes/go/chapter_stack_and_queue/linkedlist_queue.go b/en/codes/go/chapter_stack_and_queue/linkedlist_queue.go new file mode 100644 index 000000000..579e3c18e --- /dev/null +++ b/en/codes/go/chapter_stack_and_queue/linkedlist_queue.go @@ -0,0 +1,61 @@ +// File: linkedlist_queue.go +// Created Time: 2022-11-28 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import ( + "container/list" +) + +/* Queue based on linked list implementation */ +type linkedListQueue struct { + // Use built-in package list to implement queue + data *list.List +} + +/* Access front of the queue element */ +func newLinkedListQueue() *linkedListQueue { + return &linkedListQueue{ + data: list.New(), + } +} + +/* Enqueue */ +func (s *linkedListQueue) push(value any) { + s.data.PushBack(value) +} + +/* Dequeue */ +func (s *linkedListQueue) pop() any { + if s.isEmpty() { + return nil + } + e := s.data.Front() + s.data.Remove(e) + return e.Value +} + +/* Return list for printing */ +func (s *linkedListQueue) peek() any { + if s.isEmpty() { + return nil + } + e := s.data.Front() + return e.Value +} + +/* Get the length of the queue */ +func (s *linkedListQueue) size() int { + return s.data.Len() +} + +/* Check if the queue is empty */ +func (s *linkedListQueue) isEmpty() bool { + return s.data.Len() == 0 +} + +/* Get List for printing */ +func (s *linkedListQueue) toList() *list.List { + return s.data +} diff --git a/en/codes/go/chapter_stack_and_queue/linkedlist_stack.go b/en/codes/go/chapter_stack_and_queue/linkedlist_stack.go new file mode 100644 index 000000000..36c48f468 --- /dev/null +++ b/en/codes/go/chapter_stack_and_queue/linkedlist_stack.go @@ -0,0 +1,61 @@ +// File: linkedlist_stack.go +// Created Time: 2022-11-28 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import ( + "container/list" +) + +/* Stack based on linked list implementation */ +type linkedListStack struct { + // Use built-in package list to implement stack + data *list.List +} + +/* Access top of the stack element */ +func newLinkedListStack() *linkedListStack { + return &linkedListStack{ + data: list.New(), + } +} + +/* Push */ +func (s *linkedListStack) push(value int) { + s.data.PushBack(value) +} + +/* Pop */ +func (s *linkedListStack) pop() any { + if s.isEmpty() { + return nil + } + e := s.data.Back() + s.data.Remove(e) + return e.Value +} + +/* Return list for printing */ +func (s *linkedListStack) peek() any { + if s.isEmpty() { + return nil + } + e := s.data.Back() + return e.Value +} + +/* Get the length of the stack */ +func (s *linkedListStack) size() int { + return s.data.Len() +} + +/* Check if the stack is empty */ +func (s *linkedListStack) isEmpty() bool { + return s.data.Len() == 0 +} + +/* Get List for printing */ +func (s *linkedListStack) toList() *list.List { + return s.data +} diff --git a/en/codes/go/chapter_stack_and_queue/queue_test.go b/en/codes/go/chapter_stack_and_queue/queue_test.go new file mode 100644 index 000000000..7e7a7c088 --- /dev/null +++ b/en/codes/go/chapter_stack_and_queue/queue_test.go @@ -0,0 +1,146 @@ +// File: queue_test.go +// Created Time: 2022-11-28 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import ( + "container/list" + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestQueue(t *testing.T) { + /* Access front of the queue element */ + // In Go, use list as queue + queue := list.New() + + /* Elements enqueue */ + queue.PushBack(1) + queue.PushBack(3) + queue.PushBack(2) + queue.PushBack(5) + queue.PushBack(4) + fmt.Print("Queue queue = ") + PrintList(queue) + + /* Return list for printing */ + peek := queue.Front() + fmt.Println("Front element peek =", peek.Value) + + /* Element dequeue */ + pop := queue.Front() + queue.Remove(pop) + fmt.Print("Dequeue element pop = ", pop.Value, ", after dequeue, queue = ") + PrintList(queue) + + /* Get the length of the queue */ + size := queue.Len() + fmt.Println("Queue length size =", size) + + /* Check if the queue is empty */ + isEmpty := queue.Len() == 0 + fmt.Println("Is queue empty =", isEmpty) +} + +func TestArrayQueue(t *testing.T) { + + // Initialize queue using queue's common interface + capacity := 10 + queue := newArrayQueue(capacity) + if queue.pop() != nil { + t.Errorf("want:%v,got:%v", nil, queue.pop()) + } + + // Elements enqueue + queue.push(1) + queue.push(3) + queue.push(2) + queue.push(5) + queue.push(4) + fmt.Print("Queue queue = ") + PrintSlice(queue.toSlice()) + + // Return list for printing + peek := queue.peek() + fmt.Println("Front element peek =", peek) + + // Element dequeue + pop := queue.pop() + fmt.Print("Dequeue element pop = ", pop, ", after dequeue, queue = ") + PrintSlice(queue.toSlice()) + + // Get queue length + size := queue.size() + fmt.Println("Queue length size =", size) + + // Check if empty + isEmpty := queue.isEmpty() + fmt.Println("Is queue empty =", isEmpty) + + /* Test circular array */ + for i := 0; i < 10; i++ { + queue.push(i) + queue.pop() + fmt.Print("Round ", i, " enqueue + dequeue, queue =") + PrintSlice(queue.toSlice()) + } +} + +func TestLinkedListQueue(t *testing.T) { + // Initialize queue + queue := newLinkedListQueue() + + // Elements enqueue + queue.push(1) + queue.push(3) + queue.push(2) + queue.push(5) + queue.push(4) + fmt.Print("Queue queue = ") + PrintList(queue.toList()) + + // Return list for printing + peek := queue.peek() + fmt.Println("Front element peek =", peek) + + // Element dequeue + pop := queue.pop() + fmt.Print("Dequeue element pop = ", pop, ", after dequeue, queue = ") + PrintList(queue.toList()) + + // Get queue length + size := queue.size() + fmt.Println("Queue length size =", size) + + // Check if empty + isEmpty := queue.isEmpty() + fmt.Println("Is queue empty =", isEmpty) +} + +// BenchmarkArrayQueue 8 ns/op in Mac M1 Pro +func BenchmarkArrayQueue(b *testing.B) { + capacity := 1000 + queue := newArrayQueue(capacity) + // use b.N for looping + for i := 0; i < b.N; i++ { + queue.push(777) + } + for i := 0; i < b.N; i++ { + queue.pop() + } +} + +// BenchmarkLinkedQueue 62.66 ns/op in Mac M1 Pro +func BenchmarkLinkedQueue(b *testing.B) { + queue := newLinkedListQueue() + // use b.N for looping + for i := 0; i < b.N; i++ { + queue.push(777) + } + for i := 0; i < b.N; i++ { + queue.pop() + } +} diff --git a/en/codes/go/chapter_stack_and_queue/stack_test.go b/en/codes/go/chapter_stack_and_queue/stack_test.go new file mode 100644 index 000000000..0fa825152 --- /dev/null +++ b/en/codes/go/chapter_stack_and_queue/stack_test.go @@ -0,0 +1,130 @@ +// File: stack_test.go +// Created Time: 2022-11-28 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestStack(t *testing.T) { + /* Access top of the stack element */ + // In Go, recommended to use Slice as stack + var stack []int + + /* Elements push onto stack */ + stack = append(stack, 1) + stack = append(stack, 3) + stack = append(stack, 2) + stack = append(stack, 5) + stack = append(stack, 4) + fmt.Print("Stack stack = ") + PrintSlice(stack) + + /* Return list for printing */ + peek := stack[len(stack)-1] + fmt.Println("Stack top element peek =", peek) + + /* Element pop from stack */ + pop := stack[len(stack)-1] + stack = stack[:len(stack)-1] + fmt.Print("Pop element pop = ", pop, ", after pop, stack = ") + PrintSlice(stack) + + /* Get the length of the stack */ + size := len(stack) + fmt.Println("Stack length size =", size) + + /* Check if empty */ + isEmpty := len(stack) == 0 + fmt.Println("Is stack empty =", isEmpty) +} + +func TestArrayStack(t *testing.T) { + // Initialize stack using interface + stack := newArrayStack() + + // Elements push onto stack + stack.push(1) + stack.push(3) + stack.push(2) + stack.push(5) + stack.push(4) + fmt.Print("Stack stack = ") + PrintSlice(stack.toSlice()) + + // Return list for printing + peek := stack.peek() + fmt.Println("Stack top element peek =", peek) + + // Element pop from stack + pop := stack.pop() + fmt.Print("Pop element pop = ", pop, ", after pop, stack = ") + PrintSlice(stack.toSlice()) + + // Get the length of the stack + size := stack.size() + fmt.Println("Stack length size =", size) + + // Check if empty + isEmpty := stack.isEmpty() + fmt.Println("Is stack empty =", isEmpty) +} + +func TestLinkedListStack(t *testing.T) { + // Access top of the stack element + stack := newLinkedListStack() + // Elements push onto stack + stack.push(1) + stack.push(3) + stack.push(2) + stack.push(5) + stack.push(4) + fmt.Print("Stack stack = ") + PrintList(stack.toList()) + + // Return list for printing + peek := stack.peek() + fmt.Println("Stack top element peek =", peek) + + // Element pop from stack + pop := stack.pop() + fmt.Print("Pop element pop = ", pop, ", after pop, stack = ") + PrintList(stack.toList()) + + // Get the length of the stack + size := stack.size() + fmt.Println("Stack length size =", size) + + // Check if empty + isEmpty := stack.isEmpty() + fmt.Println("Is stack empty =", isEmpty) +} + +// BenchmarkArrayStack 8 ns/op in Mac M1 Pro +func BenchmarkArrayStack(b *testing.B) { + stack := newArrayStack() + // use b.N for looping + for i := 0; i < b.N; i++ { + stack.push(777) + } + for i := 0; i < b.N; i++ { + stack.pop() + } +} + +// BenchmarkLinkedListStack 65.02 ns/op in Mac M1 Pro +func BenchmarkLinkedListStack(b *testing.B) { + stack := newLinkedListStack() + // use b.N for looping + for i := 0; i < b.N; i++ { + stack.push(777) + } + for i := 0; i < b.N; i++ { + stack.pop() + } +} diff --git a/en/codes/go/chapter_tree/array_binary_tree.go b/en/codes/go/chapter_tree/array_binary_tree.go new file mode 100644 index 000000000..a251d01f5 --- /dev/null +++ b/en/codes/go/chapter_tree/array_binary_tree.go @@ -0,0 +1,101 @@ +// File: array_binary_tree.go +// Created Time: 2023-07-24 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +/* Binary tree class represented by array */ +type arrayBinaryTree struct { + tree []any +} + +/* Constructor */ +func newArrayBinaryTree(arr []any) *arrayBinaryTree { + return &arrayBinaryTree{ + tree: arr, + } +} + +/* List capacity */ +func (abt *arrayBinaryTree) size() int { + return len(abt.tree) +} + +/* Get value of node at index i */ +func (abt *arrayBinaryTree) val(i int) any { + // If index out of bounds, return null to represent empty position + if i < 0 || i >= abt.size() { + return nil + } + return abt.tree[i] +} + +/* Get index of left child node of node at index i */ +func (abt *arrayBinaryTree) left(i int) int { + return 2*i + 1 +} + +/* Get index of right child node of node at index i */ +func (abt *arrayBinaryTree) right(i int) int { + return 2*i + 2 +} + +/* Get index of parent node of node at index i */ +func (abt *arrayBinaryTree) parent(i int) int { + return (i - 1) / 2 +} + +/* Level-order traversal */ +func (abt *arrayBinaryTree) levelOrder() []any { + var res []any + // Traverse array directly + for i := 0; i < abt.size(); i++ { + if abt.val(i) != nil { + res = append(res, abt.val(i)) + } + } + return res +} + +/* Depth-first traversal */ +func (abt *arrayBinaryTree) dfs(i int, order string, res *[]any) { + // If empty position, return + if abt.val(i) == nil { + return + } + // Preorder traversal + if order == "pre" { + *res = append(*res, abt.val(i)) + } + abt.dfs(abt.left(i), order, res) + // Inorder traversal + if order == "in" { + *res = append(*res, abt.val(i)) + } + abt.dfs(abt.right(i), order, res) + // Postorder traversal + if order == "post" { + *res = append(*res, abt.val(i)) + } +} + +/* Preorder traversal */ +func (abt *arrayBinaryTree) preOrder() []any { + var res []any + abt.dfs(0, "pre", &res) + return res +} + +/* Inorder traversal */ +func (abt *arrayBinaryTree) inOrder() []any { + var res []any + abt.dfs(0, "in", &res) + return res +} + +/* Postorder traversal */ +func (abt *arrayBinaryTree) postOrder() []any { + var res []any + abt.dfs(0, "post", &res) + return res +} diff --git a/en/codes/go/chapter_tree/array_binary_tree_test.go b/en/codes/go/chapter_tree/array_binary_tree_test.go new file mode 100644 index 000000000..1126e0939 --- /dev/null +++ b/en/codes/go/chapter_tree/array_binary_tree_test.go @@ -0,0 +1,47 @@ +// File: array_binary_tree_test.go +// Created Time: 2023-07-24 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestArrayBinaryTree(t *testing.T) { + // Initialize binary tree + // Here we use a function to generate a binary tree directly from an array + arr := []any{1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15} + root := SliceToTree(arr) + fmt.Println("\nInitialize binary tree") + fmt.Println("Array representation of binary tree:") + fmt.Println(arr) + fmt.Println("Linked list representation of binary tree:") + PrintTree(root) + + // Binary tree class represented by array + abt := newArrayBinaryTree(arr) + + // Access node + i := 1 + l := abt.left(i) + r := abt.right(i) + p := abt.parent(i) + fmt.Println("\nCurrent node index is", i, ", value is", abt.val(i)) + fmt.Println("Its left child node index is", l, ", value is", abt.val(l)) + fmt.Println("Its right child node index is", r, ", value is", abt.val(r)) + fmt.Println("Its parent node index is", p, ", value is", abt.val(p)) + + // Traverse tree + res := abt.levelOrder() + fmt.Println("\nLevel-order traversal is:", res) + res = abt.preOrder() + fmt.Println("Preorder traversal is:", res) + res = abt.inOrder() + fmt.Println("Inorder traversal is:", res) + res = abt.postOrder() + fmt.Println("Postorder traversal is:", res) +} diff --git a/en/codes/go/chapter_tree/avl_tree.go b/en/codes/go/chapter_tree/avl_tree.go new file mode 100644 index 000000000..3a938c86e --- /dev/null +++ b/en/codes/go/chapter_tree/avl_tree.go @@ -0,0 +1,200 @@ +// File: avl_tree.go +// Created Time: 2023-01-08 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import . "github.com/krahets/hello-algo/pkg" + +/* AVL tree */ +type aVLTree struct { + // Root node + root *TreeNode +} + +func newAVLTree() *aVLTree { + return &aVLTree{root: nil} +} + +/* Get node height */ +func (t *aVLTree) height(node *TreeNode) int { + // Empty node height is -1, leaf node height is 0 + if node != nil { + return node.Height + } + return -1 +} + +/* Update node height */ +func (t *aVLTree) updateHeight(node *TreeNode) { + lh := t.height(node.Left) + rh := t.height(node.Right) + // Node height equals the height of the tallest subtree + 1 + if lh > rh { + node.Height = lh + 1 + } else { + node.Height = rh + 1 + } +} + +/* Get balance factor */ +func (t *aVLTree) balanceFactor(node *TreeNode) int { + // Empty node balance factor is 0 + if node == nil { + return 0 + } + // Node balance factor = left subtree height - right subtree height + return t.height(node.Left) - t.height(node.Right) +} + +/* Right rotation operation */ +func (t *aVLTree) rightRotate(node *TreeNode) *TreeNode { + child := node.Left + grandChild := child.Right + // Using child as pivot, rotate node to the right + child.Right = node + node.Left = grandChild + // Update node height + t.updateHeight(node) + t.updateHeight(child) + // Return root node of subtree after rotation + return child +} + +/* Left rotation operation */ +func (t *aVLTree) leftRotate(node *TreeNode) *TreeNode { + child := node.Right + grandChild := child.Left + // Using child as pivot, rotate node to the left + child.Left = node + node.Right = grandChild + // Update node height + t.updateHeight(node) + t.updateHeight(child) + // Return root node of subtree after rotation + return child +} + +/* Perform rotation operation to restore balance to this subtree */ +func (t *aVLTree) rotate(node *TreeNode) *TreeNode { + // Get balance factor of node + // Go recommends short variables, here bf refers to t.balanceFactor + bf := t.balanceFactor(node) + // Left-leaning tree + if bf > 1 { + if t.balanceFactor(node.Left) >= 0 { + // Right rotation + return t.rightRotate(node) + } else { + // First left rotation then right rotation + node.Left = t.leftRotate(node.Left) + return t.rightRotate(node) + } + } + // Right-leaning tree + if bf < -1 { + if t.balanceFactor(node.Right) <= 0 { + // Left rotation + return t.leftRotate(node) + } else { + // First right rotation then left rotation + node.Right = t.rightRotate(node.Right) + return t.leftRotate(node) + } + } + // Balanced tree, no rotation needed, return directly + return node +} + +/* Insert node */ +func (t *aVLTree) insert(val int) { + t.root = t.insertHelper(t.root, val) +} + +/* Recursively insert node (helper function) */ +func (t *aVLTree) insertHelper(node *TreeNode, val int) *TreeNode { + if node == nil { + return NewTreeNode(val) + } + /* 1. Find insertion position and insert node */ + if val < node.Val.(int) { + node.Left = t.insertHelper(node.Left, val) + } else if val > node.Val.(int) { + node.Right = t.insertHelper(node.Right, val) + } else { + // Duplicate node not inserted, return directly + return node + } + // Update node height + t.updateHeight(node) + /* 2. Perform rotation operation to restore balance to this subtree */ + node = t.rotate(node) + // Return root node of subtree + return node +} + +/* Remove node */ +func (t *aVLTree) remove(val int) { + t.root = t.removeHelper(t.root, val) +} + +/* Recursively remove node (helper function) */ +func (t *aVLTree) removeHelper(node *TreeNode, val int) *TreeNode { + if node == nil { + return nil + } + /* 1. Find node and delete */ + if val < node.Val.(int) { + node.Left = t.removeHelper(node.Left, val) + } else if val > node.Val.(int) { + node.Right = t.removeHelper(node.Right, val) + } else { + if node.Left == nil || node.Right == nil { + child := node.Left + if node.Right != nil { + child = node.Right + } + if child == nil { + // Number of child nodes = 0, delete node directly and return + return nil + } else { + // Number of child nodes = 1, delete node directly + node = child + } + } else { + // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + temp := node.Right + for temp.Left != nil { + temp = temp.Left + } + node.Right = t.removeHelper(node.Right, temp.Val.(int)) + node.Val = temp.Val + } + } + // Update node height + t.updateHeight(node) + /* 2. Perform rotation operation to restore balance to this subtree */ + node = t.rotate(node) + // Return root node of subtree + return node +} + +/* Search node */ +func (t *aVLTree) search(val int) *TreeNode { + cur := t.root + // Loop search, exit after passing leaf node + for cur != nil { + if cur.Val.(int) < val { + // Target node is in cur's right subtree + cur = cur.Right + } else if cur.Val.(int) > val { + // Target node is in cur's left subtree + cur = cur.Left + } else { + // Found target node, exit loop + break + } + } + // Return target node + return cur +} diff --git a/en/codes/go/chapter_tree/avl_tree_test.go b/en/codes/go/chapter_tree/avl_tree_test.go new file mode 100644 index 000000000..4ea32f43e --- /dev/null +++ b/en/codes/go/chapter_tree/avl_tree_test.go @@ -0,0 +1,54 @@ +// File: avl_tree_test.go +// Created Time: 2023-01-08 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestAVLTree(t *testing.T) { + /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + tree := newAVLTree() + /* Insert node */ + // Delete nodes + testInsert(tree, 1) + testInsert(tree, 2) + testInsert(tree, 3) + testInsert(tree, 4) + testInsert(tree, 5) + testInsert(tree, 8) + testInsert(tree, 7) + testInsert(tree, 9) + testInsert(tree, 10) + testInsert(tree, 6) + + /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + testInsert(tree, 7) + + /* Remove node */ + // Delete node with degree 1 + testRemove(tree, 8) // Delete node with degree 2 + testRemove(tree, 5) // Remove node with degree 1 + testRemove(tree, 4) // Remove node with degree 2 + + /* Search node */ + node := tree.search(7) + fmt.Printf("\nFound node object is %#v, node value = %d \n", node, node.Val) +} + +func testInsert(tree *aVLTree, val int) { + tree.insert(val) + fmt.Printf("\nAfter inserting node %d, AVL tree is \n", val) + PrintTree(tree.root) +} + +func testRemove(tree *aVLTree, val int) { + tree.remove(val) + fmt.Printf("\nAfter removing node %d, AVL tree is \n", val) + PrintTree(tree.root) +} diff --git a/en/codes/go/chapter_tree/binary_search_tree.go b/en/codes/go/chapter_tree/binary_search_tree.go new file mode 100644 index 000000000..7ee4e7df7 --- /dev/null +++ b/en/codes/go/chapter_tree/binary_search_tree.go @@ -0,0 +1,142 @@ +// File: binary_search_tree.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + . "github.com/krahets/hello-algo/pkg" +) + +type binarySearchTree struct { + root *TreeNode +} + +func newBinarySearchTree() *binarySearchTree { + bst := &binarySearchTree{} + // Initialize empty tree + bst.root = nil + return bst +} + +/* Get root node */ +func (bst *binarySearchTree) getRoot() *TreeNode { + return bst.root +} + +/* Search node */ +func (bst *binarySearchTree) search(num int) *TreeNode { + node := bst.root + // Loop search, exit after passing leaf node + for node != nil { + if node.Val.(int) < num { + // Target node is in cur's right subtree + node = node.Right + } else if node.Val.(int) > num { + // Target node is in cur's left subtree + node = node.Left + } else { + // Found target node, exit loop + break + } + } + // Return target node + return node +} + +/* Insert node */ +func (bst *binarySearchTree) insert(num int) { + cur := bst.root + // If tree is empty, initialize root node + if cur == nil { + bst.root = NewTreeNode(num) + return + } + // Node position before the node to be inserted + var pre *TreeNode = nil + // Loop search, exit after passing leaf node + for cur != nil { + if cur.Val == num { + return + } + pre = cur + if cur.Val.(int) < num { + cur = cur.Right + } else { + cur = cur.Left + } + } + // Insert node + node := NewTreeNode(num) + if pre.Val.(int) < num { + pre.Right = node + } else { + pre.Left = node + } +} + +/* Remove node */ +func (bst *binarySearchTree) remove(num int) { + cur := bst.root + // If tree is empty, return directly + if cur == nil { + return + } + // Node position before the node to be removed + var pre *TreeNode = nil + // Loop search, exit after passing leaf node + for cur != nil { + if cur.Val == num { + break + } + pre = cur + if cur.Val.(int) < num { + // Node to be removed is in right subtree + cur = cur.Right + } else { + // Node to be removed is in left subtree + cur = cur.Left + } + } + // If no node to delete, return directly + if cur == nil { + return + } + // Number of child nodes is 0 or 1 + if cur.Left == nil || cur.Right == nil { + var child *TreeNode = nil + // Get child node of node to be removed + if cur.Left != nil { + child = cur.Left + } else { + child = cur.Right + } + // Delete node cur + if cur != bst.root { + if pre.Left == cur { + pre.Left = child + } else { + pre.Right = child + } + } else { + // If deleted node is root node, reassign root node + bst.root = child + } + // Number of child nodes is 2 + } else { + // Get next node of node cur to be removed in in-order traversal + tmp := cur.Right + for tmp.Left != nil { + tmp = tmp.Left + } + // Recursively delete node tmp + bst.remove(tmp.Val.(int)) + // Replace cur with tmp + cur.Val = tmp.Val + } +} + +/* Print binary search tree */ +func (bst *binarySearchTree) print() { + PrintTree(bst.root) +} diff --git a/en/codes/go/chapter_tree/binary_search_tree_test.go b/en/codes/go/chapter_tree/binary_search_tree_test.go new file mode 100644 index 000000000..47de52e6f --- /dev/null +++ b/en/codes/go/chapter_tree/binary_search_tree_test.go @@ -0,0 +1,45 @@ +// File: binary_search_tree_test.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + "fmt" + "testing" +) + +func TestBinarySearchTree(t *testing.T) { + bst := newBinarySearchTree() + nums := []int{8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15} + // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + for _, num := range nums { + bst.insert(num) + } + fmt.Println("\nInitialized binary tree is:") + bst.print() + + // Get root node + node := bst.getRoot() + fmt.Println("\nRoot node of binary tree is:", node.Val) + + // Search node + node = bst.search(7) + fmt.Println("Found node object is", node, ", node value =", node.Val) + + // Insert node + bst.insert(16) + fmt.Println("\nAfter inserting node 16, binary tree is:") + bst.print() + + // Remove node + bst.remove(1) + fmt.Println("\nAfter removing node 1, binary tree is:") + bst.print() + bst.remove(2) + fmt.Println("\nAfter removing node 2, binary tree is:") + bst.print() + bst.remove(4) + fmt.Println("\nAfter removing node 4, binary tree is:") + bst.print() +} diff --git a/en/codes/go/chapter_tree/binary_tree_bfs.go b/en/codes/go/chapter_tree/binary_tree_bfs.go new file mode 100644 index 000000000..e63bebf7b --- /dev/null +++ b/en/codes/go/chapter_tree/binary_tree_bfs.go @@ -0,0 +1,35 @@ +// File: binary_tree_bfs.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + "container/list" + + . "github.com/krahets/hello-algo/pkg" +) + +/* Level-order traversal */ +func levelOrder(root *TreeNode) []any { + // Initialize queue, add root node + queue := list.New() + queue.PushBack(root) + // Initialize a slice to save traversal sequence + nums := make([]any, 0) + for queue.Len() > 0 { + // Dequeue + node := queue.Remove(queue.Front()).(*TreeNode) + // Save node value + nums = append(nums, node.Val) + if node.Left != nil { + // Left child node enqueue + queue.PushBack(node.Left) + } + if node.Right != nil { + // Right child node enqueue + queue.PushBack(node.Right) + } + } + return nums +} diff --git a/en/codes/go/chapter_tree/binary_tree_bfs_test.go b/en/codes/go/chapter_tree/binary_tree_bfs_test.go new file mode 100644 index 000000000..4a5f88718 --- /dev/null +++ b/en/codes/go/chapter_tree/binary_tree_bfs_test.go @@ -0,0 +1,24 @@ +// File: binary_tree_bfs_test.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestLevelOrder(t *testing.T) { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + root := SliceToTree([]any{1, 2, 3, 4, 5, 6, 7}) + fmt.Println("\nInitialize binary tree: ") + PrintTree(root) + + // Level-order traversal + nums := levelOrder(root) + fmt.Println("\nLevel-order traversal node print sequence =", nums) +} diff --git a/en/codes/go/chapter_tree/binary_tree_dfs.go b/en/codes/go/chapter_tree/binary_tree_dfs.go new file mode 100644 index 000000000..7a5bb7700 --- /dev/null +++ b/en/codes/go/chapter_tree/binary_tree_dfs.go @@ -0,0 +1,44 @@ +// File: binary_tree_dfs.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + . "github.com/krahets/hello-algo/pkg" +) + +var nums []any + +/* Preorder traversal */ +func preOrder(node *TreeNode) { + if node == nil { + return + } + // Visit priority: root node -> left subtree -> right subtree + nums = append(nums, node.Val) + preOrder(node.Left) + preOrder(node.Right) +} + +/* Inorder traversal */ +func inOrder(node *TreeNode) { + if node == nil { + return + } + // Visit priority: left subtree -> root node -> right subtree + inOrder(node.Left) + nums = append(nums, node.Val) + inOrder(node.Right) +} + +/* Postorder traversal */ +func postOrder(node *TreeNode) { + if node == nil { + return + } + // Visit priority: left subtree -> right subtree -> root node + postOrder(node.Left) + postOrder(node.Right) + nums = append(nums, node.Val) +} diff --git a/en/codes/go/chapter_tree/binary_tree_dfs_test.go b/en/codes/go/chapter_tree/binary_tree_dfs_test.go new file mode 100644 index 000000000..6bee7960f --- /dev/null +++ b/en/codes/go/chapter_tree/binary_tree_dfs_test.go @@ -0,0 +1,35 @@ +// File: binary_tree_dfs_test.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestPreInPostOrderTraversal(t *testing.T) { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + root := SliceToTree([]any{1, 2, 3, 4, 5, 6, 7}) + fmt.Println("\nInitialize binary tree: ") + PrintTree(root) + + // Preorder traversal + nums = nil + preOrder(root) + fmt.Println("\nPre-order traversal node print sequence =", nums) + + // Inorder traversal + nums = nil + inOrder(root) + fmt.Println("\nIn-order traversal node print sequence =", nums) + + // Postorder traversal + nums = nil + postOrder(root) + fmt.Println("\nPost-order traversal node print sequence =", nums) +} diff --git a/en/codes/go/chapter_tree/binary_tree_test.go b/en/codes/go/chapter_tree/binary_tree_test.go new file mode 100644 index 000000000..3ee31a9aa --- /dev/null +++ b/en/codes/go/chapter_tree/binary_tree_test.go @@ -0,0 +1,41 @@ +// File: binary_tree_test.go +// Created Time: 2022-11-25 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestBinaryTree(t *testing.T) { + /* Initialize binary tree */ + // Initialize nodes + n1 := NewTreeNode(1) + n2 := NewTreeNode(2) + n3 := NewTreeNode(3) + n4 := NewTreeNode(4) + n5 := NewTreeNode(5) + // Build references (pointers) between nodes + n1.Left = n2 + n1.Right = n3 + n2.Left = n4 + n2.Right = n5 + fmt.Println("Initialize binary tree") + PrintTree(n1) + + /* Insert node P between n1 -> n2 */ + // Insert node + p := NewTreeNode(0) + n1.Left = p + p.Left = n2 + fmt.Println("After inserting node P") + PrintTree(n1) + // Remove node + n1.Left = n2 + fmt.Println("After removing node P") + PrintTree(n1) +} diff --git a/en/codes/go/go.mod b/en/codes/go/go.mod new file mode 100644 index 000000000..34f5dac20 --- /dev/null +++ b/en/codes/go/go.mod @@ -0,0 +1,3 @@ +module github.com/krahets/hello-algo + +go 1.19 diff --git a/en/codes/go/pkg/list_node.go b/en/codes/go/pkg/list_node.go new file mode 100644 index 000000000..2b923f752 --- /dev/null +++ b/en/codes/go/pkg/list_node.go @@ -0,0 +1,31 @@ +// File: list_node.go +// Created Time: 2022-11-25 +// Author: Reanon (793584285@qq.com) + +package pkg + +// ListNode linked list node +type ListNode struct { + Next *ListNode + Val int +} + +// NewListNode linked list node constructor +func NewListNode(v int) *ListNode { + return &ListNode{ + Next: nil, + Val: v, + } +} + +// ArrayToLinkedList deserialize array to linked list +func ArrayToLinkedList(arr []int) *ListNode { + // dummy header of linked list + dummy := NewListNode(0) + node := dummy + for _, val := range arr { + node.Next = NewListNode(val) + node = node.Next + } + return dummy.Next +} diff --git a/en/codes/go/pkg/list_node_test.go b/en/codes/go/pkg/list_node_test.go new file mode 100644 index 000000000..e61d8d5bf --- /dev/null +++ b/en/codes/go/pkg/list_node_test.go @@ -0,0 +1,16 @@ +// File: list_node_test.go +// Created Time: 2022-11-25 +// Author: Reanon (793584285@qq.com) + +package pkg + +import ( + "testing" +) + +func TestListNode(t *testing.T) { + arr := []int{2, 3, 5, 6, 7} + head := ArrayToLinkedList(arr) + + PrintLinkedList(head) +} diff --git a/en/codes/go/pkg/print_utils.go b/en/codes/go/pkg/print_utils.go new file mode 100644 index 000000000..1a71a7358 --- /dev/null +++ b/en/codes/go/pkg/print_utils.go @@ -0,0 +1,118 @@ +// File: print_utils.go +// Created Time: 2022-12-03 +// Author: Reanon (793584285@qq.com), krahets (krahets@163.com), msk397 (machangxinq@gmail.com) + +package pkg + +import ( + "container/list" + "fmt" + "strconv" + "strings" +) + +// PrintSlice print slice +func PrintSlice[T any](nums []T) { + fmt.Printf("%v", nums) + fmt.Println() +} + +// PrintList print list +func PrintList(list *list.List) { + if list.Len() == 0 { + fmt.Print("[]\n") + return + } + e := list.Front() + // Force conversion to string will affect efficiency + fmt.Print("[") + for e.Next() != nil { + fmt.Print(e.Value, " ") + e = e.Next() + } + fmt.Print(e.Value, "]\n") +} + +// PrintMap print hash table +func PrintMap[K comparable, V any](m map[K]V) { + for key, value := range m { + fmt.Println(key, "->", value) + } +} + +// PrintHeap print heap +func PrintHeap(h []any) { + fmt.Printf("Heap array representation:") + fmt.Printf("%v", h) + fmt.Printf("\nTree representation of heap:\n") + root := SliceToTree(h) + PrintTree(root) +} + +// PrintLinkedList print linked list +func PrintLinkedList(node *ListNode) { + if node == nil { + return + } + var builder strings.Builder + for node.Next != nil { + builder.WriteString(strconv.Itoa(node.Val) + " -> ") + node = node.Next + } + builder.WriteString(strconv.Itoa(node.Val)) + fmt.Println(builder.String()) +} + +// PrintTree print binary tree +func PrintTree(root *TreeNode) { + printTreeHelper(root, nil, false) +} + +// printTreeHelper print binary tree +// This tree printer is borrowed from TECHIE DELIGHT +// https://www.techiedelight.com/c-program-print-binary-tree/ +func printTreeHelper(root *TreeNode, prev *trunk, isRight bool) { + if root == nil { + return + } + prevStr := " " + trunk := newTrunk(prev, prevStr) + printTreeHelper(root.Right, trunk, true) + if prev == nil { + trunk.str = "———" + } else if isRight { + trunk.str = "/———" + prevStr = " |" + } else { + trunk.str = "\\———" + prev.str = prevStr + } + showTrunk(trunk) + fmt.Println(root.Val) + if prev != nil { + prev.str = prevStr + } + trunk.str = " |" + printTreeHelper(root.Left, trunk, false) +} + +type trunk struct { + prev *trunk + str string +} + +func newTrunk(prev *trunk, str string) *trunk { + return &trunk{ + prev: prev, + str: str, + } +} + +func showTrunk(t *trunk) { + if t == nil { + return + } + + showTrunk(t.prev) + fmt.Print(t.str) +} diff --git a/en/codes/go/pkg/tree_node.go b/en/codes/go/pkg/tree_node.go new file mode 100644 index 000000000..fe2860499 --- /dev/null +++ b/en/codes/go/pkg/tree_node.go @@ -0,0 +1,78 @@ +// File: tree_node.go +// Created Time: 2022-11-25 +// Author: Reanon (793584285@qq.com) + +package pkg + +// TreeNode binary tree node +type TreeNode struct { + Val any // Node value + Height int // Node height + Left *TreeNode // Reference to left child node + Right *TreeNode // Reference to right child node +} + +// NewTreeNode binary tree node constructor +func NewTreeNode(v any) *TreeNode { + return &TreeNode{ + Val: v, + Height: 0, + Left: nil, + Right: nil, + } +} + +// For the serialization encoding rules, please refer to: +// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ +// Array representation of binary tree: +// [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] +// Linked list representation of binary tree: +// +// /——— 15 +// /——— 7 +// /——— 3 +// | \——— 6 +// | \——— 12 +// +// ——— 1 +// +// \——— 2 +// | /——— 9 +// \——— 4 +// \——— 8 + +// SliceToTreeDFS deserialize list to binary tree: recursion +func SliceToTreeDFS(arr []any, i int) *TreeNode { + if i < 0 || i >= len(arr) || arr[i] == nil { + return nil + } + root := NewTreeNode(arr[i]) + root.Left = SliceToTreeDFS(arr, 2*i+1) + root.Right = SliceToTreeDFS(arr, 2*i+2) + return root +} + +// SliceToTree deserialize slice to binary tree +func SliceToTree(arr []any) *TreeNode { + return SliceToTreeDFS(arr, 0) +} + +// TreeToSliceDFS serialize binary tree to slice: recursion +func TreeToSliceDFS(root *TreeNode, i int, res *[]any) { + if root == nil { + return + } + for i >= len(*res) { + *res = append(*res, nil) + } + (*res)[i] = root.Val + TreeToSliceDFS(root.Left, 2*i+1, res) + TreeToSliceDFS(root.Right, 2*i+2, res) +} + +// TreeToSlice serialize binary tree to slice +func TreeToSlice(root *TreeNode) []any { + var res []any + TreeToSliceDFS(root, 0, &res) + return res +} diff --git a/en/codes/go/pkg/tree_node_test.go b/en/codes/go/pkg/tree_node_test.go new file mode 100644 index 000000000..043aab099 --- /dev/null +++ b/en/codes/go/pkg/tree_node_test.go @@ -0,0 +1,21 @@ +// File: tree_node_test.go +// Created Time: 2022-11-25 +// Author: Reanon (793584285@qq.com) + +package pkg + +import ( + "fmt" + "testing" +) + +func TestTreeNode(t *testing.T) { + arr := []any{1, 2, 3, nil, 5, 6, nil} + node := SliceToTree(arr) + + // print tree + PrintTree(node) + + // tree to arr + fmt.Println(TreeToSlice(node)) +} diff --git a/en/codes/go/pkg/vertex.go b/en/codes/go/pkg/vertex.go new file mode 100644 index 000000000..111c52344 --- /dev/null +++ b/en/codes/go/pkg/vertex.go @@ -0,0 +1,55 @@ +// File: vertex.go +// Created Time: 2023-02-18 +// Author: Reanon (793584285@qq.com) + +package pkg + +// Vertex vertex class +type Vertex struct { + Val int +} + +// NewVertex vertex constructor +func NewVertex(val int) Vertex { + return Vertex{ + Val: val, + } +} + +// ValsToVets deserialize value list to vertex list +func ValsToVets(vals []int) []Vertex { + vets := make([]Vertex, len(vals)) + for i := 0; i < len(vals); i++ { + vets[i] = NewVertex(vals[i]) + } + return vets +} + +// VetsToVals serialize vertex list to value list +func VetsToVals(vets []Vertex) []int { + vals := make([]int, len(vets)) + for i := range vets { + vals[i] = vets[i].Val + } + return vals +} + +// DeleteSliceElms delete specified elements from slice +func DeleteSliceElms[T any](a []T, elms ...T) []T { + if len(a) == 0 || len(elms) == 0 { + return a + } + // First convert elements to set + m := make(map[any]struct{}) + for _, v := range elms { + m[v] = struct{}{} + } + // Filter out specified elements + res := make([]T, 0, len(a)) + for _, v := range a { + if _, ok := m[v]; !ok { + res = append(res, v) + } + } + return res +} diff --git a/en/codes/java/chapter_array_and_linkedlist/array.java b/en/codes/java/chapter_array_and_linkedlist/array.java index 5e8ed875d..328eb4f45 100644 --- a/en/codes/java/chapter_array_and_linkedlist/array.java +++ b/en/codes/java/chapter_array_and_linkedlist/array.java @@ -10,40 +10,40 @@ import java.util.*; import java.util.concurrent.ThreadLocalRandom; public class array { - /* Random access to elements */ + /* Random access to element */ static int randomAccess(int[] nums) { // Randomly select a number in the interval [0, nums.length) int randomIndex = ThreadLocalRandom.current().nextInt(0, nums.length); - // Retrieve and return a random element + // Retrieve and return the random element int randomNum = nums[randomIndex]; return randomNum; } /* Extend array length */ static int[] extend(int[] nums, int enlarge) { - // Initialize an extended length array + // Initialize an array with extended length int[] res = new int[nums.length + enlarge]; // Copy all elements from the original array to the new array for (int i = 0; i < nums.length; i++) { res[i] = nums[i]; } - // Return the new array after expansion + // Return the extended new array return res; } - /* Insert element num at `index` */ + /* Insert element num at index index in the array */ static void insert(int[] nums, int num, int index) { - // Move all elements after `index` one position backward + // Move all elements at and after index index backward by one position for (int i = nums.length - 1; i > index; i--) { nums[i] = nums[i - 1]; } - // Assign num to the element at index + // Assign num to the element at index index nums[index] = num; } - /* Remove the element at `index` */ + /* Remove the element at index index */ static void remove(int[] nums, int index) { - // Move all elements after `index` one position forward + // Move all elements after index index forward by one position for (int i = index; i < nums.length - 1; i++) { nums[i] = nums[i + 1]; } @@ -56,13 +56,13 @@ public class array { for (int i = 0; i < nums.length; i++) { count += nums[i]; } - // Traverse array elements + // Direct traversal of array elements for (int num : nums) { count += num; } } - /* Search for a specified element in the array */ + /* Find the specified element in the array */ static int find(int[] nums, int target) { for (int i = 0; i < nums.length; i++) { if (nums[i] == target) @@ -73,33 +73,33 @@ public class array { /* Driver Code */ public static void main(String[] args) { - /* Initialize an array */ + /* Initialize array */ int[] arr = new int[5]; System.out.println("Array arr = " + Arrays.toString(arr)); int[] nums = { 1, 3, 2, 5, 4 }; System.out.println("Array nums = " + Arrays.toString(nums)); - /* Random access */ + /* Insert element */ int randomNum = randomAccess(nums); - System.out.println("Get a random element from nums = " + randomNum); + System.out.println("Get random element in nums " + randomNum); - /* Length extension */ + /* Traverse array */ nums = extend(nums, 3); - System.out.println("Extend the array length to 8, resulting in nums = " + Arrays.toString(nums)); + System.out.println("Extend array length to 8, resulting in nums = " + Arrays.toString(nums)); /* Insert element */ insert(nums, 6, 3); - System.out.println("Insert the number 6 at index 3, resulting in nums = " + Arrays.toString(nums)); + System.out.println("Insert number 6 at index 3, resulting in nums = " + Arrays.toString(nums)); /* Remove element */ remove(nums, 2); - System.out.println("Remove the element at index 2, resulting in nums = " + Arrays.toString(nums)); + System.out.println("Remove element at index 2, resulting in nums = " + Arrays.toString(nums)); /* Traverse array */ traverse(nums); - /* Search for elements */ + /* Find element */ int index = find(nums, 3); - System.out.println("Find element 3 in nums, index = " + index); + System.out.println("Find element 3 in nums, get index = " + index); } } diff --git a/en/codes/java/chapter_array_and_linkedlist/linked_list.java b/en/codes/java/chapter_array_and_linkedlist/linked_list.java index e8f619487..8eb1683da 100644 --- a/en/codes/java/chapter_array_and_linkedlist/linked_list.java +++ b/en/codes/java/chapter_array_and_linkedlist/linked_list.java @@ -26,7 +26,7 @@ public class linked_list { n0.next = n1; } - /* Access the node at `index` in the linked list */ + /* Access the node at index index in the linked list */ static ListNode access(ListNode head, int index) { for (int i = 0; i < index; i++) { if (head == null) @@ -36,7 +36,7 @@ public class linked_list { return head; } - /* Search for the first node with value target in the linked list */ + /* Find the first node with value target in the linked list */ static int find(ListNode head, int target) { int index = 0; while (head != null) { @@ -62,25 +62,25 @@ public class linked_list { n1.next = n2; n2.next = n3; n3.next = n4; - System.out.println("The initialized linked list is"); + System.out.println("Initialized linked list is"); PrintUtil.printLinkedList(n0); /* Insert node */ insert(n0, new ListNode(0)); - System.out.println("Linked list after inserting the node is"); + System.out.println("Linked list after inserting node is"); PrintUtil.printLinkedList(n0); /* Remove node */ remove(n0); - System.out.println("Linked list after removing the node is"); + System.out.println("Linked list after removing node is"); PrintUtil.printLinkedList(n0); /* Access node */ ListNode node = access(n0, 3); - System.out.println("The value of the node at index 3 in the linked list = " + node.val); + System.out.println("Value of node at index 3 in linked list = " + node.val); /* Search node */ int index = find(n0, 2); - System.out.println("The index of the node with value 2 in the linked list = " + index); + System.out.println("Index of node with value 2 in linked list = " + index); } } diff --git a/en/codes/java/chapter_array_and_linkedlist/list.java b/en/codes/java/chapter_array_and_linkedlist/list.java index ad3f7f809..fb7d99776 100644 --- a/en/codes/java/chapter_array_and_linkedlist/list.java +++ b/en/codes/java/chapter_array_and_linkedlist/list.java @@ -11,24 +11,24 @@ import java.util.*; public class list { public static void main(String[] args) { /* Initialize list */ - // The array's element type is Integer[], a wrapper class for int + // Note that the array element type is Integer[], the wrapper class of int[] Integer[] numbers = new Integer[] { 1, 3, 2, 5, 4 }; List nums = new ArrayList<>(Arrays.asList(numbers)); System.out.println("List nums = " + nums); - /* Access element */ - int num = nums.get(1); - System.out.println("Access the element at index 1, obtained num = " + num); - /* Update element */ + int num = nums.get(1); + System.out.println("Access element at index 1, get num = " + num); + + /* Add elements at the end */ nums.set(1, 0); - System.out.println("Update the element at index 1 to 0, resulting in nums = " + nums); + System.out.println("Update element at index 1 to 0, resulting in nums = " + nums); - /* Clear list */ + /* Remove element */ nums.clear(); - System.out.println("After clearing the list, nums = " + nums); + System.out.println("After clearing list, nums = " + nums); - /* Add element at the end */ + /* Direct traversal of list elements */ nums.add(1); nums.add(3); nums.add(2); @@ -36,20 +36,20 @@ public class list { nums.add(4); System.out.println("After adding elements, nums = " + nums); - /* Insert element in the middle */ + /* Sort list */ nums.add(3, 6); - System.out.println("Insert the number 6 at index 3, resulting in nums = " + nums); + System.out.println("Insert number 6 at index 3, resulting in nums = " + nums); /* Remove element */ nums.remove(3); - System.out.println("Remove the element at index 3, resulting in nums = " + nums); + System.out.println("Remove element at index 3, resulting in nums = " + nums); - /* Traverse the list by index */ + /* Traverse list by index */ int count = 0; for (int i = 0; i < nums.size(); i++) { count += nums.get(i); } - /* Traverse the list elements */ + /* Directly traverse list elements */ for (int x : nums) { count += x; } @@ -61,6 +61,6 @@ public class list { /* Sort list */ Collections.sort(nums); - System.out.println("After sorting the list, nums = " + nums); + System.out.println("After sorting list, nums = " + nums); } } diff --git a/en/codes/java/chapter_array_and_linkedlist/my_list.java b/en/codes/java/chapter_array_and_linkedlist/my_list.java index 98783b941..c18087dff 100644 --- a/en/codes/java/chapter_array_and_linkedlist/my_list.java +++ b/en/codes/java/chapter_array_and_linkedlist/my_list.java @@ -13,7 +13,7 @@ class MyList { private int[] arr; // Array (stores list elements) private int capacity = 10; // List capacity private int size = 0; // List length (current number of elements) - private int extendRatio = 2; // Multiple for each list expansion + private int extendRatio = 2; // Multiple by which the list capacity is extended each time /* Constructor */ public MyList() { @@ -30,7 +30,7 @@ class MyList { return capacity; } - /* Access element */ + /* Update element */ public int get(int index) { // If the index is out of bounds, throw an exception, as below if (index < 0 || index >= size) @@ -38,16 +38,16 @@ class MyList { return arr[index]; } - /* Update element */ + /* Add elements at the end */ public void set(int index, int num) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index out of bounds"); arr[index] = num; } - /* Add element at the end */ + /* Direct traversal of list elements */ public void add(int num) { - // When the number of elements exceeds capacity, trigger the expansion mechanism + // When the number of elements exceeds capacity, trigger the extension mechanism if (size == capacity()) extendCapacity(); arr[size] = num; @@ -55,14 +55,14 @@ class MyList { size++; } - /* Insert element in the middle */ + /* Sort list */ public void insert(int index, int num) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index out of bounds"); - // When the number of elements exceeds capacity, trigger the expansion mechanism + // When the number of elements exceeds capacity, trigger the extension mechanism if (size == capacity()) extendCapacity(); - // Move all elements after `index` one position backward + // Move all elements after index index forward by one position for (int j = size - 1; j >= index; j--) { arr[j + 1] = arr[j]; } @@ -76,7 +76,7 @@ class MyList { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index out of bounds"); int num = arr[index]; - // Move all elements after `index` one position forward + // Move all elements after index forward by one position for (int j = index; j < size - 1; j++) { arr[j] = arr[j + 1]; } @@ -86,18 +86,18 @@ class MyList { return num; } - /* Extend list */ + /* Driver Code */ public void extendCapacity() { - // Create a new array with a length multiple of the original array by extendRatio, and copy the original array to the new array + // Create a new array with length extendRatio times the original array and copy the original array to the new array arr = Arrays.copyOf(arr, capacity() * extendRatio); - // Update list capacity + // Add elements at the end capacity = arr.length; } - /* Convert the list to an array */ + /* Convert list to array */ public int[] toArray() { int size = size(); - // Only convert elements within valid length range + // Elements enqueue int[] arr = new int[size]; for (int i = 0; i < size; i++) { arr[i] = get(i); @@ -111,7 +111,7 @@ public class my_list { public static void main(String[] args) { /* Initialize list */ MyList nums = new MyList(); - /* Add element at the end */ + /* Direct traversal of list elements */ nums.add(1); nums.add(3); nums.add(2); @@ -120,28 +120,28 @@ public class my_list { System.out.println("List nums = " + Arrays.toString(nums.toArray()) + ", capacity = " + nums.capacity() + ", length = " + nums.size()); - /* Insert element in the middle */ + /* Sort list */ nums.insert(3, 6); - System.out.println("Insert the number 6 at index 3, resulting in nums = " + Arrays.toString(nums.toArray())); + System.out.println("Insert number 6 at index 3, resulting in nums = " + Arrays.toString(nums.toArray())); /* Remove element */ nums.remove(3); - System.out.println("Remove the element at index 3, resulting in nums = " + Arrays.toString(nums.toArray())); - - /* Access element */ - int num = nums.get(1); - System.out.println("Access the element at index 1, obtained num = " + num); + System.out.println("Remove element at index 3, resulting in nums = " + Arrays.toString(nums.toArray())); /* Update element */ - nums.set(1, 0); - System.out.println("Update the element at index 1 to 0, resulting in nums = " + Arrays.toString(nums.toArray())); + int num = nums.get(1); + System.out.println("Access element at index 1, get num = " + num); - /* Test expansion mechanism */ + /* Add elements at the end */ + nums.set(1, 0); + System.out.println("Update element at index 1 to 0, resulting in nums = " + Arrays.toString(nums.toArray())); + + /* Test capacity expansion mechanism */ for (int i = 0; i < 10; i++) { - // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism at this time + // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism nums.add(i); } - System.out.println("After extending, list nums = " + Arrays.toString(nums.toArray()) + + System.out.println("List nums after expansion = " + Arrays.toString(nums.toArray()) + ", capacity = " + nums.capacity() + ", length = " + nums.size()); } } diff --git a/en/codes/java/chapter_backtracking/n_queens.java b/en/codes/java/chapter_backtracking/n_queens.java index a533a8fed..d429d3250 100644 --- a/en/codes/java/chapter_backtracking/n_queens.java +++ b/en/codes/java/chapter_backtracking/n_queens.java @@ -9,7 +9,7 @@ package chapter_backtracking; import java.util.*; public class n_queens { - /* Backtracking algorithm: n queens */ + /* Backtracking algorithm: N queens */ public static void backtrack(int row, int n, List> state, List>> res, boolean[] cols, boolean[] diags1, boolean[] diags2) { // When all rows are placed, record the solution @@ -23,26 +23,26 @@ public class n_queens { } // Traverse all columns for (int col = 0; col < n; col++) { - // Calculate the main and minor diagonals corresponding to the cell + // Calculate the main diagonal and anti-diagonal corresponding to this cell int diag1 = row - col + n - 1; int diag2 = row + col; - // Pruning: do not allow queens on the column, main diagonal, or minor diagonal of the cell + // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // Attempt: place the queen in the cell + // Attempt: place the queen in this cell state.get(row).set(col, "Q"); cols[col] = diags1[diag1] = diags2[diag2] = true; // Place the next row backtrack(row + 1, n, state, res, cols, diags1, diags2); - // Retract: restore the cell to an empty spot + // Backtrack: restore this cell to an empty cell state.get(row).set(col, "#"); cols[col] = diags1[diag1] = diags2[diag2] = false; } } } - /* Solve n queens */ + /* Solve N queens */ public static List>> nQueens(int n) { - // Initialize an n*n size chessboard, where 'Q' represents the queen and '#' represents an empty spot + // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell List> state = new ArrayList<>(); for (int i = 0; i < n; i++) { List row = new ArrayList<>(); @@ -51,9 +51,9 @@ public class n_queens { } state.add(row); } - boolean[] cols = new boolean[n]; // Record columns with queens - boolean[] diags1 = new boolean[2 * n - 1]; // Record main diagonals with queens - boolean[] diags2 = new boolean[2 * n - 1]; // Record minor diagonals with queens + boolean[] cols = new boolean[n]; // Record whether there is a queen in the column + boolean[] diags1 = new boolean[2 * n - 1]; // Record whether there is a queen on the main diagonal + boolean[] diags2 = new boolean[2 * n - 1]; // Record whether there is a queen on the anti-diagonal List>> res = new ArrayList<>(); backtrack(0, n, state, res, cols, diags1, diags2); @@ -65,8 +65,8 @@ public class n_queens { int n = 4; List>> res = nQueens(n); - System.out.println("Input the dimensions of the chessboard as " + n); - System.out.println("Total number of queen placement solutions = " + res.size()); + System.out.println("Input board size is " + n); + System.out.println("Total queen placement solutions: " + res.size() + ""); for (List> state : res) { System.out.println("--------------------"); for (List row : state) { diff --git a/en/codes/java/chapter_backtracking/permutations_i.java b/en/codes/java/chapter_backtracking/permutations_i.java index dd9b44a24..c2366c64a 100644 --- a/en/codes/java/chapter_backtracking/permutations_i.java +++ b/en/codes/java/chapter_backtracking/permutations_i.java @@ -9,7 +9,7 @@ package chapter_backtracking; import java.util.*; public class permutations_i { - /* Backtracking algorithm: Permutation I */ + /* Backtracking algorithm: Permutations I */ public static void backtrack(List state, int[] choices, boolean[] selected, List> res) { // When the state length equals the number of elements, record the solution if (state.size() == choices.length) { @@ -21,19 +21,19 @@ public class permutations_i { int choice = choices[i]; // Pruning: do not allow repeated selection of elements if (!selected[i]) { - // Attempt: make a choice, update the state + // Attempt: make choice, update state selected[i] = true; state.add(choice); // Proceed to the next round of selection backtrack(state, choices, selected, res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state selected[i] = false; state.remove(state.size() - 1); } } } - /* Permutation I */ + /* Permutations I */ static List> permutationsI(int[] nums) { List> res = new ArrayList>(); backtrack(new ArrayList(), nums, new boolean[nums.length], res); diff --git a/en/codes/java/chapter_backtracking/permutations_ii.java b/en/codes/java/chapter_backtracking/permutations_ii.java index b91e0d8fb..a2ca4d431 100644 --- a/en/codes/java/chapter_backtracking/permutations_ii.java +++ b/en/codes/java/chapter_backtracking/permutations_ii.java @@ -9,7 +9,7 @@ package chapter_backtracking; import java.util.*; public class permutations_ii { - /* Backtracking algorithm: Permutation II */ + /* Backtracking algorithm: Permutations II */ static void backtrack(List state, int[] choices, boolean[] selected, List> res) { // When the state length equals the number of elements, record the solution if (state.size() == choices.length) { @@ -22,20 +22,20 @@ public class permutations_ii { int choice = choices[i]; // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements if (!selected[i] && !duplicated.contains(choice)) { - // Attempt: make a choice, update the state - duplicated.add(choice); // Record selected element values + // Attempt: make choice, update state + duplicated.add(choice); // Record the selected element value selected[i] = true; state.add(choice); // Proceed to the next round of selection backtrack(state, choices, selected, res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state selected[i] = false; state.remove(state.size() - 1); } } } - /* Permutation II */ + /* Permutations II */ static List> permutationsII(int[] nums) { List> res = new ArrayList>(); backtrack(new ArrayList(), nums, new boolean[nums.length], res); diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_i_compact.java b/en/codes/java/chapter_backtracking/preorder_traversal_i_compact.java index fc77adbea..a042d7efe 100644 --- a/en/codes/java/chapter_backtracking/preorder_traversal_i_compact.java +++ b/en/codes/java/chapter_backtracking/preorder_traversal_i_compact.java @@ -12,7 +12,7 @@ import java.util.*; public class preorder_traversal_i_compact { static List res; - /* Pre-order traversal: Example one */ + /* Preorder traversal: Example 1 */ static void preOrder(TreeNode root) { if (root == null) { return; @@ -30,7 +30,7 @@ public class preorder_traversal_i_compact { System.out.println("\nInitialize binary tree"); PrintUtil.printTree(root); - // Pre-order traversal + // Preorder traversal res = new ArrayList<>(); preOrder(root); diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_ii_compact.java b/en/codes/java/chapter_backtracking/preorder_traversal_ii_compact.java index 685c7404d..9d90b8857 100644 --- a/en/codes/java/chapter_backtracking/preorder_traversal_ii_compact.java +++ b/en/codes/java/chapter_backtracking/preorder_traversal_ii_compact.java @@ -13,7 +13,7 @@ public class preorder_traversal_ii_compact { static List path; static List> res; - /* Pre-order traversal: Example two */ + /* Preorder traversal: Example 2 */ static void preOrder(TreeNode root) { if (root == null) { return; @@ -26,7 +26,7 @@ public class preorder_traversal_ii_compact { } preOrder(root.left); preOrder(root.right); - // Retract + // Backtrack path.remove(path.size() - 1); } @@ -35,12 +35,12 @@ public class preorder_traversal_ii_compact { System.out.println("\nInitialize binary tree"); PrintUtil.printTree(root); - // Pre-order traversal + // Preorder traversal path = new ArrayList<>(); res = new ArrayList<>(); preOrder(root); - System.out.println("\nOutput all root-to-node 7 paths"); + System.out.println("\nOutput all paths from root node to node 7"); for (List path : res) { List vals = new ArrayList<>(); for (TreeNode node : path) { diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_iii_compact.java b/en/codes/java/chapter_backtracking/preorder_traversal_iii_compact.java index b66a84e1f..0292f06fa 100644 --- a/en/codes/java/chapter_backtracking/preorder_traversal_iii_compact.java +++ b/en/codes/java/chapter_backtracking/preorder_traversal_iii_compact.java @@ -13,7 +13,7 @@ public class preorder_traversal_iii_compact { static List path; static List> res; - /* Pre-order traversal: Example three */ + /* Preorder traversal: Example 3 */ static void preOrder(TreeNode root) { // Pruning if (root == null || root.val == 3) { @@ -27,7 +27,7 @@ public class preorder_traversal_iii_compact { } preOrder(root.left); preOrder(root.right); - // Retract + // Backtrack path.remove(path.size() - 1); } @@ -36,12 +36,12 @@ public class preorder_traversal_iii_compact { System.out.println("\nInitialize binary tree"); PrintUtil.printTree(root); - // Pre-order traversal + // Preorder traversal path = new ArrayList<>(); res = new ArrayList<>(); preOrder(root); - System.out.println("\nOutput all root-to-node 7 paths, not including nodes with value 3"); + System.out.println("\nOutput all paths from root node to node 7, paths do not include nodes with value 3"); for (List path : res) { List vals = new ArrayList<>(); for (TreeNode node : path) { diff --git a/en/codes/java/chapter_backtracking/preorder_traversal_iii_template.java b/en/codes/java/chapter_backtracking/preorder_traversal_iii_template.java index c4dfa71c2..f31847104 100644 --- a/en/codes/java/chapter_backtracking/preorder_traversal_iii_template.java +++ b/en/codes/java/chapter_backtracking/preorder_traversal_iii_template.java @@ -10,7 +10,7 @@ import utils.*; import java.util.*; public class preorder_traversal_iii_template { - /* Determine if the current state is a solution */ + /* Check if the current state is a solution */ static boolean isSolution(List state) { return !state.isEmpty() && state.get(state.size() - 1).val == 7; } @@ -20,7 +20,7 @@ public class preorder_traversal_iii_template { res.add(new ArrayList<>(state)); } - /* Determine if the choice is legal under the current state */ + /* Check if the choice is valid under the current state */ static boolean isValid(List state, TreeNode choice) { return choice != null && choice.val != 3; } @@ -35,22 +35,22 @@ public class preorder_traversal_iii_template { state.remove(state.size() - 1); } - /* Backtracking algorithm: Example three */ + /* Backtracking algorithm: Example 3 */ static void backtrack(List state, List choices, List> res) { - // Check if it's a solution + // Check if it is a solution if (isSolution(state)) { // Record solution recordSolution(state, res); } // Traverse all choices for (TreeNode choice : choices) { - // Pruning: check if the choice is legal + // Pruning: check if the choice is valid if (isValid(state, choice)) { - // Attempt: make a choice, update the state + // Attempt: make choice, update state makeChoice(state, choice); // Proceed to the next round of selection backtrack(state, Arrays.asList(choice.left, choice.right), res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state undoChoice(state, choice); } } @@ -65,7 +65,7 @@ public class preorder_traversal_iii_template { List> res = new ArrayList<>(); backtrack(new ArrayList<>(), Arrays.asList(root), res); - System.out.println("\nOutput all root-to-node 7 paths, requiring paths not to include nodes with value 3"); + System.out.println("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3"); for (List path : res) { List vals = new ArrayList<>(); for (TreeNode node : path) { diff --git a/en/codes/java/chapter_backtracking/subset_sum_i.java b/en/codes/java/chapter_backtracking/subset_sum_i.java index ce231bcbf..fa0f7ad0b 100644 --- a/en/codes/java/chapter_backtracking/subset_sum_i.java +++ b/en/codes/java/chapter_backtracking/subset_sum_i.java @@ -9,7 +9,7 @@ package chapter_backtracking; import java.util.*; public class subset_sum_i { - /* Backtracking algorithm: Subset Sum I */ + /* Backtracking algorithm: Subset sum I */ static void backtrack(List state, int target, int[] choices, int start, List> res) { // When the subset sum equals target, record the solution if (target == 0) { @@ -17,23 +17,23 @@ public class subset_sum_i { return; } // Traverse all choices - // Pruning two: start traversing from start to avoid generating duplicate subsets + // Pruning 2: start traversing from start to avoid generating duplicate subsets for (int i = start; i < choices.length; i++) { - // Pruning one: if the subset sum exceeds target, end the loop immediately + // Pruning 1: if the subset sum exceeds target, end the loop directly // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target if (target - choices[i] < 0) { break; } - // Attempt: make a choice, update target, start + // Attempt: make choice, update target, start state.add(choices[i]); // Proceed to the next round of selection backtrack(state, target - choices[i], choices, i, res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state state.remove(state.size() - 1); } } - /* Solve Subset Sum I */ + /* Solve subset sum I */ static List> subsetSumI(int[] nums, int target) { List state = new ArrayList<>(); // State (subset) Arrays.sort(nums); // Sort nums @@ -50,6 +50,6 @@ public class subset_sum_i { List> res = subsetSumI(nums, target); System.out.println("Input array nums = " + Arrays.toString(nums) + ", target = " + target); - System.out.println("All subsets summing to " + target + " res = " + res); + System.out.println("All subsets with sum equal to " + target + " are res = " + res); } } diff --git a/en/codes/java/chapter_backtracking/subset_sum_i_naive.java b/en/codes/java/chapter_backtracking/subset_sum_i_naive.java index 2fc44ca98..2160d299e 100644 --- a/en/codes/java/chapter_backtracking/subset_sum_i_naive.java +++ b/en/codes/java/chapter_backtracking/subset_sum_i_naive.java @@ -9,7 +9,7 @@ package chapter_backtracking; import java.util.*; public class subset_sum_i_naive { - /* Backtracking algorithm: Subset Sum I */ + /* Backtracking algorithm: Subset sum I */ static void backtrack(List state, int target, int total, int[] choices, List> res) { // When the subset sum equals target, record the solution if (total == target) { @@ -18,20 +18,20 @@ public class subset_sum_i_naive { } // Traverse all choices for (int i = 0; i < choices.length; i++) { - // Pruning: if the subset sum exceeds target, skip that choice + // Pruning: if the subset sum exceeds target, skip this choice if (total + choices[i] > target) { continue; } - // Attempt: make a choice, update elements and total + // Attempt: make choice, update element sum total state.add(choices[i]); // Proceed to the next round of selection backtrack(state, target, total + choices[i], choices, res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state state.remove(state.size() - 1); } } - /* Solve Subset Sum I (including duplicate subsets) */ + /* Solve subset sum I (including duplicate subsets) */ static List> subsetSumINaive(int[] nums, int target) { List state = new ArrayList<>(); // State (subset) int total = 0; // Subset sum @@ -47,7 +47,7 @@ public class subset_sum_i_naive { List> res = subsetSumINaive(nums, target); System.out.println("Input array nums = " + Arrays.toString(nums) + ", target = " + target); - System.out.println("All subsets summing to " + target + " res = " + res); - System.out.println("The result of this method includes duplicate sets"); + System.out.println("All subsets with sum equal to " + target + " are res = " + res); + System.out.println("Please note that this method outputs results containing duplicate sets"); } } diff --git a/en/codes/java/chapter_backtracking/subset_sum_ii.java b/en/codes/java/chapter_backtracking/subset_sum_ii.java index 9a503f672..e0383dd62 100644 --- a/en/codes/java/chapter_backtracking/subset_sum_ii.java +++ b/en/codes/java/chapter_backtracking/subset_sum_ii.java @@ -9,7 +9,7 @@ package chapter_backtracking; import java.util.*; public class subset_sum_ii { - /* Backtracking algorithm: Subset Sum II */ + /* Backtracking algorithm: Subset sum II */ static void backtrack(List state, int target, int[] choices, int start, List> res) { // When the subset sum equals target, record the solution if (target == 0) { @@ -17,28 +17,28 @@ public class subset_sum_ii { return; } // Traverse all choices - // Pruning two: start traversing from start to avoid generating duplicate subsets - // Pruning three: start traversing from start to avoid repeatedly selecting the same element + // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Pruning 3: start traversing from start to avoid repeatedly selecting the same element for (int i = start; i < choices.length; i++) { - // Pruning one: if the subset sum exceeds target, end the loop immediately + // Pruning 1: if the subset sum exceeds target, end the loop directly // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target if (target - choices[i] < 0) { break; } - // Pruning four: if the element equals the left element, it indicates that the search branch is repeated, skip it + // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly if (i > start && choices[i] == choices[i - 1]) { continue; } - // Attempt: make a choice, update target, start + // Attempt: make choice, update target, start state.add(choices[i]); // Proceed to the next round of selection backtrack(state, target - choices[i], choices, i + 1, res); - // Retract: undo the choice, restore to the previous state + // Backtrack: undo choice, restore to previous state state.remove(state.size() - 1); } } - /* Solve Subset Sum II */ + /* Solve subset sum II */ static List> subsetSumII(int[] nums, int target) { List state = new ArrayList<>(); // State (subset) Arrays.sort(nums); // Sort nums @@ -55,6 +55,6 @@ public class subset_sum_ii { List> res = subsetSumII(nums, target); System.out.println("Input array nums = " + Arrays.toString(nums) + ", target = " + target); - System.out.println("All subsets summing to " + target + " res = " + res); + System.out.println("All subsets with sum equal to " + target + " are res = " + res); } } diff --git a/en/codes/java/chapter_computational_complexity/iteration.java b/en/codes/java/chapter_computational_complexity/iteration.java index ce42c9439..0b4216548 100644 --- a/en/codes/java/chapter_computational_complexity/iteration.java +++ b/en/codes/java/chapter_computational_complexity/iteration.java @@ -10,7 +10,7 @@ public class iteration { /* for loop */ static int forLoop(int n) { int res = 0; - // Loop sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n for (int i = 1; i <= n; i++) { res += i; } @@ -21,7 +21,7 @@ public class iteration { static int whileLoop(int n) { int res = 0; int i = 1; // Initialize condition variable - // Loop sum 1, 2, ..., n-1, n + // Sum 1, 2, ..., n-1, n while (i <= n) { res += i; i++; // Update condition variable @@ -33,7 +33,7 @@ public class iteration { static int whileLoopII(int n) { int res = 0; int i = 1; // Initialize condition variable - // Loop sum 1, 4, 10, ... + // Sum 1, 4, 10, ... while (i <= n) { res += i; // Update condition variable @@ -43,7 +43,7 @@ public class iteration { return res; } - /* Double for loop */ + /* Nested for loop */ static String nestedForLoop(int n) { StringBuilder res = new StringBuilder(); // Loop i = 1, 2, ..., n-1, n @@ -62,15 +62,15 @@ public class iteration { int res; res = forLoop(n); - System.out.println("\nSum result of the for loop res = " + res); + System.out.println("\nfor loop sum result res = " + res); res = whileLoop(n); - System.out.println("\nSum result of the while loop res = " + res); + System.out.println("\nwhile loop sum result res = " + res); res = whileLoopII(n); - System.out.println("\nSum result of the while loop (with two updates) res = " + res); + System.out.println("\nwhile loop (two updates) sum result res = " + res); String resStr = nestedForLoop(n); - System.out.println("\nResult of the double for loop traversal = " + resStr); + System.out.println("\nDouble for loop traversal result " + resStr); } } diff --git a/en/codes/java/chapter_computational_complexity/recursion.java b/en/codes/java/chapter_computational_complexity/recursion.java index cb1249abc..b07c38a3a 100644 --- a/en/codes/java/chapter_computational_complexity/recursion.java +++ b/en/codes/java/chapter_computational_complexity/recursion.java @@ -14,25 +14,25 @@ public class recursion { // Termination condition if (n == 1) return 1; - // Recursive: recursive call + // Recurse: recursive call int res = recur(n - 1); // Return: return result return n + res; } - /* Simulate recursion with iteration */ + /* Simulate recursion using iteration */ static int forLoopRecur(int n) { // Use an explicit stack to simulate the system call stack Stack stack = new Stack<>(); int res = 0; - // Recursive: recursive call + // Recurse: recursive call for (int i = n; i > 0; i--) { - // Simulate "recursive" by "pushing onto the stack" + // Simulate "recurse" with "push" stack.push(i); } // Return: return result while (!stack.isEmpty()) { - // Simulate "return" by "popping from the stack" + // Simulate "return" with "pop" res += stack.pop(); } // res = 1+2+3+...+n @@ -48,7 +48,7 @@ public class recursion { return tailRecur(n - 1, res + n); } - /* Fibonacci sequence: Recursion */ + /* Fibonacci sequence: recursion */ static int fib(int n) { // Termination condition f(1) = 0, f(2) = 1 if (n == 1 || n == 2) @@ -65,15 +65,15 @@ public class recursion { int res; res = recur(n); - System.out.println("\nSum result of the recursive function res = " + res); + System.out.println("\nRecursive function sum result res = " + res); res = forLoopRecur(n); - System.out.println("\nSum result using iteration to simulate recursion res = " + res); + System.out.println("\nUsing iteration to simulate recursive sum result res = " + res); res = tailRecur(n, 0); - System.out.println("\nSum result of the tail-recursive function res = " + res); + System.out.println("\nTail recursive function sum result res = " + res); res = fib(n); - System.out.println("\nThe " + n + "th number in the Fibonacci sequence is " + res); + System.out.println("\nThe " + n + "th term of the Fibonacci sequence is " + res); } } diff --git a/en/codes/java/chapter_computational_complexity/space_complexity.java b/en/codes/java/chapter_computational_complexity/space_complexity.java index f158a206e..94d2f99c6 100644 --- a/en/codes/java/chapter_computational_complexity/space_complexity.java +++ b/en/codes/java/chapter_computational_complexity/space_complexity.java @@ -16,26 +16,26 @@ public class space_complexity { return 0; } - /* Constant complexity */ + /* Constant order */ static void constant(int n) { // Constants, variables, objects occupy O(1) space final int a = 0; int b = 0; int[] nums = new int[10000]; ListNode node = new ListNode(0); - // Variables in a loop occupy O(1) space + // Variables in the loop occupy O(1) space for (int i = 0; i < n; i++) { int c = 0; } - // Functions in a loop occupy O(1) space + // Functions in the loop occupy O(1) space for (int i = 0; i < n; i++) { function(); } } - /* Linear complexity */ + /* Linear order */ static void linear(int n) { - // Array of length n occupies O(n) space + // Array of length n uses O(n) space int[] nums = new int[n]; // A list of length n occupies O(n) space List nodes = new ArrayList<>(); @@ -49,7 +49,7 @@ public class space_complexity { } } - /* Linear complexity (recursive implementation) */ + /* Linear order (recursive implementation) */ static void linearRecur(int n) { System.out.println("Recursion n = " + n); if (n == 1) @@ -57,11 +57,11 @@ public class space_complexity { linearRecur(n - 1); } - /* Quadratic complexity */ + /* Exponential order */ static void quadratic(int n) { - // Matrix occupies O(n^2) space + // Matrix uses O(n^2) space int[][] numMatrix = new int[n][n]; - // A two-dimensional list occupies O(n^2) space + // 2D list uses O(n^2) space List> numList = new ArrayList<>(); for (int i = 0; i < n; i++) { List tmp = new ArrayList<>(); @@ -72,17 +72,17 @@ public class space_complexity { } } - /* Quadratic complexity (recursive implementation) */ + /* Quadratic order (recursive implementation) */ static int quadraticRecur(int n) { if (n <= 0) return 0; - // Array nums length = n, n-1, ..., 2, 1 + // Array nums has length n, n-1, ..., 2, 1 int[] nums = new int[n]; - System.out.println("Recursion n = " + n + " in the length of nums = " + nums.length); + System.out.println("In recursion n = " + n + ", nums length = " + nums.length); return quadraticRecur(n - 1); } - /* Exponential complexity (building a full binary tree) */ + /* Driver Code */ static TreeNode buildTree(int n) { if (n == 0) return null; @@ -95,15 +95,15 @@ public class space_complexity { /* Driver Code */ public static void main(String[] args) { int n = 5; - // Constant complexity + // Constant order constant(n); - // Linear complexity + // Linear order linear(n); linearRecur(n); - // Quadratic complexity + // Exponential order quadratic(n); quadraticRecur(n); - // Exponential complexity + // Exponential order TreeNode root = buildTree(n); PrintUtil.printTree(root); } diff --git a/en/codes/java/chapter_computational_complexity/time_complexity.java b/en/codes/java/chapter_computational_complexity/time_complexity.java index 4036f7aa0..06ac065cb 100644 --- a/en/codes/java/chapter_computational_complexity/time_complexity.java +++ b/en/codes/java/chapter_computational_complexity/time_complexity.java @@ -7,7 +7,7 @@ package chapter_computational_complexity; public class time_complexity { - /* Constant complexity */ + /* Constant order */ static int constant(int n) { int count = 0; int size = 100000; @@ -16,7 +16,7 @@ public class time_complexity { return count; } - /* Linear complexity */ + /* Linear order */ static int linear(int n) { int count = 0; for (int i = 0; i < n; i++) @@ -24,20 +24,20 @@ public class time_complexity { return count; } - /* Linear complexity (traversing an array) */ + /* Linear order (traversing array) */ static int arrayTraversal(int[] nums) { int count = 0; - // Loop count is proportional to the length of the array + // Number of iterations is proportional to the array length for (int num : nums) { count++; } return count; } - /* Quadratic complexity */ + /* Exponential order */ static int quadratic(int n) { int count = 0; - // Loop count is squared in relation to the data size n + // Number of iterations is quadratically related to the data size n for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { count++; @@ -46,29 +46,29 @@ public class time_complexity { return count; } - /* Quadratic complexity (bubble sort) */ + /* Quadratic order (bubble sort) */ static int bubbleSort(int[] nums) { int count = 0; // Counter // Outer loop: unsorted range is [0, i] for (int i = nums.length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - count += 3; // Element swap includes 3 individual operations + count += 3; // Element swap includes 3 unit operations } } } return count; } - /* Exponential complexity (loop implementation) */ + /* Exponential order (loop implementation) */ static int exponential(int n) { int count = 0, base = 1; - // Cells split into two every round, forming the sequence 1, 2, 4, 8, ..., 2^(n-1) + // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) for (int i = 0; i < n; i++) { for (int j = 0; j < base; j++) { count++; @@ -79,14 +79,14 @@ public class time_complexity { return count; } - /* Exponential complexity (recursive implementation) */ + /* Exponential order (recursive implementation) */ static int expRecur(int n) { if (n == 1) return 1; return expRecur(n - 1) + expRecur(n - 1) + 1; } - /* Logarithmic complexity (loop implementation) */ + /* Logarithmic order (loop implementation) */ static int logarithmic(int n) { int count = 0; while (n > 1) { @@ -96,14 +96,14 @@ public class time_complexity { return count; } - /* Logarithmic complexity (recursive implementation) */ + /* Logarithmic order (recursive implementation) */ static int logRecur(int n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } - /* Linear logarithmic complexity */ + /* Linearithmic order */ static int linearLogRecur(int n) { if (n <= 1) return 1; @@ -114,12 +114,12 @@ public class time_complexity { return count; } - /* Factorial complexity (recursive implementation) */ + /* Factorial order (recursive implementation) */ static int factorialRecur(int n) { if (n == 0) return 1; int count = 0; - // From 1 split into n + // Split from 1 into n for (int i = 0; i < n; i++) { count += factorialRecur(n - 1); } @@ -128,40 +128,40 @@ public class time_complexity { /* Driver Code */ public static void main(String[] args) { - // Can modify n to experience the trend of operation count changes under various complexities + // You can modify n to run and observe the trend of the number of operations for various complexities int n = 8; System.out.println("Input data size n = " + n); int count = constant(n); - System.out.println("Number of constant complexity operations = " + count); + System.out.println("Constant order operation count = " + count); count = linear(n); - System.out.println("Number of linear complexity operations = " + count); + System.out.println("Linear order operation count = " + count); count = arrayTraversal(new int[n]); - System.out.println("Number of linear complexity operations (traversing the array) = " + count); + System.out.println("Linear order (array traversal) operation count = " + count); count = quadratic(n); - System.out.println("Number of quadratic order operations = " + count); + System.out.println("Quadratic order operation count = " + count); int[] nums = new int[n]; for (int i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] count = bubbleSort(nums); - System.out.println("Number of quadratic order operations (bubble sort) = " + count); + System.out.println("Quadratic order (bubble sort) operation count = " + count); count = exponential(n); - System.out.println("Number of exponential complexity operations (implemented by loop) = " + count); + System.out.println("Exponential order (loop implementation) operation count = " + count); count = expRecur(n); - System.out.println("Number of exponential complexity operations (implemented by recursion) = " + count); + System.out.println("Exponential order (recursive implementation) operation count = " + count); count = logarithmic(n); - System.out.println("Number of logarithmic complexity operations (implemented by loop) = " + count); + System.out.println("Logarithmic order (loop implementation) operation count = " + count); count = logRecur(n); - System.out.println("Number of logarithmic complexity operations (implemented by recursion) = " + count); + System.out.println("Logarithmic order (recursive implementation) operation count = " + count); count = linearLogRecur(n); - System.out.println("Number of linear logarithmic complexity operations (implemented by recursion) = " + count); + System.out.println("Linearithmic order (recursive implementation) operation count = " + count); count = factorialRecur(n); - System.out.println("Number of factorial complexity operations (implemented by recursion) = " + count); + System.out.println("Factorial order (recursive implementation) operation count = " + count); } } diff --git a/en/codes/java/chapter_computational_complexity/worst_best_time_complexity.java b/en/codes/java/chapter_computational_complexity/worst_best_time_complexity.java index 386abb087..d56cce41b 100644 --- a/en/codes/java/chapter_computational_complexity/worst_best_time_complexity.java +++ b/en/codes/java/chapter_computational_complexity/worst_best_time_complexity.java @@ -9,7 +9,7 @@ package chapter_computational_complexity; import java.util.*; public class worst_best_time_complexity { - /* Generate an array with elements {1, 2, ..., n} in a randomly shuffled order */ + /* Generate an array with elements { 1, 2, ..., n }, order shuffled */ static int[] randomNumbers(int n) { Integer[] nums = new Integer[n]; // Generate array nums = { 1, 2, 3, ..., n } @@ -29,8 +29,8 @@ public class worst_best_time_complexity { /* Find the index of number 1 in array nums */ static int findOne(int[] nums) { for (int i = 0; i < nums.length; i++) { - // When element 1 is at the start of the array, achieve best time complexity O(1) - // When element 1 is at the end of the array, achieve worst time complexity O(n) + // When element 1 is at the head of the array, best time complexity O(1) is achieved + // When element 1 is at the tail of the array, worst time complexity O(n) is achieved if (nums[i] == 1) return i; } @@ -43,8 +43,8 @@ public class worst_best_time_complexity { int n = 100; int[] nums = randomNumbers(n); int index = findOne(nums); - System.out.println("\nThe array [ 1, 2, ..., n ] after being shuffled = " + Arrays.toString(nums)); - System.out.println("The index of number 1 is " + index); + System.out.println("\nArray [ 1, 2, ..., n ] after shuffling = " + Arrays.toString(nums)); + System.out.println("Index of number 1 is " + index); } } } diff --git a/en/codes/java/chapter_divide_and_conquer/binary_search_recur.java b/en/codes/java/chapter_divide_and_conquer/binary_search_recur.java index d80340100..caaa690d9 100644 --- a/en/codes/java/chapter_divide_and_conquer/binary_search_recur.java +++ b/en/codes/java/chapter_divide_and_conquer/binary_search_recur.java @@ -9,20 +9,20 @@ package chapter_divide_and_conquer; public class binary_search_recur { /* Binary search: problem f(i, j) */ static int dfs(int[] nums, int target, int i, int j) { - // If the interval is empty, indicating no target element, return -1 + // If the interval is empty, it means there is no target element, return -1 if (i > j) { return -1; } - // Calculate midpoint index m - int m = i + (j - i) / 2; + // Calculate the midpoint index m + int m = (i + j) / 2; if (nums[m] < target) { - // Recursive subproblem f(m+1, j) + // Recursion subproblem f(m+1, j) return dfs(nums, target, m + 1, j); } else if (nums[m] > target) { - // Recursive subproblem f(i, m-1) + // Recursion subproblem f(i, m-1) return dfs(nums, target, i, m - 1); } else { - // Found the target element, thus return its index + // Found the target element, return its index return m; } } @@ -30,7 +30,7 @@ public class binary_search_recur { /* Binary search */ static int binarySearch(int[] nums, int target) { int n = nums.length; - // Solve problem f(0, n-1) + // Solve the problem f(0, n-1) return dfs(nums, target, 0, n - 1); } @@ -38,8 +38,8 @@ public class binary_search_recur { int target = 6; int[] nums = { 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }; - // Binary search (double closed interval) + // Binary search (closed interval on both sides) int index = binarySearch(nums, target); - System.out.println("Index of target element 6 =" + index); + System.out.println("Index of target element 6 = " + index); } } diff --git a/en/codes/java/chapter_divide_and_conquer/build_tree.java b/en/codes/java/chapter_divide_and_conquer/build_tree.java index 678b20377..aaca66ac4 100644 --- a/en/codes/java/chapter_divide_and_conquer/build_tree.java +++ b/en/codes/java/chapter_divide_and_conquer/build_tree.java @@ -10,26 +10,26 @@ import utils.*; import java.util.*; public class build_tree { - /* Build binary tree: Divide and conquer */ + /* Build binary tree: divide and conquer */ static TreeNode dfs(int[] preorder, Map inorderMap, int i, int l, int r) { - // Terminate when subtree interval is empty + // Terminate when the subtree interval is empty if (r - l < 0) return null; - // Initialize root node + // Initialize the root node TreeNode root = new TreeNode(preorder[i]); - // Query m to divide left and right subtrees + // Query m to divide the left and right subtrees int m = inorderMap.get(preorder[i]); - // Subproblem: build left subtree + // Subproblem: build the left subtree root.left = dfs(preorder, inorderMap, i + 1, l, m - 1); - // Subproblem: build right subtree + // Subproblem: build the right subtree root.right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r); - // Return root node + // Return the root node return root; } /* Build binary tree */ static TreeNode buildTree(int[] preorder, int[] inorder) { - // Initialize hash table, storing in-order elements to indices mapping + // Initialize hash map, storing the mapping from inorder elements to indices Map inorderMap = new HashMap<>(); for (int i = 0; i < inorder.length; i++) { inorderMap.put(inorder[i], i); @@ -41,11 +41,11 @@ public class build_tree { public static void main(String[] args) { int[] preorder = { 3, 9, 2, 1, 7 }; int[] inorder = { 9, 3, 1, 2, 7 }; - System.out.println("Pre-order traversal = " + Arrays.toString(preorder)); - System.out.println("In-order traversal = " + Arrays.toString(inorder)); + System.out.println("Preorder traversal = " + Arrays.toString(preorder)); + System.out.println("Inorder traversal = " + Arrays.toString(inorder)); TreeNode root = buildTree(preorder, inorder); - System.out.println("The built binary tree is:"); + System.out.println("The constructed binary tree is:"); PrintUtil.printTree(root); } } diff --git a/en/codes/java/chapter_divide_and_conquer/hanota.java b/en/codes/java/chapter_divide_and_conquer/hanota.java index 8e7ec569d..8671d41f8 100644 --- a/en/codes/java/chapter_divide_and_conquer/hanota.java +++ b/en/codes/java/chapter_divide_and_conquer/hanota.java @@ -9,49 +9,49 @@ package chapter_divide_and_conquer; import java.util.*; public class hanota { - /* Move a disc */ + /* Move a disk */ static void move(List src, List tar) { - // Take out a disc from the top of src + // Take out a disk from the top of src Integer pan = src.remove(src.size() - 1); - // Place the disc on top of tar + // Place the disk on top of tar tar.add(pan); } /* Solve the Tower of Hanoi problem f(i) */ static void dfs(int i, List src, List buf, List tar) { - // If only one disc remains on src, move it to tar + // If there is only one disk left in src, move it directly to tar if (i == 1) { move(src, tar); return; } - // Subproblem f(i-1): move the top i-1 discs from src with the help of tar to buf + // Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf); - // Subproblem f(1): move the remaining one disc from src to tar + // Subproblem f(1): move the remaining disk from src to tar move(src, tar); - // Subproblem f(i-1): move the top i-1 discs from buf with the help of src to tar + // Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar); } /* Solve the Tower of Hanoi problem */ static void solveHanota(List A, List B, List C) { int n = A.size(); - // Move the top n discs from A with the help of B to C + // Move the top n disks from A to C using B dfs(n, A, B, C); } public static void main(String[] args) { - // The tail of the list is the top of the pillar + // The tail of the list is the top of the rod List A = new ArrayList<>(Arrays.asList(5, 4, 3, 2, 1)); List B = new ArrayList<>(); List C = new ArrayList<>(); - System.out.println("Initial state:"); + System.out.println("In initial state:"); System.out.println("A = " + A); System.out.println("B = " + B); System.out.println("C = " + C); solveHanota(A, B, C); - System.out.println("After the discs are moved:"); + System.out.println("After disk movement is complete:"); System.out.println("A = " + A); System.out.println("B = " + B); System.out.println("C = " + C); diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_backtrack.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_backtrack.java index 247020625..ea8e6e4b3 100644 --- a/en/codes/java/chapter_dynamic_programming/climbing_stairs_backtrack.java +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_backtrack.java @@ -11,26 +11,26 @@ import java.util.*; public class climbing_stairs_backtrack { /* Backtracking */ public static void backtrack(List choices, int state, int n, List res) { - // When climbing to the nth step, add 1 to the number of solutions + // When climbing to the n-th stair, add 1 to the solution count if (state == n) res.set(0, res.get(0) + 1); // Traverse all choices for (Integer choice : choices) { - // Pruning: do not allow climbing beyond the nth step + // Pruning: not allowed to go beyond the n-th stair if (state + choice > n) continue; - // Attempt: make a choice, update the state + // Attempt: make choice, update state backtrack(choices, state + choice, n, res); - // Retract + // Backtrack } } /* Climbing stairs: Backtracking */ public static int climbingStairsBacktrack(int n) { - List choices = Arrays.asList(1, 2); // Can choose to climb up 1 step or 2 steps - int state = 0; // Start climbing from the 0th step + List choices = Arrays.asList(1, 2); // Can choose to climb up 1 or 2 stairs + int state = 0; // Start climbing from the 0-th stair List res = new ArrayList<>(); - res.add(0); // Use res[0] to record the number of solutions + res.add(0); // Use res[0] to record the solution count backtrack(choices, state, n, res); return res.get(0); } @@ -39,6 +39,6 @@ public class climbing_stairs_backtrack { int n = 9; int res = climbingStairsBacktrack(n); - System.out.println(String.format("There are %d solutions to climb %d stairs", res, n)); + System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); } } diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_constraint_dp.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_constraint_dp.java index 9af0203ef..e87aaa714 100644 --- a/en/codes/java/chapter_dynamic_programming/climbing_stairs_constraint_dp.java +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_constraint_dp.java @@ -7,14 +7,14 @@ package chapter_dynamic_programming; public class climbing_stairs_constraint_dp { - /* Constrained climbing stairs: Dynamic programming */ + /* Climbing stairs with constraint: Dynamic programming */ static int climbingStairsConstraintDP(int n) { if (n == 1 || n == 2) { return 1; } - // Initialize dp table, used to store subproblem solutions + // Initialize dp table, used to store solutions to subproblems int[][] dp = new int[n + 1][3]; - // Initial state: preset the smallest subproblem solution + // Initial state: preset the solution to the smallest subproblem dp[1][1] = 1; dp[1][2] = 0; dp[2][1] = 0; @@ -31,6 +31,6 @@ public class climbing_stairs_constraint_dp { int n = 9; int res = climbingStairsConstraintDP(n); - System.out.println(String.format("There are %d solutions to climb %d stairs", res, n)); + System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); } } diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs.java index 5997b5ec6..0085fa29d 100644 --- a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs.java +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs.java @@ -26,6 +26,6 @@ public class climbing_stairs_dfs { int n = 9; int res = climbingStairsDFS(n); - System.out.println(String.format("There are %d solutions to climb %d stairs", res, n)); + System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); } } diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs_mem.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs_mem.java index 3aad1fd68..8819aa643 100644 --- a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs_mem.java +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dfs_mem.java @@ -9,12 +9,12 @@ package chapter_dynamic_programming; import java.util.Arrays; public class climbing_stairs_dfs_mem { - /* Memoized search */ + /* Memoization search */ public static int dfs(int i, int[] mem) { // Known dp[1] and dp[2], return them if (i == 1 || i == 2) return i; - // If there is a record for dp[i], return it + // If record dp[i] exists, return it directly if (mem[i] != -1) return mem[i]; // dp[i] = dp[i-1] + dp[i-2] @@ -24,9 +24,9 @@ public class climbing_stairs_dfs_mem { return count; } - /* Climbing stairs: Memoized search */ + /* Climbing stairs: Memoization search */ public static int climbingStairsDFSMem(int n) { - // mem[i] records the total number of solutions for climbing to the ith step, -1 means no record + // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record int[] mem = new int[n + 1]; Arrays.fill(mem, -1); return dfs(n, mem); @@ -36,6 +36,6 @@ public class climbing_stairs_dfs_mem { int n = 9; int res = climbingStairsDFSMem(n); - System.out.println(String.format("There are %d solutions to climb %d stairs", res, n)); + System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); } -} +} \ No newline at end of file diff --git a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dp.java b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dp.java index f1d2c48e9..e43738187 100644 --- a/en/codes/java/chapter_dynamic_programming/climbing_stairs_dp.java +++ b/en/codes/java/chapter_dynamic_programming/climbing_stairs_dp.java @@ -11,9 +11,9 @@ public class climbing_stairs_dp { public static int climbingStairsDP(int n) { if (n == 1 || n == 2) return n; - // Initialize dp table, used to store subproblem solutions + // Initialize dp table, used to store solutions to subproblems int[] dp = new int[n + 1]; - // Initial state: preset the smallest subproblem solution + // Initial state: preset the solution to the smallest subproblem dp[1] = 1; dp[2] = 2; // State transition: gradually solve larger subproblems from smaller ones @@ -40,9 +40,9 @@ public class climbing_stairs_dp { int n = 9; int res = climbingStairsDP(n); - System.out.println(String.format("There are %d solutions to climb %d stairs", res, n)); + System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); res = climbingStairsDPComp(n); - System.out.println(String.format("There are %d solutions to climb %d stairs", res, n)); + System.out.println(String.format("Climbing %d stairs has %d solutions", n, res)); } } diff --git a/en/codes/java/chapter_dynamic_programming/coin_change.java b/en/codes/java/chapter_dynamic_programming/coin_change.java index ae0af6cc5..d1abb4c4c 100644 --- a/en/codes/java/chapter_dynamic_programming/coin_change.java +++ b/en/codes/java/chapter_dynamic_programming/coin_change.java @@ -19,14 +19,14 @@ public class coin_change { for (int a = 1; a <= amt; a++) { dp[0][a] = MAX; } - // State transition: the rest of the rows and columns + // State transition: rest of the rows and columns for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeding the target amount, do not choose coin i + // If exceeds target amount, don't select coin i dp[i][a] = dp[i - 1][a]; } else { - // The smaller value between not choosing and choosing coin i + // The smaller value between not selecting and selecting coin i dp[i][a] = Math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); } } @@ -46,10 +46,10 @@ public class coin_change { for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeding the target amount, do not choose coin i + // If exceeds target amount, don't select coin i dp[a] = dp[a]; } else { - // The smaller value between not choosing and choosing coin i + // The smaller value between not selecting and selecting coin i dp[a] = Math.min(dp[a], dp[a - coins[i - 1]] + 1); } } @@ -63,10 +63,10 @@ public class coin_change { // Dynamic programming int res = coinChangeDP(coins, amt); - System.out.println("The minimum number of coins required to make up the target amount is " + res); + System.out.println("Minimum number of coins needed to make target amount is " + res); // Space-optimized dynamic programming res = coinChangeDPComp(coins, amt); - System.out.println("The minimum number of coins required to make up the target amount is " + res); + System.out.println("Minimum number of coins needed to make target amount is " + res); } } diff --git a/en/codes/java/chapter_dynamic_programming/coin_change_ii.java b/en/codes/java/chapter_dynamic_programming/coin_change_ii.java index 01987fb88..67b85cd9f 100644 --- a/en/codes/java/chapter_dynamic_programming/coin_change_ii.java +++ b/en/codes/java/chapter_dynamic_programming/coin_change_ii.java @@ -20,10 +20,10 @@ public class coin_change_ii { for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeding the target amount, do not choose coin i + // If exceeds target amount, don't select coin i dp[i][a] = dp[i - 1][a]; } else { - // The sum of the two options of not choosing and choosing coin i + // Sum of the two options: not selecting and selecting coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; } } @@ -41,10 +41,10 @@ public class coin_change_ii { for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // If exceeding the target amount, do not choose coin i + // If exceeds target amount, don't select coin i dp[a] = dp[a]; } else { - // The sum of the two options of not choosing and choosing coin i + // Sum of the two options: not selecting and selecting coin i dp[a] = dp[a] + dp[a - coins[i - 1]]; } } @@ -58,10 +58,10 @@ public class coin_change_ii { // Dynamic programming int res = coinChangeIIDP(coins, amt); - System.out.println("The number of coin combinations to make up the target amount is " + res); + System.out.println("Number of coin combinations to make target amount is " + res); // Space-optimized dynamic programming res = coinChangeIIDPComp(coins, amt); - System.out.println("The number of coin combinations to make up the target amount is " + res); + System.out.println("Number of coin combinations to make target amount is " + res); } } diff --git a/en/codes/java/chapter_dynamic_programming/edit_distance.java b/en/codes/java/chapter_dynamic_programming/edit_distance.java index b37c7c13d..cf883b466 100644 --- a/en/codes/java/chapter_dynamic_programming/edit_distance.java +++ b/en/codes/java/chapter_dynamic_programming/edit_distance.java @@ -9,50 +9,50 @@ package chapter_dynamic_programming; import java.util.Arrays; public class edit_distance { - /* Edit distance: Brute force search */ + /* Edit distance: Brute-force search */ static int editDistanceDFS(String s, String t, int i, int j) { // If both s and t are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return the length of t + // If s is empty, return length of t if (i == 0) return j; - // If t is empty, return the length of s + // If t is empty, return length of s if (j == 0) return i; - // If the two characters are equal, skip these two characters + // If two characters are equal, skip both characters if (s.charAt(i - 1) == t.charAt(j - 1)) return editDistanceDFS(s, t, i - 1, j - 1); - // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 int insert = editDistanceDFS(s, t, i, j - 1); int delete = editDistanceDFS(s, t, i - 1, j); int replace = editDistanceDFS(s, t, i - 1, j - 1); - // Return the minimum number of edits + // Return minimum edit steps return Math.min(Math.min(insert, delete), replace) + 1; } - /* Edit distance: Memoized search */ + /* Edit distance: Memoization search */ static int editDistanceDFSMem(String s, String t, int[][] mem, int i, int j) { // If both s and t are empty, return 0 if (i == 0 && j == 0) return 0; - // If s is empty, return the length of t + // If s is empty, return length of t if (i == 0) return j; - // If t is empty, return the length of s + // If t is empty, return length of s if (j == 0) return i; - // If there is a record, return it + // If there's a record, return it directly if (mem[i][j] != -1) return mem[i][j]; - // If the two characters are equal, skip these two characters + // If two characters are equal, skip both characters if (s.charAt(i - 1) == t.charAt(j - 1)) return editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 int insert = editDistanceDFSMem(s, t, mem, i, j - 1); int delete = editDistanceDFSMem(s, t, mem, i - 1, j); int replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1); - // Record and return the minimum number of edits + // Record and return minimum edit steps mem[i][j] = Math.min(Math.min(insert, delete), replace) + 1; return mem[i][j]; } @@ -68,14 +68,14 @@ public class edit_distance { for (int j = 1; j <= m; j++) { dp[0][j] = j; } - // State transition: the rest of the rows and columns + // State transition: rest of the rows and columns for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s.charAt(i - 1) == t.charAt(j - 1)) { - // If the two characters are equal, skip these two characters + // If two characters are equal, skip both characters dp[i][j] = dp[i - 1][j - 1]; } else { - // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; } } @@ -91,22 +91,22 @@ public class edit_distance { for (int j = 1; j <= m; j++) { dp[j] = j; } - // State transition: the rest of the rows + // State transition: rest of the rows for (int i = 1; i <= n; i++) { // State transition: first column int leftup = dp[0]; // Temporarily store dp[i-1, j-1] dp[0] = i; - // State transition: the rest of the columns + // State transition: rest of the columns for (int j = 1; j <= m; j++) { int temp = dp[j]; if (s.charAt(i - 1) == t.charAt(j - 1)) { - // If the two characters are equal, skip these two characters + // If two characters are equal, skip both characters dp[j] = leftup; } else { - // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 dp[j] = Math.min(Math.min(dp[j - 1], dp[j]), leftup) + 1; } - leftup = temp; // Update for the next round of dp[i-1, j-1] + leftup = temp; // Update for next round's dp[i-1, j-1] } } return dp[m]; @@ -117,11 +117,11 @@ public class edit_distance { String t = "pack"; int n = s.length(), m = t.length(); - // Brute force search + // Brute-force search int res = editDistanceDFS(s, t, n, m); System.out.println("Changing " + s + " to " + t + " requires a minimum of " + res + " edits"); - // Memoized search + // Memoization search int[][] mem = new int[n + 1][m + 1]; for (int[] row : mem) Arrays.fill(row, -1); diff --git a/en/codes/java/chapter_dynamic_programming/knapsack.java b/en/codes/java/chapter_dynamic_programming/knapsack.java index a8bf6f3d5..a1b2afcbe 100644 --- a/en/codes/java/chapter_dynamic_programming/knapsack.java +++ b/en/codes/java/chapter_dynamic_programming/knapsack.java @@ -10,46 +10,46 @@ import java.util.Arrays; public class knapsack { - /* 0-1 Knapsack: Brute force search */ + /* 0-1 knapsack: Brute-force search */ static int knapsackDFS(int[] wgt, int[] val, int i, int c) { - // If all items have been chosen or the knapsack has no remaining capacity, return value 0 + // If all items have been selected or knapsack has no remaining capacity, return value 0 if (i == 0 || c == 0) { return 0; } - // If exceeding the knapsack capacity, can only choose not to put it in the knapsack + // If exceeds knapsack capacity, can only choose not to put it in if (wgt[i - 1] > c) { return knapsackDFS(wgt, val, i - 1, c); } // Calculate the maximum value of not putting in and putting in item i int no = knapsackDFS(wgt, val, i - 1, c); int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Return the greater value of the two options + // Return the larger value of the two options return Math.max(no, yes); } - /* 0-1 Knapsack: Memoized search */ + /* 0-1 knapsack: Memoization search */ static int knapsackDFSMem(int[] wgt, int[] val, int[][] mem, int i, int c) { - // If all items have been chosen or the knapsack has no remaining capacity, return value 0 + // If all items have been selected or knapsack has no remaining capacity, return value 0 if (i == 0 || c == 0) { return 0; } - // If there is a record, return it + // If there's a record, return it directly if (mem[i][c] != -1) { return mem[i][c]; } - // If exceeding the knapsack capacity, can only choose not to put it in the knapsack + // If exceeds knapsack capacity, can only choose not to put it in if (wgt[i - 1] > c) { return knapsackDFSMem(wgt, val, mem, i - 1, c); } // Calculate the maximum value of not putting in and putting in item i int no = knapsackDFSMem(wgt, val, mem, i - 1, c); int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // Record and return the greater value of the two options + // Record and return the larger value of the two options mem[i][c] = Math.max(no, yes); return mem[i][c]; } - /* 0-1 Knapsack: Dynamic programming */ + /* 0-1 knapsack: Dynamic programming */ static int knapsackDP(int[] wgt, int[] val, int cap) { int n = wgt.length; // Initialize dp table @@ -58,10 +58,10 @@ public class knapsack { for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeding the knapsack capacity, do not choose item i + // If exceeds knapsack capacity, don't select item i dp[i][c] = dp[i - 1][c]; } else { - // The greater value between not choosing and choosing item i + // The larger value between not selecting and selecting item i dp[i][c] = Math.max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); } } @@ -69,7 +69,7 @@ public class knapsack { return dp[n][cap]; } - /* 0-1 Knapsack: Space-optimized dynamic programming */ + /* 0-1 knapsack: Space-optimized dynamic programming */ static int knapsackDPComp(int[] wgt, int[] val, int cap) { int n = wgt.length; // Initialize dp table @@ -79,7 +79,7 @@ public class knapsack { // Traverse in reverse order for (int c = cap; c >= 1; c--) { if (wgt[i - 1] <= c) { - // The greater value between not choosing and choosing item i + // The larger value between not selecting and selecting item i dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -93,24 +93,24 @@ public class knapsack { int cap = 50; int n = wgt.length; - // Brute force search + // Brute-force search int res = knapsackDFS(wgt, val, n, cap); - System.out.println("The maximum value within the bag capacity is " + res); + System.out.println("Maximum item value not exceeding knapsack capacity is " + res); - // Memoized search + // Memoization search int[][] mem = new int[n + 1][cap + 1]; for (int[] row : mem) { Arrays.fill(row, -1); } res = knapsackDFSMem(wgt, val, mem, n, cap); - System.out.println("The maximum value within the bag capacity is " + res); + System.out.println("Maximum item value not exceeding knapsack capacity is " + res); // Dynamic programming res = knapsackDP(wgt, val, cap); - System.out.println("The maximum value within the bag capacity is " + res); + System.out.println("Maximum item value not exceeding knapsack capacity is " + res); // Space-optimized dynamic programming res = knapsackDPComp(wgt, val, cap); - System.out.println("The maximum value within the bag capacity is " + res); + System.out.println("Maximum item value not exceeding knapsack capacity is " + res); } } diff --git a/en/codes/java/chapter_dynamic_programming/min_cost_climbing_stairs_dp.java b/en/codes/java/chapter_dynamic_programming/min_cost_climbing_stairs_dp.java index 26de37cb4..9e24d7f15 100644 --- a/en/codes/java/chapter_dynamic_programming/min_cost_climbing_stairs_dp.java +++ b/en/codes/java/chapter_dynamic_programming/min_cost_climbing_stairs_dp.java @@ -9,14 +9,14 @@ package chapter_dynamic_programming; import java.util.Arrays; public class min_cost_climbing_stairs_dp { - /* Climbing stairs with minimum cost: Dynamic programming */ + /* Minimum cost climbing stairs: Dynamic programming */ public static int minCostClimbingStairsDP(int[] cost) { int n = cost.length - 1; if (n == 1 || n == 2) return cost[n]; - // Initialize dp table, used to store subproblem solutions + // Initialize dp table, used to store solutions to subproblems int[] dp = new int[n + 1]; - // Initial state: preset the smallest subproblem solution + // Initial state: preset the solution to the smallest subproblem dp[1] = cost[1]; dp[2] = cost[2]; // State transition: gradually solve larger subproblems from smaller ones @@ -26,7 +26,7 @@ public class min_cost_climbing_stairs_dp { return dp[n]; } - /* Climbing stairs with minimum cost: Space-optimized dynamic programming */ + /* Minimum cost climbing stairs: Space-optimized dynamic programming */ public static int minCostClimbingStairsDPComp(int[] cost) { int n = cost.length - 1; if (n == 1 || n == 2) @@ -42,12 +42,12 @@ public class min_cost_climbing_stairs_dp { public static void main(String[] args) { int[] cost = { 0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1 }; - System.out.println(String.format("Input the cost list for stairs as %s", Arrays.toString(cost))); + System.out.println(String.format("Input staircase cost list is %s", Arrays.toString(cost))); int res = minCostClimbingStairsDP(cost); - System.out.println(String.format("Minimum cost to climb the stairs %d", res)); + System.out.println(String.format("Minimum cost to climb staircase is %d", res)); res = minCostClimbingStairsDPComp(cost); - System.out.println(String.format("Minimum cost to climb the stairs %d", res)); + System.out.println(String.format("Minimum cost to climb staircase is %d", res)); } } diff --git a/en/codes/java/chapter_dynamic_programming/min_path_sum.java b/en/codes/java/chapter_dynamic_programming/min_path_sum.java index 6c033ab3d..06e8d5d46 100644 --- a/en/codes/java/chapter_dynamic_programming/min_path_sum.java +++ b/en/codes/java/chapter_dynamic_programming/min_path_sum.java @@ -9,41 +9,41 @@ package chapter_dynamic_programming; import java.util.Arrays; public class min_path_sum { - /* Minimum path sum: Brute force search */ + /* Minimum path sum: Brute-force search */ static int minPathSumDFS(int[][] grid, int i, int j) { // If it's the top-left cell, terminate the search if (i == 0 && j == 0) { return grid[0][0]; } - // If the row or column index is out of bounds, return a +∞ cost + // If row or column index is out of bounds, return +∞ cost if (i < 0 || j < 0) { return Integer.MAX_VALUE; } - // Calculate the minimum path cost from the top-left to (i-1, j) and (i, j-1) + // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) int up = minPathSumDFS(grid, i - 1, j); int left = minPathSumDFS(grid, i, j - 1); - // Return the minimum path cost from the top-left to (i, j) + // Return the minimum path cost from top-left to (i, j) return Math.min(left, up) + grid[i][j]; } - /* Minimum path sum: Memoized search */ + /* Minimum path sum: Memoization search */ static int minPathSumDFSMem(int[][] grid, int[][] mem, int i, int j) { // If it's the top-left cell, terminate the search if (i == 0 && j == 0) { return grid[0][0]; } - // If the row or column index is out of bounds, return a +∞ cost + // If row or column index is out of bounds, return +∞ cost if (i < 0 || j < 0) { return Integer.MAX_VALUE; } - // If there is a record, return it + // If there's a record, return it directly if (mem[i][j] != -1) { return mem[i][j]; } - // The minimum path cost from the left and top cells + // Minimum path cost for left and upper cells int up = minPathSumDFSMem(grid, mem, i - 1, j); int left = minPathSumDFSMem(grid, mem, i, j - 1); - // Record and return the minimum path cost from the top-left to (i, j) + // Record and return the minimum path cost from top-left to (i, j) mem[i][j] = Math.min(left, up) + grid[i][j]; return mem[i][j]; } @@ -62,7 +62,7 @@ public class min_path_sum { for (int i = 1; i < n; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } - // State transition: the rest of the rows and columns + // State transition: rest of the rows and columns for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; @@ -81,11 +81,11 @@ public class min_path_sum { for (int j = 1; j < m; j++) { dp[j] = dp[j - 1] + grid[0][j]; } - // State transition: the rest of the rows + // State transition: rest of the rows for (int i = 1; i < n; i++) { // State transition: first column dp[0] = dp[0] + grid[i][0]; - // State transition: the rest of the columns + // State transition: rest of the columns for (int j = 1; j < m; j++) { dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; } @@ -102,24 +102,24 @@ public class min_path_sum { }; int n = grid.length, m = grid[0].length; - // Brute force search + // Brute-force search int res = minPathSumDFS(grid, n - 1, m - 1); - System.out.println("The minimum path sum from the top left corner to the bottom right corner is " + res); + System.out.println("Minimum path sum from top-left to bottom-right is " + res); - // Memoized search + // Memoization search int[][] mem = new int[n][m]; for (int[] row : mem) { Arrays.fill(row, -1); } res = minPathSumDFSMem(grid, mem, n - 1, m - 1); - System.out.println("The minimum path sum from the top left corner to the bottom right corner is " + res); + System.out.println("Minimum path sum from top-left to bottom-right is " + res); // Dynamic programming res = minPathSumDP(grid); - System.out.println("The minimum path sum from the top left corner to the bottom right corner is " + res); + System.out.println("Minimum path sum from top-left to bottom-right is " + res); // Space-optimized dynamic programming res = minPathSumDPComp(grid); - System.out.println("The minimum path sum from the top left corner to the bottom right corner is " + res); + System.out.println("Minimum path sum from top-left to bottom-right is " + res); } } diff --git a/en/codes/java/chapter_dynamic_programming/unbounded_knapsack.java b/en/codes/java/chapter_dynamic_programming/unbounded_knapsack.java index 9e1f5111a..78bce1a8e 100644 --- a/en/codes/java/chapter_dynamic_programming/unbounded_knapsack.java +++ b/en/codes/java/chapter_dynamic_programming/unbounded_knapsack.java @@ -7,7 +7,7 @@ package chapter_dynamic_programming; public class unbounded_knapsack { - /* Complete knapsack: Dynamic programming */ + /* Unbounded knapsack: Dynamic programming */ static int unboundedKnapsackDP(int[] wgt, int[] val, int cap) { int n = wgt.length; // Initialize dp table @@ -16,10 +16,10 @@ public class unbounded_knapsack { for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeding the knapsack capacity, do not choose item i + // If exceeds knapsack capacity, don't select item i dp[i][c] = dp[i - 1][c]; } else { - // The greater value between not choosing and choosing item i + // The larger value between not selecting and selecting item i dp[i][c] = Math.max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); } } @@ -27,7 +27,7 @@ public class unbounded_knapsack { return dp[n][cap]; } - /* Complete knapsack: Space-optimized dynamic programming */ + /* Unbounded knapsack: Space-optimized dynamic programming */ static int unboundedKnapsackDPComp(int[] wgt, int[] val, int cap) { int n = wgt.length; // Initialize dp table @@ -36,10 +36,10 @@ public class unbounded_knapsack { for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // If exceeding the knapsack capacity, do not choose item i + // If exceeds knapsack capacity, don't select item i dp[c] = dp[c]; } else { - // The greater value between not choosing and choosing item i + // The larger value between not selecting and selecting item i dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -54,10 +54,10 @@ public class unbounded_knapsack { // Dynamic programming int res = unboundedKnapsackDP(wgt, val, cap); - System.out.println("The maximum value within the bag capacity is " + res); + System.out.println("Maximum item value not exceeding knapsack capacity is " + res); // Space-optimized dynamic programming res = unboundedKnapsackDPComp(wgt, val, cap); - System.out.println("The maximum value within the bag capacity is " + res); + System.out.println("Maximum item value not exceeding knapsack capacity is " + res); } } diff --git a/en/codes/java/chapter_graph/graph_adjacency_list.java b/en/codes/java/chapter_graph/graph_adjacency_list.java index c14b24b00..c80b6bca0 100644 --- a/en/codes/java/chapter_graph/graph_adjacency_list.java +++ b/en/codes/java/chapter_graph/graph_adjacency_list.java @@ -52,7 +52,7 @@ class GraphAdjList { public void addVertex(Vertex vet) { if (adjList.containsKey(vet)) return; - // Add a new linked list to the adjacency list + // Add a new linked list in the adjacency list adjList.put(vet, new ArrayList<>()); } @@ -60,15 +60,15 @@ class GraphAdjList { public void removeVertex(Vertex vet) { if (!adjList.containsKey(vet)) throw new IllegalArgumentException(); - // Remove the vertex vet's corresponding linked list from the adjacency list + // Remove the linked list corresponding to vertex vet in the adjacency list adjList.remove(vet); - // Traverse other vertices' linked lists, removing all edges containing vet + // Traverse the linked lists of other vertices and remove all edges containing vet for (List list : adjList.values()) { list.remove(vet); } } - /* Print the adjacency list */ + /* Print adjacency list */ public void print() { System.out.println("Adjacency list ="); for (Map.Entry> pair : adjList.entrySet()) { @@ -82,36 +82,36 @@ class GraphAdjList { public class graph_adjacency_list { public static void main(String[] args) { - /* Initialize undirected graph */ + /* Add edge */ Vertex[] v = Vertex.valsToVets(new int[] { 1, 3, 2, 5, 4 }); Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] }, { v[2], v[3] }, { v[2], v[4] }, { v[3], v[4] } }; GraphAdjList graph = new GraphAdjList(edges); - System.out.println("\nAfter initialization, the graph is"); + System.out.println("\nAfter initialization, graph is"); graph.print(); /* Add edge */ - // Vertices 1, 2 i.e., v[0], v[2] + // Vertices 1, 3 are v[0], v[1] graph.addEdge(v[0], v[2]); - System.out.println("\nAfter adding edge 1-2, the graph is"); + System.out.println("\nAfter adding edge 1-2, graph is"); graph.print(); /* Remove edge */ - // Vertices 1, 3 i.e., v[0], v[1] + // Vertex 3 is v[1] graph.removeEdge(v[0], v[1]); - System.out.println("\nAfter removing edge 1-3, the graph is"); + System.out.println("\nAfter removing edge 1-3, graph is"); graph.print(); /* Add vertex */ Vertex v5 = new Vertex(6); graph.addVertex(v5); - System.out.println("\nAfter adding vertex 6, the graph is"); + System.out.println("\nAfter adding vertex 6, graph is"); graph.print(); /* Remove vertex */ - // Vertex 3 i.e., v[1] + // Vertex 3 is v[1] graph.removeVertex(v[1]); - System.out.println("\nAfter removing vertex 3, the graph is"); + System.out.println("\nAfter removing vertex 3, graph is"); graph.print(); } } diff --git a/en/codes/java/chapter_graph/graph_adjacency_matrix.java b/en/codes/java/chapter_graph/graph_adjacency_matrix.java index 30d718a1f..99b26e6c7 100644 --- a/en/codes/java/chapter_graph/graph_adjacency_matrix.java +++ b/en/codes/java/chapter_graph/graph_adjacency_matrix.java @@ -11,8 +11,8 @@ import java.util.*; /* Undirected graph class based on adjacency matrix */ class GraphAdjMat { - List vertices; // Vertex list, elements represent "vertex value", index represents "vertex index" - List> adjMat; // Adjacency matrix, row and column indices correspond to "vertex index" + List vertices; // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + List> adjMat; // Adjacency matrix, where the row and column indices correspond to the "vertex index" /* Constructor */ public GraphAdjMat(int[] vertices, int[][] edges) { @@ -23,7 +23,7 @@ class GraphAdjMat { addVertex(val); } // Add edge - // Edges elements represent vertex indices + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices for (int[] e : edges) { addEdge(e[0], e[1]); } @@ -37,7 +37,7 @@ class GraphAdjMat { /* Add vertex */ public void addVertex(int val) { int n = size(); - // Add new vertex value to the vertex list + // Add the value of the new vertex to the vertex list vertices.add(val); // Add a row to the adjacency matrix List newRow = new ArrayList<>(n); @@ -55,29 +55,29 @@ class GraphAdjMat { public void removeVertex(int index) { if (index >= size()) throw new IndexOutOfBoundsException(); - // Remove vertex at `index` from the vertex list + // Remove the vertex at index from the vertex list vertices.remove(index); - // Remove the row at `index` from the adjacency matrix + // Remove the row at index from the adjacency matrix adjMat.remove(index); - // Remove the column at `index` from the adjacency matrix + // Remove the column at index from the adjacency matrix for (List row : adjMat) { row.remove(index); } } /* Add edge */ - // Parameters i, j correspond to vertices element indices + // Parameters i, j correspond to the vertices element indices public void addEdge(int i, int j) { // Handle index out of bounds and equality if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw new IndexOutOfBoundsException(); - // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., satisfies (i, j) == (j, i) + // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) adjMat.get(i).set(j, 1); adjMat.get(j).set(i, 1); } /* Remove edge */ - // Parameters i, j correspond to vertices element indices + // Parameters i, j correspond to the vertices element indices public void removeEdge(int i, int j) { // Handle index out of bounds and equality if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) @@ -97,35 +97,35 @@ class GraphAdjMat { public class graph_adjacency_matrix { public static void main(String[] args) { - /* Initialize undirected graph */ - // Edges elements represent vertex indices + /* Add edge */ + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices int[] vertices = { 1, 3, 2, 5, 4 }; int[][] edges = { { 0, 1 }, { 0, 3 }, { 1, 2 }, { 2, 3 }, { 2, 4 }, { 3, 4 } }; GraphAdjMat graph = new GraphAdjMat(vertices, edges); - System.out.println("\nAfter initialization, the graph is"); + System.out.println("\nAfter initialization, graph is"); graph.print(); /* Add edge */ - // Indices of vertices 1, 2 are 0, 2 respectively + // Add vertex graph.addEdge(0, 2); - System.out.println("\nAfter adding edge 1-2, the graph is"); + System.out.println("\nAfter adding edge 1-2, graph is"); graph.print(); /* Remove edge */ - // Indices of vertices 1, 3 are 0, 1 respectively + // Vertices 1, 3 have indices 0, 1 respectively graph.removeEdge(0, 1); - System.out.println("\nAfter removing edge 1-3, the graph is"); + System.out.println("\nAfter removing edge 1-3, graph is"); graph.print(); /* Add vertex */ graph.addVertex(6); - System.out.println("\nAfter adding vertex 6, the graph is"); + System.out.println("\nAfter adding vertex 6, graph is"); graph.print(); /* Remove vertex */ - // Index of vertex 3 is 1 + // Vertex 3 has index 1 graph.removeVertex(1); - System.out.println("\nAfter removing vertex 3, the graph is"); + System.out.println("\nAfter removing vertex 3, graph is"); graph.print(); } } diff --git a/en/codes/java/chapter_graph/graph_bfs.java b/en/codes/java/chapter_graph/graph_bfs.java index ba6519930..e9262f5df 100644 --- a/en/codes/java/chapter_graph/graph_bfs.java +++ b/en/codes/java/chapter_graph/graph_bfs.java @@ -11,11 +11,11 @@ import utils.*; public class graph_bfs { /* Breadth-first traversal */ - // Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex + // Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex static List graphBFS(GraphAdjList graph, Vertex startVet) { // Vertex traversal sequence List res = new ArrayList<>(); - // Hash set, used to record visited vertices + // Hash set for recording vertices that have been visited Set visited = new HashSet<>(); visited.add(startVet); // Queue used to implement BFS @@ -23,28 +23,28 @@ public class graph_bfs { que.offer(startVet); // Starting from vertex vet, loop until all vertices are visited while (!que.isEmpty()) { - Vertex vet = que.poll(); // Dequeue the vertex at the head of the queue + Vertex vet = que.poll(); // Dequeue the front vertex res.add(vet); // Record visited vertex - // Traverse all adjacent vertices of that vertex + // Traverse all adjacent vertices of this vertex for (Vertex adjVet : graph.adjList.get(vet)) { if (visited.contains(adjVet)) - continue; // Skip already visited vertices + continue; // Skip vertices that have been visited que.offer(adjVet); // Only enqueue unvisited vertices - visited.add(adjVet); // Mark the vertex as visited + visited.add(adjVet); // Mark this vertex as visited } } - // Return the vertex traversal sequence + // Return vertex traversal sequence return res; } public static void main(String[] args) { - /* Initialize undirected graph */ + /* Add edge */ Vertex[] v = Vertex.valsToVets(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] }, { v[1], v[4] }, { v[2], v[5] }, { v[3], v[4] }, { v[3], v[6] }, { v[4], v[5] }, { v[4], v[7] }, { v[5], v[8] }, { v[6], v[7] }, { v[7], v[8] } }; GraphAdjList graph = new GraphAdjList(edges); - System.out.println("\nAfter initialization, the graph is"); + System.out.println("\nAfter initialization, graph is"); graph.print(); /* Breadth-first traversal */ diff --git a/en/codes/java/chapter_graph/graph_dfs.java b/en/codes/java/chapter_graph/graph_dfs.java index 7838f566b..fb1efcbf4 100644 --- a/en/codes/java/chapter_graph/graph_dfs.java +++ b/en/codes/java/chapter_graph/graph_dfs.java @@ -13,34 +13,34 @@ public class graph_dfs { /* Depth-first traversal helper function */ static void dfs(GraphAdjList graph, Set visited, List res, Vertex vet) { res.add(vet); // Record visited vertex - visited.add(vet); // Mark the vertex as visited - // Traverse all adjacent vertices of that vertex + visited.add(vet); // Mark this vertex as visited + // Traverse all adjacent vertices of this vertex for (Vertex adjVet : graph.adjList.get(vet)) { if (visited.contains(adjVet)) - continue; // Skip already visited vertices + continue; // Skip vertices that have been visited // Recursively visit adjacent vertices dfs(graph, visited, res, adjVet); } } /* Depth-first traversal */ - // Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex + // Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex static List graphDFS(GraphAdjList graph, Vertex startVet) { // Vertex traversal sequence List res = new ArrayList<>(); - // Hash set, used to record visited vertices + // Hash set for recording vertices that have been visited Set visited = new HashSet<>(); dfs(graph, visited, res, startVet); return res; } public static void main(String[] args) { - /* Initialize undirected graph */ + /* Add edge */ Vertex[] v = Vertex.valsToVets(new int[] { 0, 1, 2, 3, 4, 5, 6 }); Vertex[][] edges = { { v[0], v[1] }, { v[0], v[3] }, { v[1], v[2] }, { v[2], v[5] }, { v[4], v[5] }, { v[5], v[6] } }; GraphAdjList graph = new GraphAdjList(edges); - System.out.println("\nAfter initialization, the graph is"); + System.out.println("\nAfter initialization, graph is"); graph.print(); /* Depth-first traversal */ diff --git a/en/codes/java/chapter_greedy/coin_change_greedy.java b/en/codes/java/chapter_greedy/coin_change_greedy.java index b2da9e637..09201ba8f 100644 --- a/en/codes/java/chapter_greedy/coin_change_greedy.java +++ b/en/codes/java/chapter_greedy/coin_change_greedy.java @@ -9,14 +9,14 @@ package chapter_greedy; import java.util.Arrays; public class coin_change_greedy { - /* Coin change: Greedy */ + /* Coin change: Greedy algorithm */ static int coinChangeGreedy(int[] coins, int amt) { - // Assume coins list is ordered + // Assume coins list is sorted int i = coins.length - 1; int count = 0; - // Loop for greedy selection until no remaining amount + // Loop to make greedy choices until no remaining amount while (amt > 0) { - // Find the smallest coin close to and less than the remaining amount + // Find the coin that is less than and closest to the remaining amount while (i > 0 && coins[i] > amt) { i--; } @@ -29,27 +29,27 @@ public class coin_change_greedy { } public static void main(String[] args) { - // Greedy: can ensure finding a global optimal solution + // Greedy algorithm: Can guarantee finding the global optimal solution int[] coins = { 1, 5, 10, 20, 50, 100 }; int amt = 186; int res = coinChangeGreedy(coins, amt); System.out.println("\ncoins = " + Arrays.toString(coins) + ", amt = " + amt); - System.out.println("The minimum number of coins required to make up " + amt + " is " + res); + System.out.println("Minimum number of coins needed to make " + amt + " is " + res); - // Greedy: cannot ensure finding a global optimal solution + // Greedy algorithm: Cannot guarantee finding the global optimal solution coins = new int[] { 1, 20, 50 }; amt = 60; res = coinChangeGreedy(coins, amt); System.out.println("\ncoins = " + Arrays.toString(coins) + ", amt = " + amt); - System.out.println("The minimum number of coins required to make up " + amt + " is " + res); - System.out.println("In reality, the minimum number needed is 3, i.e., 20 + 20 + 20"); + System.out.println("Minimum number of coins needed to make " + amt + " is " + res); + System.out.println("Actually the minimum number needed is 3, i.e., 20 + 20 + 20"); - // Greedy: cannot ensure finding a global optimal solution + // Greedy algorithm: Cannot guarantee finding the global optimal solution coins = new int[] { 1, 49, 50 }; amt = 98; res = coinChangeGreedy(coins, amt); System.out.println("\ncoins = " + Arrays.toString(coins) + ", amt = " + amt); - System.out.println("The minimum number of coins required to make up " + amt + " is " + res); - System.out.println("In reality, the minimum number needed is 2, i.e., 49 + 49"); + System.out.println("Minimum number of coins needed to make " + amt + " is " + res); + System.out.println("Actually the minimum number needed is 2, i.e., 49 + 49"); } } diff --git a/en/codes/java/chapter_greedy/fractional_knapsack.java b/en/codes/java/chapter_greedy/fractional_knapsack.java index 9bb9303bd..fa0f59354 100644 --- a/en/codes/java/chapter_greedy/fractional_knapsack.java +++ b/en/codes/java/chapter_greedy/fractional_knapsack.java @@ -21,9 +21,9 @@ class Item { } public class fractional_knapsack { - /* Fractional knapsack: Greedy */ + /* Fractional knapsack: Greedy algorithm */ static double fractionalKnapsack(int[] wgt, int[] val, int cap) { - // Create an item list, containing two properties: weight, value + // Create item list with two attributes: weight, value Item[] items = new Item[wgt.length]; for (int i = 0; i < wgt.length; i++) { items[i] = new Item(wgt[i], val[i]); @@ -34,13 +34,13 @@ public class fractional_knapsack { double res = 0; for (Item item : items) { if (item.w <= cap) { - // If the remaining capacity is sufficient, put the entire item into the knapsack + // If remaining capacity is sufficient, put the entire current item into the knapsack res += item.v; cap -= item.w; } else { - // If the remaining capacity is insufficient, put part of the item into the knapsack + // If remaining capacity is insufficient, put part of the current item into the knapsack res += (double) item.v / item.w * cap; - // No remaining capacity left, thus break the loop + // No remaining capacity, so break out of the loop break; } } @@ -54,6 +54,6 @@ public class fractional_knapsack { // Greedy algorithm double res = fractionalKnapsack(wgt, val, cap); - System.out.println("The maximum value within the bag capacity is " + res); + System.out.println("Maximum item value not exceeding knapsack capacity is " + res); } } diff --git a/en/codes/java/chapter_greedy/max_capacity.java b/en/codes/java/chapter_greedy/max_capacity.java index ce1470029..684eb1cf6 100644 --- a/en/codes/java/chapter_greedy/max_capacity.java +++ b/en/codes/java/chapter_greedy/max_capacity.java @@ -7,15 +7,15 @@ package chapter_greedy; public class max_capacity { - /* Maximum capacity: Greedy */ + /* Max capacity: Greedy algorithm */ static int maxCapacity(int[] ht) { - // Initialize i, j, making them split the array at both ends + // Initialize i, j to be at both ends of the array int i = 0, j = ht.length - 1; - // Initial maximum capacity is 0 + // Initial max capacity is 0 int res = 0; // Loop for greedy selection until the two boards meet while (i < j) { - // Update maximum capacity + // Update max capacity int cap = Math.min(ht[i], ht[j]) * (j - i); res = Math.max(res, cap); // Move the shorter board inward @@ -33,6 +33,6 @@ public class max_capacity { // Greedy algorithm int res = maxCapacity(ht); - System.out.println("The maximum capacity is " + res); + System.out.println("Maximum capacity is " + res); } } diff --git a/en/codes/java/chapter_greedy/max_product_cutting.java b/en/codes/java/chapter_greedy/max_product_cutting.java index 006162cec..91788951b 100644 --- a/en/codes/java/chapter_greedy/max_product_cutting.java +++ b/en/codes/java/chapter_greedy/max_product_cutting.java @@ -9,17 +9,17 @@ package chapter_greedy; import java.lang.Math; public class max_product_cutting { - /* Maximum product of cutting: Greedy */ + /* Max product cutting: Greedy algorithm */ public static int maxProductCutting(int n) { // When n <= 3, must cut out a 1 if (n <= 3) { return 1 * (n - 1); } - // Greedy cut out 3s, a is the number of 3s, b is the remainder + // Greedily cut out 3, a is the number of 3s, b is the remainder int a = n / 3; int b = n % 3; if (b == 1) { - // When the remainder is 1, convert a pair of 1 * 3 into 2 * 2 + // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 return (int) Math.pow(3, a - 1) * 2 * 2; } if (b == 2) { @@ -35,6 +35,6 @@ public class max_product_cutting { // Greedy algorithm int res = maxProductCutting(n); - System.out.println("The maximum product of division is " + res); + System.out.println("Maximum cutting product is " + res); } } diff --git a/en/codes/java/chapter_hashing/array_hash_map.java b/en/codes/java/chapter_hashing/array_hash_map.java index 014721c3e..74388f984 100644 --- a/en/codes/java/chapter_hashing/array_hash_map.java +++ b/en/codes/java/chapter_hashing/array_hash_map.java @@ -24,7 +24,7 @@ class ArrayHashMap { private List buckets; public ArrayHashMap() { - // Initialize an array, containing 100 buckets + // Initialize array with 100 buckets buckets = new ArrayList<>(); for (int i = 0; i < 100; i++) { buckets.add(null); @@ -56,7 +56,7 @@ class ArrayHashMap { /* Remove operation */ public void remove(int key) { int index = hashFunc(key); - // Set to null, indicating removal + // Set to null to represent deletion buckets.set(index, null); } @@ -105,23 +105,23 @@ public class array_hash_map { /* Add operation */ // Add key-value pair (key, value) to the hash table - map.put(12836, "Ha"); - map.put(15937, "Luo"); - map.put(16750, "Suan"); - map.put(13276, "Fa"); - map.put(10583, "Ya"); - System.out.println("\nAfter adding, the hash table is\nKey -> Value"); + map.put(12836, "Xiao Ha"); + map.put(15937, "Xiao Luo"); + map.put(16750, "Xiao Suan"); + map.put(13276, "Xiao Fa"); + map.put(10583, "Xiao Ya"); + System.out.println("\nAfter adding is complete, hash table is\nKey -> Value"); map.print(); /* Query operation */ - // Enter key to the hash table, get value + // Input key into hash table to get value String name = map.get(15937); - System.out.println("\nEnter student ID 15937, found name " + name); + System.out.println("\nInput student ID 15937, query name " + name); /* Remove operation */ - // Remove key-value pair (key, value) from the hash table + // Remove key-value pair (key, value) from hash table map.remove(10583); - System.out.println("\nAfter removing 10583, the hash table is\nKey -> Value"); + System.out.println("\nAfter removing 10583, hash table is\nKey -> Value"); map.print(); /* Traverse hash table */ @@ -129,11 +129,11 @@ public class array_hash_map { for (Pair kv : map.pairSet()) { System.out.println(kv.key + " -> " + kv.val); } - System.out.println("\nIndividually traverse keys Key"); + System.out.println("\nTraverse keys only Key"); for (int key : map.keySet()) { System.out.println(key); } - System.out.println("\nIndividually traverse values Value"); + System.out.println("\nTraverse values only Value"); for (String val : map.valueSet()) { System.out.println(val); } diff --git a/en/codes/java/chapter_hashing/built_in_hash.java b/en/codes/java/chapter_hashing/built_in_hash.java index 3d935af7c..4249778b0 100644 --- a/en/codes/java/chapter_hashing/built_in_hash.java +++ b/en/codes/java/chapter_hashing/built_in_hash.java @@ -13,26 +13,26 @@ public class built_in_hash { public static void main(String[] args) { int num = 3; int hashNum = Integer.hashCode(num); - System.out.println("The hash value of integer " + num + " is " + hashNum); + System.out.println("Hash value of integer " + num + " is " + hashNum); boolean bol = true; int hashBol = Boolean.hashCode(bol); - System.out.println("The hash value of boolean " + bol + " is " + hashBol); + System.out.println("Hash value of boolean " + bol + " is " + hashBol); double dec = 3.14159; int hashDec = Double.hashCode(dec); - System.out.println("The hash value of decimal " + dec + " is " + hashDec); + System.out.println("Hash value of decimal " + dec + " is " + hashDec); - String str = "Hello algorithm"; + String str = "Hello Algo"; int hashStr = str.hashCode(); - System.out.println("The hash value of string " + str + " is " + hashStr); + System.out.println("Hash value of string " + str + " is " + hashStr); - Object[] arr = { 12836, "Ha" }; + Object[] arr = { 12836, "Xiao Ha" }; int hashTup = Arrays.hashCode(arr); - System.out.println("The hash value of array " + Arrays.toString(arr) + " is " + hashTup); + System.out.println("Hash value of array " + Arrays.toString(arr) + " is " + hashTup); ListNode obj = new ListNode(0); int hashObj = obj.hashCode(); - System.out.println("The hash value of node object " + obj + " is " + hashObj); + System.out.println("Hash value of node object " + obj + " is " + hashObj); } } diff --git a/en/codes/java/chapter_hashing/hash_map.java b/en/codes/java/chapter_hashing/hash_map.java index 483a36150..9c368d634 100644 --- a/en/codes/java/chapter_hashing/hash_map.java +++ b/en/codes/java/chapter_hashing/hash_map.java @@ -16,23 +16,23 @@ public class hash_map { /* Add operation */ // Add key-value pair (key, value) to the hash table - map.put(12836, "Ha"); - map.put(15937, "Luo"); - map.put(16750, "Suan"); - map.put(13276, "Fa"); - map.put(10583, "Ya"); - System.out.println("\nAfter adding, the hash table is\nKey -> Value"); + map.put(12836, "Xiao Ha"); + map.put(15937, "Xiao Luo"); + map.put(16750, "Xiao Suan"); + map.put(13276, "Xiao Fa"); + map.put(10583, "Xiao Ya"); + System.out.println("\nAfter adding is complete, hash table is\nKey -> Value"); PrintUtil.printHashMap(map); /* Query operation */ - // Enter key to the hash table, get value + // Input key into hash table to get value String name = map.get(15937); - System.out.println("\nEnter student ID 15937, found name " + name); + System.out.println("\nInput student ID 15937, query name " + name); /* Remove operation */ - // Remove key-value pair (key, value) from the hash table + // Remove key-value pair (key, value) from hash table map.remove(10583); - System.out.println("\nAfter removing 10583, the hash table is\nKey -> Value"); + System.out.println("\nAfter removing 10583, hash table is\nKey -> Value"); PrintUtil.printHashMap(map); /* Traverse hash table */ @@ -40,11 +40,11 @@ public class hash_map { for (Map.Entry kv : map.entrySet()) { System.out.println(kv.getKey() + " -> " + kv.getValue()); } - System.out.println("\nIndividually traverse keys Key"); + System.out.println("\nTraverse keys only Key"); for (int key : map.keySet()) { System.out.println(key); } - System.out.println("\nIndividually traverse values Value"); + System.out.println("\nTraverse values only Value"); for (String val : map.values()) { System.out.println(val); } diff --git a/en/codes/java/chapter_hashing/hash_map_chaining.java b/en/codes/java/chapter_hashing/hash_map_chaining.java index cc213d812..2db41716f 100644 --- a/en/codes/java/chapter_hashing/hash_map_chaining.java +++ b/en/codes/java/chapter_hashing/hash_map_chaining.java @@ -9,7 +9,7 @@ package chapter_hashing; import java.util.ArrayList; import java.util.List; -/* Chained address hash table */ +/* Hash table with separate chaining */ class HashMapChaining { int size; // Number of key-value pairs int capacity; // Hash table capacity @@ -43,7 +43,7 @@ class HashMapChaining { String get(int key) { int index = hashFunc(key); List bucket = buckets.get(index); - // Traverse the bucket, if the key is found, return the corresponding val + // Traverse bucket, if key is found, return corresponding val for (Pair pair : bucket) { if (pair.key == key) { return pair.val; @@ -55,20 +55,20 @@ class HashMapChaining { /* Add operation */ void put(int key, String val) { - // When the load factor exceeds the threshold, perform expansion + // When load factor exceeds threshold, perform expansion if (loadFactor() > loadThres) { extend(); } int index = hashFunc(key); List bucket = buckets.get(index); - // Traverse the bucket, if the specified key is encountered, update the corresponding val and return + // Traverse bucket, if specified key is encountered, update corresponding val and return for (Pair pair : bucket) { if (pair.key == key) { pair.val = val; return; } } - // If the key is not found, add the key-value pair to the end + // If key does not exist, append key-value pair to the end Pair pair = new Pair(key, val); bucket.add(pair); size++; @@ -78,7 +78,7 @@ class HashMapChaining { void remove(int key) { int index = hashFunc(key); List bucket = buckets.get(index); - // Traverse the bucket, remove the key-value pair from it + // Traverse bucket and remove key-value pair from it for (Pair pair : bucket) { if (pair.key == key) { bucket.remove(pair); @@ -88,18 +88,18 @@ class HashMapChaining { } } - /* Extend hash table */ + /* Expand hash table */ void extend() { // Temporarily store the original hash table List> bucketsTmp = buckets; - // Initialize the extended new hash table + // Initialize expanded new hash table capacity *= extendRatio; buckets = new ArrayList<>(capacity); for (int i = 0; i < capacity; i++) { buckets.add(new ArrayList<>()); } size = 0; - // Move key-value pairs from the original hash table to the new hash table + // Move key-value pairs from original hash table to new hash table for (List bucket : bucketsTmp) { for (Pair pair : bucket) { put(pair.key, pair.val); @@ -126,23 +126,23 @@ public class hash_map_chaining { /* Add operation */ // Add key-value pair (key, value) to the hash table - map.put(12836, "Ha"); - map.put(15937, "Luo"); - map.put(16750, "Suan"); - map.put(13276, "Fa"); - map.put(10583, "Ya"); - System.out.println("\nAfter adding, the hash table is\nKey -> Value"); + map.put(12836, "Xiao Ha"); + map.put(15937, "Xiao Luo"); + map.put(16750, "Xiao Suan"); + map.put(13276, "Xiao Fa"); + map.put(10583, "Xiao Ya"); + System.out.println("\nAfter adding is complete, hash table is\nKey -> Value"); map.print(); /* Query operation */ - // Enter key to the hash table, get value + // Input key into hash table to get value String name = map.get(13276); - System.out.println("\nEnter student ID 13276, found name " + name); + System.out.println("\nInput student ID 13276, query name " + name); /* Remove operation */ - // Remove key-value pair (key, value) from the hash table + // Remove key-value pair (key, value) from hash table map.remove(12836); - System.out.println("\nAfter removing 12836, the hash table is\nKey -> Value"); + System.out.println("\nAfter removing 12836, hash table is\nKey -> Value"); map.print(); } } diff --git a/en/codes/java/chapter_hashing/hash_map_open_addressing.java b/en/codes/java/chapter_hashing/hash_map_open_addressing.java index fc0ab4c5d..6d157fed8 100644 --- a/en/codes/java/chapter_hashing/hash_map_open_addressing.java +++ b/en/codes/java/chapter_hashing/hash_map_open_addressing.java @@ -6,14 +6,14 @@ package chapter_hashing; -/* Open addressing hash table */ +/* Hash table with open addressing */ class HashMapOpenAddressing { private int size; // Number of key-value pairs private int capacity = 4; // Hash table capacity private final double loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion private final int extendRatio = 2; // Expansion multiplier private Pair[] buckets; // Bucket array - private final Pair TOMBSTONE = new Pair(-1, "-1"); // Removal mark + private final Pair TOMBSTONE = new Pair(-1, "-1"); // Removal marker /* Constructor */ public HashMapOpenAddressing() { @@ -31,15 +31,15 @@ class HashMapOpenAddressing { return (double) size / capacity; } - /* Search for the bucket index corresponding to key */ + /* Search for bucket index corresponding to key */ private int findBucket(int key) { int index = hashFunc(key); int firstTombstone = -1; // Linear probing, break when encountering an empty bucket while (buckets[index] != null) { - // If the key is encountered, return the corresponding bucket index + // If key is encountered, return the corresponding bucket index if (buckets[index].key == key) { - // If a removal mark was encountered earlier, move the key-value pair to that index + // If a removal marker was encountered before, move the key-value pair to that index if (firstTombstone != -1) { buckets[firstTombstone] = buckets[index]; buckets[index] = TOMBSTONE; @@ -47,67 +47,67 @@ class HashMapOpenAddressing { } return index; // Return bucket index } - // Record the first encountered removal mark + // Record the first removal marker encountered if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { firstTombstone = index; } - // Calculate the bucket index, return to the head if exceeding the tail + // Calculate bucket index, wrap around to the head if past the tail index = (index + 1) % capacity; } - // If the key does not exist, return the index of the insertion point + // If key does not exist, return the index for insertion return firstTombstone == -1 ? index : firstTombstone; } /* Query operation */ public String get(int key) { - // Search for the bucket index corresponding to key + // Search for bucket index corresponding to key int index = findBucket(key); - // If the key-value pair is found, return the corresponding val + // If key-value pair is found, return corresponding val if (buckets[index] != null && buckets[index] != TOMBSTONE) { return buckets[index].val; } - // If the key-value pair does not exist, return null + // If key-value pair does not exist, return null return null; } /* Add operation */ public void put(int key, String val) { - // When the load factor exceeds the threshold, perform expansion + // When load factor exceeds threshold, perform expansion if (loadFactor() > loadThres) { extend(); } - // Search for the bucket index corresponding to key + // Search for bucket index corresponding to key int index = findBucket(key); - // If the key-value pair is found, overwrite val and return + // If key-value pair is found, overwrite val and return if (buckets[index] != null && buckets[index] != TOMBSTONE) { buckets[index].val = val; return; } - // If the key-value pair does not exist, add the key-value pair + // If key-value pair does not exist, add the key-value pair buckets[index] = new Pair(key, val); size++; } /* Remove operation */ public void remove(int key) { - // Search for the bucket index corresponding to key + // Search for bucket index corresponding to key int index = findBucket(key); - // If the key-value pair is found, cover it with a removal mark + // If key-value pair is found, overwrite it with removal marker if (buckets[index] != null && buckets[index] != TOMBSTONE) { buckets[index] = TOMBSTONE; size--; } } - /* Extend hash table */ + /* Expand hash table */ private void extend() { // Temporarily store the original hash table Pair[] bucketsTmp = buckets; - // Initialize the extended new hash table + // Initialize expanded new hash table capacity *= extendRatio; buckets = new Pair[capacity]; size = 0; - // Move key-value pairs from the original hash table to the new hash table + // Move key-value pairs from original hash table to new hash table for (Pair pair : bucketsTmp) { if (pair != null && pair != TOMBSTONE) { put(pair.key, pair.val); @@ -136,23 +136,23 @@ public class hash_map_open_addressing { // Add operation // Add key-value pair (key, val) to the hash table - hashmap.put(12836, "Ha"); - hashmap.put(15937, "Luo"); - hashmap.put(16750, "Suan"); - hashmap.put(13276, "Fa"); - hashmap.put(10583, "Ya"); - System.out.println("\nAfter adding, the hash table is\nKey -> Value"); + hashmap.put(12836, "Xiao Ha"); + hashmap.put(15937, "Xiao Luo"); + hashmap.put(16750, "Xiao Suan"); + hashmap.put(13276, "Xiao Fa"); + hashmap.put(10583, "Xiao Ya"); + System.out.println("\nAfter adding is complete, hash table is\nKey -> Value"); hashmap.print(); // Query operation - // Enter key to the hash table, get value val + // Input key into hash table to get value val String name = hashmap.get(13276); - System.out.println("\nEnter student ID 13276, found name " + name); + System.out.println("\nInput student ID 13276, query name " + name); // Remove operation - // Remove key-value pair (key, val) from the hash table + // Remove key-value pair (key, val) from hash table hashmap.remove(16750); - System.out.println("\nAfter removing 16750, the hash table is\nKey -> Value"); + System.out.println("\nAfter removing 16750, hash table is\nKey -> Value"); hashmap.print(); } } diff --git a/en/codes/java/chapter_hashing/simple_hash.java b/en/codes/java/chapter_hashing/simple_hash.java index 74f2777af..1babeb451 100644 --- a/en/codes/java/chapter_hashing/simple_hash.java +++ b/en/codes/java/chapter_hashing/simple_hash.java @@ -48,7 +48,7 @@ public class simple_hash { } public static void main(String[] args) { - String key = "Hello algorithm"; + String key = "Hello Algo"; int hash = addHash(key); System.out.println("Additive hash value is " + hash); diff --git a/en/codes/java/chapter_heap/heap.java b/en/codes/java/chapter_heap/heap.java index 2ae404bd4..68cb08198 100644 --- a/en/codes/java/chapter_heap/heap.java +++ b/en/codes/java/chapter_heap/heap.java @@ -11,38 +11,38 @@ import java.util.*; public class heap { public static void testPush(Queue heap, int val) { - heap.offer(val); // Push the element into heap - System.out.format("\nAfter element %d is added to the heap\n", val); + heap.offer(val); // Element enters heap + System.out.format("\nAfter element %d enters heap\n", val); PrintUtil.printHeap(heap); } public static void testPop(Queue heap) { - int val = heap.poll(); // Pop the element at the heap top - System.out.format("\nAfter the top element %d is removed from the heap\n", val); + int val = heap.poll(); // Time complexity is O(n), not O(nlogn) + System.out.format("\nAfter heap top element %d exits heap\n", val); PrintUtil.printHeap(heap); } public static void main(String[] args) { - /* Initialize the heap */ - // Initialize min-heap + /* Initialize heap */ + // Python's heapq module implements min heap by default Queue minHeap = new PriorityQueue<>(); - // Initialize the max-heap (using lambda expression to modify Comparator if necessary) + // Initialize max heap (modify Comparator using lambda expression) Queue maxHeap = new PriorityQueue<>((a, b) -> b - a); - System.out.println("\nThe following test case is for max-heap"); + System.out.println("\nThe following test cases are for max heap"); - /* Push the element into heap */ + /* Element enters heap */ testPush(maxHeap, 1); testPush(maxHeap, 3); testPush(maxHeap, 2); testPush(maxHeap, 5); testPush(maxHeap, 4); - /* Access heap top element */ + /* Check if heap is empty */ int peek = maxHeap.peek(); - System.out.format("\nTop element of the heap is %d\n", peek); + System.out.format("\nHeap top element is %d\n", peek); - /* Pop the element at the heap top */ + /* Time complexity is O(n), not O(nlogn) */ testPop(maxHeap); testPop(maxHeap); testPop(maxHeap); @@ -51,16 +51,16 @@ public class heap { /* Get heap size */ int size = maxHeap.size(); - System.out.format("\nNumber of elements in the heap is %d\n", size); + System.out.format("\nHeap element count is %d\n", size); - /* Determine if heap is empty */ + /* Check if heap is empty */ boolean isEmpty = maxHeap.isEmpty(); - System.out.format("\nIs the heap empty %b\n", isEmpty); + System.out.format("\nHeap is empty %b\n", isEmpty); - /* Enter list and build heap */ + /* Input list and build heap */ // Time complexity is O(n), not O(nlogn) minHeap = new PriorityQueue<>(Arrays.asList(1, 3, 2, 5, 4)); - System.out.println("\nEnter list and build min-heap"); + System.out.println("\nAfter inputting list and building min heap"); PrintUtil.printHeap(minHeap); } } diff --git a/en/codes/java/chapter_heap/my_heap.java b/en/codes/java/chapter_heap/my_heap.java index de2b21a13..0698e060d 100644 --- a/en/codes/java/chapter_heap/my_heap.java +++ b/en/codes/java/chapter_heap/my_heap.java @@ -9,16 +9,16 @@ package chapter_heap; import utils.*; import java.util.*; -/* Max-heap */ +/* Max heap */ class MaxHeap { - // Use list instead of array to avoid the need for resizing + // Use list instead of array, no need to consider capacity expansion private List maxHeap; /* Constructor, build heap based on input list */ public MaxHeap(List nums) { - // Add all list elements into the heap + // Add list elements to heap as is maxHeap = new ArrayList<>(nums); - // Heapify all nodes except leaves + // Heapify all nodes except leaf nodes for (int i = parent(size() - 1); i >= 0; i--) { siftDown(i); } @@ -36,7 +36,7 @@ class MaxHeap { /* Get index of parent node */ private int parent(int i) { - return (i - 1) / 2; // Integer division down + return (i - 1) / 2; // Floor division } /* Swap elements */ @@ -51,17 +51,17 @@ class MaxHeap { return maxHeap.size(); } - /* Determine if heap is empty */ + /* Check if heap is empty */ public boolean isEmpty() { return size() == 0; } - /* Access heap top element */ + /* Access top element */ public int peek() { return maxHeap.get(0); } - /* Push the element into heap */ + /* Element enters heap */ public void push(int val) { // Add node maxHeap.add(val); @@ -69,46 +69,46 @@ class MaxHeap { siftUp(size() - 1); } - /* Start heapifying node i, from bottom to top */ + /* Starting from node i, heapify from bottom to top */ private void siftUp(int i) { while (true) { // Get parent node of node i int p = parent(i); - // When "crossing the root node" or "node does not need repair", end heapification + // When "crossing root node" or "node needs no repair", end heapify if (p < 0 || maxHeap.get(i) <= maxHeap.get(p)) break; // Swap two nodes swap(i, p); - // Loop upwards heapification + // Loop upward heapify i = p; } } /* Element exits heap */ public int pop() { - // Empty handling + // Handle empty case if (isEmpty()) throw new IndexOutOfBoundsException(); - // Swap the root node with the rightmost leaf node (swap the first element with the last element) + // Delete node swap(0, size() - 1); // Remove node int val = maxHeap.remove(size() - 1); - // Heapify from top to bottom + // Return top element siftDown(0); // Return heap top element return val; } - /* Start heapifying node i, from top to bottom */ + /* Starting from node i, heapify from top to bottom */ private void siftDown(int i) { while (true) { - // Determine the largest node among i, l, r, noted as ma + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break int l = left(i), r = right(i), ma = i; if (l < size() && maxHeap.get(l) > maxHeap.get(ma)) ma = l; if (r < size() && maxHeap.get(r) > maxHeap.get(ma)) ma = r; - // If node i is the largest or indices l, r are out of bounds, no further heapification needed, break + // Swap two nodes if (ma == i) break; // Swap two nodes @@ -118,7 +118,7 @@ class MaxHeap { } } - /* Print heap (binary tree) */ + /* Driver Code */ public void print() { Queue queue = new PriorityQueue<>((a, b) -> { return b - a; }); queue.addAll(maxHeap); @@ -128,32 +128,32 @@ class MaxHeap { public class my_heap { public static void main(String[] args) { - /* Initialize max-heap */ + /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ MaxHeap maxHeap = new MaxHeap(Arrays.asList(9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2)); - System.out.println("\nEnter list and build heap"); + System.out.println("\nAfter inputting list and building heap"); maxHeap.print(); - /* Access heap top element */ + /* Check if heap is empty */ int peek = maxHeap.peek(); - System.out.format("\nTop element of the heap is %d\n", peek); + System.out.format("\nHeap top element is %d\n", peek); - /* Push the element into heap */ + /* Element enters heap */ int val = 7; maxHeap.push(val); - System.out.format("\nAfter element %d is added to the heap\n", val); + System.out.format("\nAfter element %d enters heap\n", val); maxHeap.print(); - /* Pop the element at the heap top */ + /* Time complexity is O(n), not O(nlogn) */ peek = maxHeap.pop(); - System.out.format("\nAfter the top element %d is removed from the heap\n", peek); + System.out.format("\nAfter heap top element %d exits heap\n", peek); maxHeap.print(); /* Get heap size */ int size = maxHeap.size(); - System.out.format("\nNumber of elements in the heap is %d\n", size); + System.out.format("\nHeap element count is %d\n", size); - /* Determine if heap is empty */ + /* Check if heap is empty */ boolean isEmpty = maxHeap.isEmpty(); - System.out.format("\nIs the heap empty %b\n", isEmpty); + System.out.format("\nHeap is empty %b\n", isEmpty); } } diff --git a/en/codes/java/chapter_heap/top_k.java b/en/codes/java/chapter_heap/top_k.java index afbb5be1e..c8a488111 100644 --- a/en/codes/java/chapter_heap/top_k.java +++ b/en/codes/java/chapter_heap/top_k.java @@ -10,17 +10,17 @@ import utils.*; import java.util.*; public class top_k { - /* Using heap to find the largest k elements in an array */ + /* Find the largest k elements in array based on heap */ static Queue topKHeap(int[] nums, int k) { - // Initialize min-heap + // Python's heapq module implements min heap by default Queue heap = new PriorityQueue(); - // Enter the first k elements of the array into the heap + // Enter the first k elements of array into heap for (int i = 0; i < k; i++) { heap.offer(nums[i]); } - // From the k+1th element, keep the heap length as k + // Starting from the (k+1)th element, maintain heap length as k for (int i = k; i < nums.length; i++) { - // If the current element is larger than the heap top element, remove the heap top element and enter the current element into the heap + // If current element is greater than top element, top element exits heap, current element enters heap if (nums[i] > heap.peek()) { heap.poll(); heap.offer(nums[i]); diff --git a/en/codes/java/chapter_searching/binary_search.java b/en/codes/java/chapter_searching/binary_search.java index 93c34dd36..8ede4897b 100644 --- a/en/codes/java/chapter_searching/binary_search.java +++ b/en/codes/java/chapter_searching/binary_search.java @@ -7,39 +7,39 @@ package chapter_searching; public class binary_search { - /* Binary search (double closed interval) */ + /* Binary search (closed interval on both sides) */ static int binarySearch(int[] nums, int target) { - // Initialize double closed interval [0, n-1], i.e., i, j point to the first element and last element of the array respectively + // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array int i = 0, j = nums.length - 1; - // Loop until the search interval is empty (when i > j, it is empty) + // Loop, exit when the search interval is empty (empty when i > j) while (i <= j) { - int m = i + (j - i) / 2; // Calculate midpoint index m - if (nums[m] < target) // This situation indicates that target is in the interval [m+1, j] + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) // This means target is in the interval [m+1, j] i = m + 1; - else if (nums[m] > target) // This situation indicates that target is in the interval [i, m-1] + else if (nums[m] > target) // This means target is in the interval [i, m-1] j = m - 1; - else // Found the target element, thus return its index + else // Found the target element, return its index return m; } - // Did not find the target element, thus return -1 + // Target element not found, return -1 return -1; } - /* Binary search (left closed right open interval) */ + /* Binary search (left-closed right-open interval) */ static int binarySearchLCRO(int[] nums, int target) { - // Initialize left closed right open interval [0, n), i.e., i, j point to the first element and the last element +1 of the array respectively + // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 int i = 0, j = nums.length; - // Loop until the search interval is empty (when i = j, it is empty) + // Loop, exit when the search interval is empty (empty when i = j) while (i < j) { - int m = i + (j - i) / 2; // Calculate midpoint index m - if (nums[m] < target) // This situation indicates that target is in the interval [m+1, j) + int m = i + (j - i) / 2; // Calculate the midpoint index m + if (nums[m] < target) // This means target is in the interval [m+1, j) i = m + 1; - else if (nums[m] > target) // This situation indicates that target is in the interval [i, m) + else if (nums[m] > target) // This means target is in the interval [i, m) j = m; - else // Found the target element, thus return its index + else // Found the target element, return its index return m; } - // Did not find the target element, thus return -1 + // Target element not found, return -1 return -1; } @@ -47,12 +47,12 @@ public class binary_search { int target = 6; int[] nums = { 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }; - /* Binary search (double closed interval) */ + /* Binary search (closed interval on both sides) */ int index = binarySearch(nums, target); - System.out.println("Index of target element 6 =" + index); + System.out.println("Index of target element 6 = " + index); - /* Binary search (left closed right open interval) */ + /* Binary search (left-closed right-open interval) */ index = binarySearchLCRO(nums, target); - System.out.println("Index of target element 6 =" + index); + System.out.println("Index of target element 6 = " + index); } } diff --git a/en/codes/java/chapter_searching/binary_search_edge.java b/en/codes/java/chapter_searching/binary_search_edge.java index 54a668808..8d6684dfa 100644 --- a/en/codes/java/chapter_searching/binary_search_edge.java +++ b/en/codes/java/chapter_searching/binary_search_edge.java @@ -11,7 +11,7 @@ public class binary_search_edge { static int binarySearchLeftEdge(int[] nums, int target) { // Equivalent to finding the insertion point of target int i = binary_search_insertion.binarySearchInsertion(nums, target); - // Did not find target, thus return -1 + // Target not found, return -1 if (i == nums.length || nums[i] != target) { return -1; } @@ -25,7 +25,7 @@ public class binary_search_edge { int i = binary_search_insertion.binarySearchInsertion(nums, target + 1); // j points to the rightmost target, i points to the first element greater than target int j = i - 1; - // Did not find target, thus return -1 + // Target not found, return -1 if (j == -1 || nums[j] != target) { return -1; } @@ -38,12 +38,12 @@ public class binary_search_edge { int[] nums = { 1, 3, 6, 6, 6, 6, 6, 10, 12, 15 }; System.out.println("\nArray nums = " + java.util.Arrays.toString(nums)); - // Binary search for left and right boundaries + // Binary search left and right boundaries for (int target : new int[] { 6, 7 }) { int index = binarySearchLeftEdge(nums, target); - System.out.println("The leftmost index of element " + target + " is " + index); + System.out.println("Index of leftmost element " + target + " is " + index); index = binarySearchRightEdge(nums, target); - System.out.println("The rightmost index of element " + target + " is " + index); + System.out.println("Index of rightmost element " + target + " is " + index); } } } diff --git a/en/codes/java/chapter_searching/binary_search_insertion.java b/en/codes/java/chapter_searching/binary_search_insertion.java index 0ce5ff833..0e7bb97fd 100644 --- a/en/codes/java/chapter_searching/binary_search_insertion.java +++ b/en/codes/java/chapter_searching/binary_search_insertion.java @@ -9,32 +9,32 @@ package chapter_searching; class binary_search_insertion { /* Binary search for insertion point (no duplicate elements) */ static int binarySearchInsertionSimple(int[] nums, int target) { - int i = 0, j = nums.length - 1; // Initialize double closed interval [0, n-1] + int i = 0, j = nums.length - 1; // Initialize closed interval [0, n-1] while (i <= j) { - int m = i + (j - i) / 2; // Calculate midpoint index m + int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // Target is in interval [m+1, j] + i = m + 1; // target is in the interval [m+1, j] } else if (nums[m] > target) { - j = m - 1; // Target is in interval [i, m-1] + j = m - 1; // target is in the interval [i, m-1] } else { return m; // Found target, return insertion point m } } - // Did not find target, return insertion point i + // Target not found, return insertion point i return i; } /* Binary search for insertion point (with duplicate elements) */ static int binarySearchInsertion(int[] nums, int target) { - int i = 0, j = nums.length - 1; // Initialize double closed interval [0, n-1] + int i = 0, j = nums.length - 1; // Initialize closed interval [0, n-1] while (i <= j) { - int m = i + (j - i) / 2; // Calculate midpoint index m + int m = i + (j - i) / 2; // Calculate the midpoint index m if (nums[m] < target) { - i = m + 1; // Target is in interval [m+1, j] + i = m + 1; // target is in the interval [m+1, j] } else if (nums[m] > target) { - j = m - 1; // Target is in interval [i, m-1] + j = m - 1; // target is in the interval [i, m-1] } else { - j = m - 1; // First element less than target is in interval [i, m-1] + j = m - 1; // The first element less than target is in the interval [i, m-1] } } // Return insertion point i @@ -48,7 +48,7 @@ class binary_search_insertion { // Binary search for insertion point for (int target : new int[] { 6, 9 }) { int index = binarySearchInsertionSimple(nums, target); - System.out.println("The insertion point index for element " + target + " is " + index); + System.out.println("Insertion point index for element " + target + " is " + index); } // Array with duplicate elements @@ -57,7 +57,7 @@ class binary_search_insertion { // Binary search for insertion point for (int target : new int[] { 2, 6, 20 }) { int index = binarySearchInsertion(nums, target); - System.out.println("The insertion point index for element " + target + " is " + index); + System.out.println("Insertion point index for element " + target + " is " + index); } } } diff --git a/en/codes/java/chapter_searching/hashing_search.java b/en/codes/java/chapter_searching/hashing_search.java index 568984091..ce920d6d2 100644 --- a/en/codes/java/chapter_searching/hashing_search.java +++ b/en/codes/java/chapter_searching/hashing_search.java @@ -13,14 +13,14 @@ public class hashing_search { /* Hash search (array) */ static int hashingSearchArray(Map map, int target) { // Hash table's key: target element, value: index - // If the hash table does not contain this key, return -1 + // If this key does not exist in the hash table, return -1 return map.getOrDefault(target, -1); } /* Hash search (linked list) */ static ListNode hashingSearchLinkedList(Map map, int target) { // Hash table key: target node value, value: node object - // If the key is not in the hash table, return null + // If key is not in hash table, return null return map.getOrDefault(target, null); } @@ -35,7 +35,7 @@ public class hashing_search { map.put(nums[i], i); // key: element, value: index } int index = hashingSearchArray(map, target); - System.out.println("The index of target element 3 is " + index); + System.out.println("Index of target element 3 = " + index); /* Hash search (linked list) */ ListNode head = ListNode.arrToLinkedList(nums); @@ -46,6 +46,6 @@ public class hashing_search { head = head.next; } ListNode node = hashingSearchLinkedList(map1, target); - System.out.println("The corresponding node object for target node value 3 is " + node); + System.out.println("Node object corresponding to target node value 3 is " + node); } } diff --git a/en/codes/java/chapter_searching/linear_search.java b/en/codes/java/chapter_searching/linear_search.java index eb7a2f84a..4398e8209 100644 --- a/en/codes/java/chapter_searching/linear_search.java +++ b/en/codes/java/chapter_searching/linear_search.java @@ -13,24 +13,24 @@ public class linear_search { static int linearSearchArray(int[] nums, int target) { // Traverse array for (int i = 0; i < nums.length; i++) { - // Found the target element, thus return its index + // Found the target element, return its index if (nums[i] == target) return i; } - // Did not find the target element, thus return -1 + // Target element not found, return -1 return -1; } /* Linear search (linked list) */ static ListNode linearSearchLinkedList(ListNode head, int target) { - // Traverse the list + // Traverse the linked list while (head != null) { // Found the target node, return it if (head.val == target) return head; head = head.next; } - // If the target node is not found, return null + // Target node not found, return null return null; } @@ -40,11 +40,11 @@ public class linear_search { /* Perform linear search in array */ int[] nums = { 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 }; int index = linearSearchArray(nums, target); - System.out.println("The index of target element 3 is " + index); + System.out.println("Index of target element 3 = " + index); /* Perform linear search in linked list */ ListNode head = ListNode.arrToLinkedList(nums); ListNode node = linearSearchLinkedList(head, target); - System.out.println("The corresponding node object for target node value 3 is " + node); + System.out.println("Node object corresponding to target node value 3 is " + node); } } diff --git a/en/codes/java/chapter_searching/two_sum.java b/en/codes/java/chapter_searching/two_sum.java index 22dbd5d83..34adfcb07 100644 --- a/en/codes/java/chapter_searching/two_sum.java +++ b/en/codes/java/chapter_searching/two_sum.java @@ -9,10 +9,10 @@ package chapter_searching; import java.util.*; public class two_sum { - /* Method one: Brute force enumeration */ + /* Method 1: Brute force enumeration */ static int[] twoSumBruteForce(int[] nums, int target) { int size = nums.length; - // Two-layer loop, time complexity is O(n^2) + // Two nested loops, time complexity is O(n^2) for (int i = 0; i < size - 1; i++) { for (int j = i + 1; j < size; j++) { if (nums[i] + nums[j] == target) @@ -22,12 +22,12 @@ public class two_sum { return new int[0]; } - /* Method two: Auxiliary hash table */ + /* Method 2: Auxiliary hash table */ static int[] twoSumHashTable(int[] nums, int target) { int size = nums.length; // Auxiliary hash table, space complexity is O(n) Map dic = new HashMap<>(); - // Single-layer loop, time complexity is O(n) + // Single loop, time complexity is O(n) for (int i = 0; i < size; i++) { if (dic.containsKey(target - nums[i])) { return new int[] { dic.get(target - nums[i]), i }; @@ -43,11 +43,11 @@ public class two_sum { int target = 13; // ====== Driver Code ====== - // Method one + // Method 1 int[] res = twoSumBruteForce(nums, target); - System.out.println("Method one res = " + Arrays.toString(res)); - // Method two + System.out.println("Method 1 res = " + Arrays.toString(res)); + // Method 2 res = twoSumHashTable(nums, target); - System.out.println("Method two res = " + Arrays.toString(res)); + System.out.println("Method 2 res = " + Arrays.toString(res)); } } diff --git a/en/codes/java/chapter_sorting/bubble_sort.java b/en/codes/java/chapter_sorting/bubble_sort.java index 3880789ae..a6b2325d4 100644 --- a/en/codes/java/chapter_sorting/bubble_sort.java +++ b/en/codes/java/chapter_sorting/bubble_sort.java @@ -13,7 +13,7 @@ public class bubble_sort { static void bubbleSort(int[] nums) { // Outer loop: unsorted range is [0, i] for (int i = nums.length - 1; i > 0; i--) { - // Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] @@ -25,33 +25,33 @@ public class bubble_sort { } } - /* Bubble sort (optimized with flag) */ + /* Bubble sort (flag optimization) */ static void bubbleSortWithFlag(int[] nums) { // Outer loop: unsorted range is [0, i] for (int i = nums.length - 1; i > 0; i--) { boolean flag = false; // Initialize flag - // Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - flag = true; // Record swapped elements + flag = true; // Record element swap } } if (!flag) - break; // If no elements were swapped in this round of "bubbling", exit + break; // No elements were swapped in this round of "bubbling", exit directly } } public static void main(String[] args) { int[] nums = { 4, 1, 3, 1, 5, 2 }; bubbleSort(nums); - System.out.println("After bubble sort, nums = " + Arrays.toString(nums)); + System.out.println("After bubble sort completes, nums = " + Arrays.toString(nums)); int[] nums1 = { 4, 1, 3, 1, 5, 2 }; bubbleSortWithFlag(nums1); - System.out.println("After bubble sort, nums1 = " + Arrays.toString(nums1)); + System.out.println("After bubble sort completes, nums1 = " + Arrays.toString(nums1)); } } diff --git a/en/codes/java/chapter_sorting/bucket_sort.java b/en/codes/java/chapter_sorting/bucket_sort.java index 07accafc1..9b76f24ea 100644 --- a/en/codes/java/chapter_sorting/bucket_sort.java +++ b/en/codes/java/chapter_sorting/bucket_sort.java @@ -39,9 +39,9 @@ public class bucket_sort { } public static void main(String[] args) { - // Assume input data is floating point, range [0, 1) + // Assume input data is floating point, interval [0, 1) float[] nums = { 0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f }; bucketSort(nums); - System.out.println("After bucket sort, nums = " + Arrays.toString(nums)); + System.out.println("After bucket sort completes, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_sorting/counting_sort.java b/en/codes/java/chapter_sorting/counting_sort.java index a240791ab..b9555e4e3 100644 --- a/en/codes/java/chapter_sorting/counting_sort.java +++ b/en/codes/java/chapter_sorting/counting_sort.java @@ -17,7 +17,7 @@ public class counting_sort { for (int num : nums) { m = Math.max(m, num); } - // 2. Count the occurrence of each digit + // 2. Count the occurrence of each number // counter[num] represents the occurrence of num int[] counter = new int[m + 1]; for (int num : nums) { @@ -40,7 +40,7 @@ public class counting_sort { for (int num : nums) { m = Math.max(m, num); } - // 2. Count the occurrence of each digit + // 2. Count the occurrence of each number // counter[num] represents the occurrence of num int[] counter = new int[m + 1]; for (int num : nums) { @@ -69,10 +69,10 @@ public class counting_sort { public static void main(String[] args) { int[] nums = { 1, 0, 1, 2, 0, 4, 0, 2, 2, 4 }; countingSortNaive(nums); - System.out.println("After count sort (unable to sort objects), nums = " + Arrays.toString(nums)); + System.out.println("After counting sort (cannot sort objects) completes, nums = " + Arrays.toString(nums)); int[] nums1 = { 1, 0, 1, 2, 0, 4, 0, 2, 2, 4 }; countingSort(nums1); - System.out.println("After count sort, nums1 = " + Arrays.toString(nums1)); + System.out.println("After counting sort completes, nums1 = " + Arrays.toString(nums1)); } } diff --git a/en/codes/java/chapter_sorting/heap_sort.java b/en/codes/java/chapter_sorting/heap_sort.java index 1931ef562..30d0569ce 100644 --- a/en/codes/java/chapter_sorting/heap_sort.java +++ b/en/codes/java/chapter_sorting/heap_sort.java @@ -12,7 +12,7 @@ public class heap_sort { /* Heap length is n, start heapifying node i, from top to bottom */ public static void siftDown(int[] nums, int n, int i) { while (true) { - // Determine the largest node among i, l, r, noted as ma + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break int l = 2 * i + 1; int r = 2 * i + 2; int ma = i; @@ -20,7 +20,7 @@ public class heap_sort { ma = l; if (r < n && nums[r] > nums[ma]) ma = r; - // If node i is the largest or indices l, r are out of bounds, no further heapification needed, break + // Swap two nodes if (ma == i) break; // Swap two nodes @@ -40,7 +40,7 @@ public class heap_sort { } // Extract the largest element from the heap and repeat for n-1 rounds for (int i = nums.length - 1; i > 0; i--) { - // Swap the root node with the rightmost leaf node (swap the first element with the last element) + // Delete node int tmp = nums[0]; nums[0] = nums[i]; nums[i] = tmp; @@ -52,6 +52,6 @@ public class heap_sort { public static void main(String[] args) { int[] nums = { 4, 1, 3, 1, 5, 2 }; heapSort(nums); - System.out.println("After heap sort, nums = " + Arrays.toString(nums)); + System.out.println("After heap sort completes, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_sorting/insertion_sort.java b/en/codes/java/chapter_sorting/insertion_sort.java index 137552d51..f66233bfc 100644 --- a/en/codes/java/chapter_sorting/insertion_sort.java +++ b/en/codes/java/chapter_sorting/insertion_sort.java @@ -11,10 +11,10 @@ import java.util.*; public class insertion_sort { /* Insertion sort */ static void insertionSort(int[] nums) { - // Outer loop: sorted range is [0, i-1] + // Outer loop: sorted interval is [0, i-1] for (int i = 1; i < nums.length; i++) { int base = nums[i], j = i - 1; - // Inner loop: insert base into the correct position within the sorted range [0, i-1] + // Inner loop: insert base into the correct position within the sorted interval [0, i-1] while (j >= 0 && nums[j] > base) { nums[j + 1] = nums[j]; // Move nums[j] to the right by one position j--; @@ -26,6 +26,6 @@ public class insertion_sort { public static void main(String[] args) { int[] nums = { 4, 1, 3, 1, 5, 2 }; insertionSort(nums); - System.out.println("After insertion sort, nums = " + Arrays.toString(nums)); + System.out.println("After insertion sort completes, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_sorting/merge_sort.java b/en/codes/java/chapter_sorting/merge_sort.java index 3c6f3f2bf..f96f54a89 100644 --- a/en/codes/java/chapter_sorting/merge_sort.java +++ b/en/codes/java/chapter_sorting/merge_sort.java @@ -41,7 +41,7 @@ public class merge_sort { // Termination condition if (left >= right) return; // Terminate recursion when subarray length is 1 - // Partition stage + // Divide and conquer stage int mid = left + (right - left) / 2; // Calculate midpoint mergeSort(nums, left, mid); // Recursively process the left subarray mergeSort(nums, mid + 1, right); // Recursively process the right subarray @@ -53,6 +53,6 @@ public class merge_sort { /* Merge sort */ int[] nums = { 7, 3, 2, 6, 0, 1, 5, 4 }; mergeSort(nums, 0, nums.length - 1); - System.out.println("After merge sort, nums = " + Arrays.toString(nums)); + System.out.println("After merge sort completes, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_sorting/quick_sort.java b/en/codes/java/chapter_sorting/quick_sort.java index aa037eeee..d23c06f1d 100644 --- a/en/codes/java/chapter_sorting/quick_sort.java +++ b/en/codes/java/chapter_sorting/quick_sort.java @@ -17,7 +17,7 @@ class QuickSort { nums[j] = tmp; } - /* Partition */ + /* Sentinel partition */ static int partition(int[] nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; @@ -37,7 +37,7 @@ class QuickSort { // Terminate recursion when subarray length is 1 if (left >= right) return; - // Partition + // Sentinel partition int pivot = partition(nums, left, right); // Recursively process the left subarray and right subarray quickSort(nums, left, pivot - 1); @@ -64,7 +64,7 @@ class QuickSortMedian { return right; } - /* Partition (median of three) */ + /* Sentinel partition (median of three) */ static int partition(int[] nums, int left, int right) { // Select the median of three candidate elements int med = medianThree(nums, left, (left + right) / 2, right); @@ -88,7 +88,7 @@ class QuickSortMedian { // Terminate recursion when subarray length is 1 if (left >= right) return; - // Partition + // Sentinel partition int pivot = partition(nums, left, right); // Recursively process the left subarray and right subarray quickSort(nums, left, pivot - 1); @@ -96,7 +96,7 @@ class QuickSortMedian { } } -/* Quick sort class (tail recursion optimization) */ +/* Quick sort class (recursion depth optimization) */ class QuickSortTailCall { /* Swap elements */ static void swap(int[] nums, int i, int j) { @@ -105,7 +105,7 @@ class QuickSortTailCall { nums[j] = tmp; } - /* Partition */ + /* Sentinel partition */ static int partition(int[] nums, int left, int right) { // Use nums[left] as the pivot int i = left, j = right; @@ -120,11 +120,11 @@ class QuickSortTailCall { return i; // Return the index of the pivot } - /* Quick sort (tail recursion optimization) */ + /* Quick sort (recursion depth optimization) */ public static void quickSort(int[] nums, int left, int right) { // Terminate when subarray length is 1 while (left < right) { - // Partition operation + // Sentinel partition operation int pivot = partition(nums, left, right); // Perform quick sort on the shorter of the two subarrays if (pivot - left < right - pivot) { @@ -143,16 +143,16 @@ public class quick_sort { /* Quick sort */ int[] nums = { 2, 4, 1, 0, 3, 5 }; QuickSort.quickSort(nums, 0, nums.length - 1); - System.out.println("After quick sort, nums = " + Arrays.toString(nums)); + System.out.println("After quick sort completes, nums = " + Arrays.toString(nums)); - /* Quick sort (median pivot optimization) */ + /* Quick sort (recursion depth optimization) */ int[] nums1 = { 2, 4, 1, 0, 3, 5 }; QuickSortMedian.quickSort(nums1, 0, nums1.length - 1); - System.out.println("After quick sort with median pivot optimization, nums1 = " + Arrays.toString(nums1)); + System.out.println("After quick sort (median pivot optimization) completes, nums1 = " + Arrays.toString(nums1)); - /* Quick sort (tail recursion optimization) */ + /* Quick sort (recursion depth optimization) */ int[] nums2 = { 2, 4, 1, 0, 3, 5 }; QuickSortTailCall.quickSort(nums2, 0, nums2.length - 1); - System.out.println("After quick sort with tail recursion optimization, nums2 = " + Arrays.toString(nums2)); + System.out.println("After quick sort (recursion depth optimization) completes, nums2 = " + Arrays.toString(nums2)); } } diff --git a/en/codes/java/chapter_sorting/radix_sort.java b/en/codes/java/chapter_sorting/radix_sort.java index 28c295f76..f146b54cd 100644 --- a/en/codes/java/chapter_sorting/radix_sort.java +++ b/en/codes/java/chapter_sorting/radix_sort.java @@ -64,6 +64,6 @@ public class radix_sort { int[] nums = { 10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337, 63832996 }; radixSort(nums); - System.out.println("After radix sort, nums = " + Arrays.toString(nums)); + System.out.println("After radix sort completes, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_sorting/selection_sort.java b/en/codes/java/chapter_sorting/selection_sort.java index 6389cd22c..d126463e7 100644 --- a/en/codes/java/chapter_sorting/selection_sort.java +++ b/en/codes/java/chapter_sorting/selection_sort.java @@ -12,15 +12,15 @@ public class selection_sort { /* Selection sort */ public static void selectionSort(int[] nums) { int n = nums.length; - // Outer loop: unsorted range is [i, n-1] + // Outer loop: unsorted interval is [i, n-1] for (int i = 0; i < n - 1; i++) { - // Inner loop: find the smallest element within the unsorted range + // Inner loop: find the smallest element within the unsorted interval int k = i; for (int j = i + 1; j < n; j++) { if (nums[j] < nums[k]) k = j; // Record the index of the smallest element } - // Swap the smallest element with the first element of the unsorted range + // Swap the smallest element with the first element of the unsorted interval int temp = nums[i]; nums[i] = nums[k]; nums[k] = temp; @@ -30,6 +30,6 @@ public class selection_sort { public static void main(String[] args) { int[] nums = { 4, 1, 3, 1, 5, 2 }; selectionSort(nums); - System.out.println("After selection sort, nums = " + Arrays.toString(nums)); + System.out.println("After selection sort completes, nums = " + Arrays.toString(nums)); } } diff --git a/en/codes/java/chapter_stack_and_queue/array_deque.java b/en/codes/java/chapter_stack_and_queue/array_deque.java index 13cf8ac85..02ae0cd29 100644 --- a/en/codes/java/chapter_stack_and_queue/array_deque.java +++ b/en/codes/java/chapter_stack_and_queue/array_deque.java @@ -8,11 +8,11 @@ package chapter_stack_and_queue; import java.util.*; -/* Double-ended queue class based on circular array */ +/* Double-ended queue based on circular array implementation */ class ArrayDeque { - private int[] nums; // Array used to store elements of the double-ended queue - private int front; // Front pointer, pointing to the front element - private int queSize; // Length of the double-ended queue + private int[] nums; // Array for storing double-ended queue elements + private int front; // Front pointer, points to the front of the queue element + private int queSize; // Double-ended queue length /* Constructor */ public ArrayDeque(int capacity) { @@ -30,81 +30,81 @@ class ArrayDeque { return queSize; } - /* Determine if the double-ended queue is empty */ + /* Check if the double-ended queue is empty */ public boolean isEmpty() { return queSize == 0; } /* Calculate circular array index */ private int index(int i) { - // Implement circular array by modulo operation - // When i exceeds the tail of the array, return to the head - // When i exceeds the head of the array, return to the tail + // Use modulo operation to wrap the array head and tail together + // When i passes the tail of the array, return to the head + // When i passes the head of the array, return to the tail return (i + capacity()) % capacity(); } - /* Front enqueue */ + /* Front of the queue enqueue */ public void pushFirst(int num) { if (queSize == capacity()) { System.out.println("Double-ended queue is full"); return; } - // Move the front pointer one position to the left - // Implement front crossing the head of the array to return to the tail by modulo operation + // Use modulo operation to wrap front around to the tail after passing the head of the array + // Add num to the front of the queue front = index(front - 1); - // Add num to the front + // Add num to front of queue nums[front] = num; queSize++; } - /* Rear enqueue */ + /* Rear of the queue enqueue */ public void pushLast(int num) { if (queSize == capacity()) { System.out.println("Double-ended queue is full"); return; } - // Calculate rear pointer, pointing to rear index + 1 + // Use modulo operation to wrap rear around to the head after passing the tail of the array int rear = index(front + queSize); - // Add num to the rear + // Front pointer moves one position backward nums[rear] = num; queSize++; } - /* Front dequeue */ + /* Rear of the queue dequeue */ public int popFirst() { int num = peekFirst(); - // Move front pointer one position backward + // Move front pointer backward by one position front = index(front + 1); queSize--; return num; } - /* Rear dequeue */ + /* Access rear of the queue element */ public int popLast() { int num = peekLast(); queSize--; return num; } - /* Access front element */ + /* Return list for printing */ public int peekFirst() { if (isEmpty()) throw new IndexOutOfBoundsException(); return nums[front]; } - /* Access rear element */ + /* Driver Code */ public int peekLast() { if (isEmpty()) throw new IndexOutOfBoundsException(); - // Calculate rear element index + // Initialize double-ended queue int last = index(front + queSize - 1); return nums[last]; } /* Return array for printing */ public int[] toArray() { - // Only convert elements within valid length range + // Elements enqueue int[] res = new int[queSize]; for (int i = 0, j = front; i < queSize; i++, j++) { res[i] = nums[index(j)]; @@ -115,37 +115,37 @@ class ArrayDeque { public class array_deque { public static void main(String[] args) { - /* Initialize double-ended queue */ + /* Get the length of the double-ended queue */ ArrayDeque deque = new ArrayDeque(10); deque.pushLast(3); deque.pushLast(2); deque.pushLast(5); System.out.println("Double-ended queue deque = " + Arrays.toString(deque.toArray())); - /* Access element */ + /* Update element */ int peekFirst = deque.peekFirst(); System.out.println("Front element peekFirst = " + peekFirst); int peekLast = deque.peekLast(); - System.out.println("Back element peekLast = " + peekLast); + System.out.println("Rear element peekLast = " + peekLast); - /* Element enqueue */ + /* Elements enqueue */ deque.pushLast(4); - System.out.println("Element 4 enqueued at the tail, deque = " + Arrays.toString(deque.toArray())); + System.out.println("After element 4 enqueues at rear, deque = " + Arrays.toString(deque.toArray())); deque.pushFirst(1); - System.out.println("Element 1 enqueued at the head, deque = " + Arrays.toString(deque.toArray())); + System.out.println("After element 1 enqueues at front, deque = " + Arrays.toString(deque.toArray())); /* Element dequeue */ int popLast = deque.popLast(); - System.out.println("Deque tail element = " + popLast + ", after dequeuing from the tail" + Arrays.toString(deque.toArray())); + System.out.println("Rear dequeue element = " + popLast + ", after rear dequeue, deque = " + Arrays.toString(deque.toArray())); int popFirst = deque.popFirst(); - System.out.println("Deque front element = " + popFirst + ", after dequeuing from the front" + Arrays.toString(deque.toArray())); + System.out.println("Front dequeue element = " + popFirst + ", after front dequeue, deque = " + Arrays.toString(deque.toArray())); /* Get the length of the double-ended queue */ int size = deque.size(); - System.out.println("Length of the double-ended queue size = " + size); + System.out.println("Double-ended queue length size = " + size); - /* Determine if the double-ended queue is empty */ + /* Check if the double-ended queue is empty */ boolean isEmpty = deque.isEmpty(); - System.out.println("Is the double-ended queue empty = " + isEmpty); + System.out.println("Double-ended queue is empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/array_queue.java b/en/codes/java/chapter_stack_and_queue/array_queue.java index aaf0ba370..71f9c3072 100644 --- a/en/codes/java/chapter_stack_and_queue/array_queue.java +++ b/en/codes/java/chapter_stack_and_queue/array_queue.java @@ -8,10 +8,10 @@ package chapter_stack_and_queue; import java.util.*; -/* Queue class based on circular array */ +/* Queue based on circular array implementation */ class ArrayQueue { private int[] nums; // Array for storing queue elements - private int front; // Front pointer, pointing to the front element + private int front; // Front pointer, points to the front of the queue element private int queSize; // Queue length public ArrayQueue(int capacity) { @@ -29,7 +29,7 @@ class ArrayQueue { return queSize; } - /* Determine if the queue is empty */ + /* Check if the queue is empty */ public boolean isEmpty() { return queSize == 0; } @@ -40,10 +40,10 @@ class ArrayQueue { System.out.println("Queue is full"); return; } - // Calculate rear pointer, pointing to rear index + 1 - // Use modulo operation to wrap the rear pointer from the end of the array back to the start + // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Add num to the rear of the queue int rear = (front + queSize) % capacity(); - // Add num to the rear + // Front pointer moves one position backward nums[rear] = num; queSize++; } @@ -51,13 +51,13 @@ class ArrayQueue { /* Dequeue */ public int pop() { int num = peek(); - // Move front pointer one position backward, returning to the head of the array if it exceeds the tail + // Move front pointer backward by one position, if it passes the tail, return to array head front = (front + 1) % capacity(); queSize--; return num; } - /* Access front element */ + /* Return list for printing */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); @@ -66,7 +66,7 @@ class ArrayQueue { /* Return array */ public int[] toArray() { - // Only convert elements within valid length range + // Elements enqueue int[] res = new int[queSize]; for (int i = 0, j = front; i < queSize; i++, j++) { res[i] = nums[j % capacity()]; @@ -77,11 +77,11 @@ class ArrayQueue { public class array_queue { public static void main(String[] args) { - /* Initialize queue */ + /* Access front of the queue element */ int capacity = 10; ArrayQueue queue = new ArrayQueue(capacity); - /* Element enqueue */ + /* Elements enqueue */ queue.push(1); queue.push(3); queue.push(2); @@ -89,27 +89,27 @@ public class array_queue { queue.push(4); System.out.println("Queue queue = " + Arrays.toString(queue.toArray())); - /* Access front element */ + /* Return list for printing */ int peek = queue.peek(); System.out.println("Front element peek = " + peek); /* Element dequeue */ int pop = queue.pop(); - System.out.println("Dequeued element = " + pop + ", after dequeuing" + Arrays.toString(queue.toArray())); + System.out.println("Dequeue element pop = " + pop + ", after dequeue, queue = " + Arrays.toString(queue.toArray())); /* Get the length of the queue */ int size = queue.size(); - System.out.println("Length of the queue size = " + size); + System.out.println("Queue length size = " + size); - /* Determine if the queue is empty */ + /* Check if the queue is empty */ boolean isEmpty = queue.isEmpty(); - System.out.println("Is the queue empty = " + isEmpty); + System.out.println("Queue is empty = " + isEmpty); /* Test circular array */ for (int i = 0; i < 10; i++) { queue.push(i); queue.pop(); - System.out.println("After the " + i + "th round of enqueueing + dequeuing, queue = " + Arrays.toString(queue.toArray())); + System.out.println("After round " + i + " enqueue + dequeue, queue = " + Arrays.toString(queue.toArray())); } } } diff --git a/en/codes/java/chapter_stack_and_queue/array_stack.java b/en/codes/java/chapter_stack_and_queue/array_stack.java index 757ebf3b2..895029525 100644 --- a/en/codes/java/chapter_stack_and_queue/array_stack.java +++ b/en/codes/java/chapter_stack_and_queue/array_stack.java @@ -8,12 +8,12 @@ package chapter_stack_and_queue; import java.util.*; -/* Stack class based on array */ +/* Stack based on array implementation */ class ArrayStack { private ArrayList stack; public ArrayStack() { - // Initialize the list (dynamic array) + // Initialize list (dynamic array) stack = new ArrayList<>(); } @@ -22,7 +22,7 @@ class ArrayStack { return stack.size(); } - /* Determine if the stack is empty */ + /* Check if the stack is empty */ public boolean isEmpty() { return size() == 0; } @@ -39,14 +39,14 @@ class ArrayStack { return stack.remove(size() - 1); } - /* Access stack top element */ + /* Return list for printing */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); return stack.get(size() - 1); } - /* Convert the List to Array and return */ + /* Convert List to Array and return */ public Object[] toArray() { return stack.toArray(); } @@ -54,10 +54,10 @@ class ArrayStack { public class array_stack { public static void main(String[] args) { - /* Initialize stack */ + /* Access top of the stack element */ ArrayStack stack = new ArrayStack(); - /* Element push */ + /* Elements push onto stack */ stack.push(1); stack.push(3); stack.push(2); @@ -65,20 +65,20 @@ public class array_stack { stack.push(4); System.out.println("Stack stack = " + Arrays.toString(stack.toArray())); - /* Access stack top element */ + /* Return list for printing */ int peek = stack.peek(); - System.out.println("Top element peek = " + peek); + System.out.println("Stack top element peek = " + peek); - /* Element pop */ + /* Element pop from stack */ int pop = stack.pop(); - System.out.println("Popped element = " + pop + ", after popping" + Arrays.toString(stack.toArray())); + System.out.println("Pop element pop = " + pop + ", after pop, stack = " + Arrays.toString(stack.toArray())); /* Get the length of the stack */ int size = stack.size(); - System.out.println("Length of the stack size = " + size); + System.out.println("Stack length size = " + size); - /* Determine if it's empty */ + /* Check if empty */ boolean isEmpty = stack.isEmpty(); - System.out.println("Is the stack empty = " + isEmpty); + System.out.println("Stack is empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/deque.java b/en/codes/java/chapter_stack_and_queue/deque.java index 838f7b3fd..bda81fa3e 100644 --- a/en/codes/java/chapter_stack_and_queue/deque.java +++ b/en/codes/java/chapter_stack_and_queue/deque.java @@ -10,37 +10,37 @@ import java.util.*; public class deque { public static void main(String[] args) { - /* Initialize double-ended queue */ + /* Get the length of the double-ended queue */ Deque deque = new LinkedList<>(); deque.offerLast(3); deque.offerLast(2); deque.offerLast(5); System.out.println("Double-ended queue deque = " + deque); - /* Access element */ + /* Update element */ int peekFirst = deque.peekFirst(); System.out.println("Front element peekFirst = " + peekFirst); int peekLast = deque.peekLast(); - System.out.println("Back element peekLast = " + peekLast); + System.out.println("Rear element peekLast = " + peekLast); - /* Element enqueue */ + /* Elements enqueue */ deque.offerLast(4); - System.out.println("Element 4 enqueued at the tail, deque = " + deque); + System.out.println("After element 4 enqueues at rear, deque = " + deque); deque.offerFirst(1); - System.out.println("Element 1 enqueued at the head, deque = " + deque); + System.out.println("After element 1 enqueues at front, deque = " + deque); /* Element dequeue */ int popLast = deque.pollLast(); - System.out.println("Deque tail element = " + popLast + ", after dequeuing from the tail" + deque); + System.out.println("Rear dequeue element = " + popLast + ", after rear dequeue, deque = " + deque); int popFirst = deque.pollFirst(); - System.out.println("Deque front element = " + popFirst + ", after dequeuing from the front" + deque); + System.out.println("Front dequeue element = " + popFirst + ", after front dequeue, deque = " + deque); /* Get the length of the double-ended queue */ int size = deque.size(); - System.out.println("Length of the double-ended queue size = " + size); + System.out.println("Double-ended queue length size = " + size); - /* Determine if the double-ended queue is empty */ + /* Check if the double-ended queue is empty */ boolean isEmpty = deque.isEmpty(); - System.out.println("Is the double-ended queue empty = " + isEmpty); + System.out.println("Double-ended queue is empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java b/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java index 6340c9009..b1bfe074a 100644 --- a/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java +++ b/en/codes/java/chapter_stack_and_queue/linkedlist_deque.java @@ -8,11 +8,11 @@ package chapter_stack_and_queue; import java.util.*; -/* Double-linked list node */ +/* Doubly linked list node */ class ListNode { int val; // Node value - ListNode next; // Reference to successor node - ListNode prev; // Reference to predecessor node + ListNode next; // Successor node reference + ListNode prev; // Predecessor node reference ListNode(int val) { this.val = val; @@ -20,9 +20,9 @@ class ListNode { } } -/* Double-ended queue class based on double-linked list */ +/* Double-ended queue based on doubly linked list implementation */ class LinkedListDeque { - private ListNode front, rear; // Front node front, back node rear + private ListNode front, rear; // Head node front, tail node rear private int queSize = 0; // Length of the double-ended queue public LinkedListDeque() { @@ -34,7 +34,7 @@ class LinkedListDeque { return queSize; } - /* Determine if the double-ended queue is empty */ + /* Check if the double-ended queue is empty */ public boolean isEmpty() { return size() == 0; } @@ -42,18 +42,18 @@ class LinkedListDeque { /* Enqueue operation */ private void push(int num, boolean isFront) { ListNode node = new ListNode(num); - // If the list is empty, make front and rear both point to node + // If the linked list is empty, make both front and rear point to node if (isEmpty()) front = rear = node; - // Front enqueue operation + // Front of the queue enqueue operation else if (isFront) { - // Add node to the head of the list + // Add node to the head of the linked list front.prev = node; node.next = front; front = node; // Update head node - // Rear enqueue operation + // Rear of the queue enqueue operation } else { - // Add node to the tail of the list + // Add node to the tail of the linked list rear.next = node; node.prev = rear; rear = node; // Update tail node @@ -61,12 +61,12 @@ class LinkedListDeque { queSize++; // Update queue length } - /* Front enqueue */ + /* Front of the queue enqueue */ public void pushFirst(int num) { push(num, true); } - /* Rear enqueue */ + /* Rear of the queue enqueue */ public void pushLast(int num) { push(num, false); } @@ -76,20 +76,20 @@ class LinkedListDeque { if (isEmpty()) throw new IndexOutOfBoundsException(); int val; - // Front dequeue operation + // Temporarily store head node value if (isFront) { - val = front.val; // Temporarily store the head node value - // Remove head node + val = front.val; // Delete head node + // Delete head node ListNode fNext = front.next; if (fNext != null) { fNext.prev = null; front.next = null; } front = fNext; // Update head node - // Rear dequeue operation + // Temporarily store tail node value } else { - val = rear.val; // Temporarily store the tail node value - // Remove tail node + val = rear.val; // Delete tail node + // Update tail node ListNode rPrev = rear.prev; if (rPrev != null) { rPrev.next = null; @@ -101,24 +101,24 @@ class LinkedListDeque { return val; } - /* Front dequeue */ + /* Rear of the queue dequeue */ public int popFirst() { return pop(true); } - /* Rear dequeue */ + /* Access rear of the queue element */ public int popLast() { return pop(false); } - /* Access front element */ + /* Return list for printing */ public int peekFirst() { if (isEmpty()) throw new IndexOutOfBoundsException(); return front.val; } - /* Access rear element */ + /* Driver Code */ public int peekLast() { if (isEmpty()) throw new IndexOutOfBoundsException(); @@ -139,37 +139,37 @@ class LinkedListDeque { public class linkedlist_deque { public static void main(String[] args) { - /* Initialize double-ended queue */ + /* Get the length of the double-ended queue */ LinkedListDeque deque = new LinkedListDeque(); deque.pushLast(3); deque.pushLast(2); deque.pushLast(5); System.out.println("Double-ended queue deque = " + Arrays.toString(deque.toArray())); - /* Access element */ + /* Update element */ int peekFirst = deque.peekFirst(); System.out.println("Front element peekFirst = " + peekFirst); int peekLast = deque.peekLast(); - System.out.println("Back element peekLast = " + peekLast); + System.out.println("Rear element peekLast = " + peekLast); - /* Element enqueue */ + /* Elements enqueue */ deque.pushLast(4); - System.out.println("Element 4 enqueued at the tail, deque = " + Arrays.toString(deque.toArray())); + System.out.println("After element 4 enqueues at rear, deque = " + Arrays.toString(deque.toArray())); deque.pushFirst(1); - System.out.println("Element 1 enqueued at the head, deque = " + Arrays.toString(deque.toArray())); + System.out.println("After element 1 enqueues at front, deque = " + Arrays.toString(deque.toArray())); /* Element dequeue */ int popLast = deque.popLast(); - System.out.println("Deque tail element = " + popLast + ", after dequeuing from the tail" + Arrays.toString(deque.toArray())); + System.out.println("Rear dequeue element = " + popLast + ", after rear dequeue, deque = " + Arrays.toString(deque.toArray())); int popFirst = deque.popFirst(); - System.out.println("Deque front element = " + popFirst + ", after dequeuing from the front" + Arrays.toString(deque.toArray())); + System.out.println("Front dequeue element = " + popFirst + ", after front dequeue, deque = " + Arrays.toString(deque.toArray())); /* Get the length of the double-ended queue */ int size = deque.size(); - System.out.println("Length of the double-ended queue size = " + size); + System.out.println("Double-ended queue length size = " + size); - /* Determine if the double-ended queue is empty */ + /* Check if the double-ended queue is empty */ boolean isEmpty = deque.isEmpty(); - System.out.println("Is the double-ended queue empty = " + isEmpty); + System.out.println("Double-ended queue is empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/linkedlist_queue.java b/en/codes/java/chapter_stack_and_queue/linkedlist_queue.java index d60b27f4d..e19c1ffc9 100644 --- a/en/codes/java/chapter_stack_and_queue/linkedlist_queue.java +++ b/en/codes/java/chapter_stack_and_queue/linkedlist_queue.java @@ -8,9 +8,9 @@ package chapter_stack_and_queue; import java.util.*; -/* Queue class based on linked list */ +/* Queue based on linked list implementation */ class LinkedListQueue { - private ListNode front, rear; // Front node front, back node rear + private ListNode front, rear; // Head node front, tail node rear private int queSize = 0; public LinkedListQueue() { @@ -23,20 +23,20 @@ class LinkedListQueue { return queSize; } - /* Determine if the queue is empty */ + /* Check if the queue is empty */ public boolean isEmpty() { return size() == 0; } /* Enqueue */ public void push(int num) { - // Add num behind the tail node + // Add num after the tail node ListNode node = new ListNode(num); - // If the queue is empty, make the head and tail nodes both point to that node + // If the queue is empty, make both front and rear point to the node if (front == null) { front = node; rear = node; - // If the queue is not empty, add that node behind the tail node + // If the queue is not empty, add the node after the tail node } else { rear.next = node; rear = node; @@ -47,20 +47,20 @@ class LinkedListQueue { /* Dequeue */ public int pop() { int num = peek(); - // Remove head node + // Delete head node front = front.next; queSize--; return num; } - /* Access front element */ + /* Return list for printing */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); return front.val; } - /* Convert the linked list to Array and return */ + /* Convert linked list to Array and return */ public int[] toArray() { ListNode node = front; int[] res = new int[size()]; @@ -74,10 +74,10 @@ class LinkedListQueue { public class linkedlist_queue { public static void main(String[] args) { - /* Initialize queue */ + /* Access front of the queue element */ LinkedListQueue queue = new LinkedListQueue(); - /* Element enqueue */ + /* Elements enqueue */ queue.push(1); queue.push(3); queue.push(2); @@ -85,20 +85,20 @@ public class linkedlist_queue { queue.push(4); System.out.println("Queue queue = " + Arrays.toString(queue.toArray())); - /* Access front element */ + /* Return list for printing */ int peek = queue.peek(); System.out.println("Front element peek = " + peek); /* Element dequeue */ int pop = queue.pop(); - System.out.println("Dequeued element = " + pop + ", after dequeuing" + Arrays.toString(queue.toArray())); + System.out.println("Dequeue element pop = " + pop + ", after dequeue, queue = " + Arrays.toString(queue.toArray())); /* Get the length of the queue */ int size = queue.size(); - System.out.println("Length of the queue size = " + size); + System.out.println("Queue length size = " + size); - /* Determine if the queue is empty */ + /* Check if the queue is empty */ boolean isEmpty = queue.isEmpty(); - System.out.println("Is the queue empty = " + isEmpty); + System.out.println("Queue is empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/linkedlist_stack.java b/en/codes/java/chapter_stack_and_queue/linkedlist_stack.java index c4438def8..873a52692 100644 --- a/en/codes/java/chapter_stack_and_queue/linkedlist_stack.java +++ b/en/codes/java/chapter_stack_and_queue/linkedlist_stack.java @@ -9,10 +9,10 @@ package chapter_stack_and_queue; import java.util.*; import utils.*; -/* Stack class based on linked list */ +/* Stack based on linked list implementation */ class LinkedListStack { - private ListNode stackPeek; // Use the head node as the top of the stack - private int stkSize = 0; // Length of the stack + private ListNode stackPeek; // Use head node as stack top + private int stkSize = 0; // Stack length public LinkedListStack() { stackPeek = null; @@ -23,7 +23,7 @@ class LinkedListStack { return stkSize; } - /* Determine if the stack is empty */ + /* Check if the stack is empty */ public boolean isEmpty() { return size() == 0; } @@ -44,14 +44,14 @@ class LinkedListStack { return num; } - /* Access stack top element */ + /* Return list for printing */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); return stackPeek.val; } - /* Convert the List to Array and return */ + /* Convert List to Array and return */ public int[] toArray() { ListNode node = stackPeek; int[] res = new int[size()]; @@ -65,10 +65,10 @@ class LinkedListStack { public class linkedlist_stack { public static void main(String[] args) { - /* Initialize stack */ + /* Access top of the stack element */ LinkedListStack stack = new LinkedListStack(); - /* Element push */ + /* Elements push onto stack */ stack.push(1); stack.push(3); stack.push(2); @@ -76,20 +76,20 @@ public class linkedlist_stack { stack.push(4); System.out.println("Stack stack = " + Arrays.toString(stack.toArray())); - /* Access stack top element */ + /* Return list for printing */ int peek = stack.peek(); - System.out.println("Top element peek = " + peek); + System.out.println("Stack top element peek = " + peek); - /* Element pop */ + /* Element pop from stack */ int pop = stack.pop(); - System.out.println("Popped element = " + pop + ", after popping" + Arrays.toString(stack.toArray())); + System.out.println("Pop element pop = " + pop + ", after pop, stack = " + Arrays.toString(stack.toArray())); /* Get the length of the stack */ int size = stack.size(); - System.out.println("Length of the stack size = " + size); + System.out.println("Stack length size = " + size); - /* Determine if it's empty */ + /* Check if empty */ boolean isEmpty = stack.isEmpty(); - System.out.println("Is the stack empty = " + isEmpty); + System.out.println("Stack is empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/queue.java b/en/codes/java/chapter_stack_and_queue/queue.java index 7b37e41c3..0db5d68db 100644 --- a/en/codes/java/chapter_stack_and_queue/queue.java +++ b/en/codes/java/chapter_stack_and_queue/queue.java @@ -10,10 +10,10 @@ import java.util.*; public class queue { public static void main(String[] args) { - /* Initialize queue */ + /* Access front of the queue element */ Queue queue = new LinkedList<>(); - /* Element enqueue */ + /* Elements enqueue */ queue.offer(1); queue.offer(3); queue.offer(2); @@ -21,20 +21,20 @@ public class queue { queue.offer(4); System.out.println("Queue queue = " + queue); - /* Access front element */ + /* Return list for printing */ int peek = queue.peek(); System.out.println("Front element peek = " + peek); /* Element dequeue */ int pop = queue.poll(); - System.out.println("Dequeued element = " + pop + ", after dequeuing" + queue); + System.out.println("Dequeue element pop = " + pop + ", after dequeue, queue = " + queue); /* Get the length of the queue */ int size = queue.size(); - System.out.println("Length of the queue size = " + size); + System.out.println("Queue length size = " + size); - /* Determine if the queue is empty */ + /* Check if the queue is empty */ boolean isEmpty = queue.isEmpty(); - System.out.println("Is the queue empty = " + isEmpty); + System.out.println("Queue is empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_stack_and_queue/stack.java b/en/codes/java/chapter_stack_and_queue/stack.java index 5270c018f..4d7e952bb 100644 --- a/en/codes/java/chapter_stack_and_queue/stack.java +++ b/en/codes/java/chapter_stack_and_queue/stack.java @@ -10,10 +10,10 @@ import java.util.*; public class stack { public static void main(String[] args) { - /* Initialize stack */ + /* Access top of the stack element */ Stack stack = new Stack<>(); - /* Element push */ + /* Elements push onto stack */ stack.push(1); stack.push(3); stack.push(2); @@ -21,20 +21,20 @@ public class stack { stack.push(4); System.out.println("Stack stack = " + stack); - /* Access stack top element */ + /* Return list for printing */ int peek = stack.peek(); - System.out.println("Top element peek = " + peek); + System.out.println("Stack top element peek = " + peek); - /* Element pop */ + /* Element pop from stack */ int pop = stack.pop(); - System.out.println("Popped element = " + pop + ", after popping" + stack); + System.out.println("Pop element pop = " + pop + ", after pop, stack = " + stack); /* Get the length of the stack */ int size = stack.size(); - System.out.println("Length of the stack size = " + size); + System.out.println("Stack length size = " + size); - /* Determine if it's empty */ + /* Check if empty */ boolean isEmpty = stack.isEmpty(); - System.out.println("Is the stack empty = " + isEmpty); + System.out.println("Stack is empty = " + isEmpty); } } diff --git a/en/codes/java/chapter_tree/array_binary_tree.java b/en/codes/java/chapter_tree/array_binary_tree.java index 9adb13597..4d3dc773c 100644 --- a/en/codes/java/chapter_tree/array_binary_tree.java +++ b/en/codes/java/chapter_tree/array_binary_tree.java @@ -9,7 +9,7 @@ package chapter_tree; import utils.*; import java.util.*; -/* Array-based binary tree class */ +/* Binary tree class represented by array */ class ArrayBinaryTree { private List tree; @@ -23,25 +23,25 @@ class ArrayBinaryTree { return tree.size(); } - /* Get the value of the node at index i */ + /* Get value of node at index i */ public Integer val(int i) { - // If the index is out of bounds, return null, representing an empty spot + // If index out of bounds, return null to represent empty position if (i < 0 || i >= size()) return null; return tree.get(i); } - /* Get the index of the left child of the node at index i */ + /* Get index of left child node of node at index i */ public Integer left(int i) { return 2 * i + 1; } - /* Get the index of the right child of the node at index i */ + /* Get index of right child node of node at index i */ public Integer right(int i) { return 2 * i + 2; } - /* Get the index of the parent of the node at index i */ + /* Get index of parent node of node at index i */ public Integer parent(int i) { return (i - 1) / 2; } @@ -49,7 +49,7 @@ class ArrayBinaryTree { /* Level-order traversal */ public List levelOrder() { List res = new ArrayList<>(); - // Traverse array + // Traverse array directly for (int i = 0; i < size(); i++) { if (val(i) != null) res.add(val(i)); @@ -59,37 +59,37 @@ class ArrayBinaryTree { /* Depth-first traversal */ private void dfs(Integer i, String order, List res) { - // If it is an empty spot, return + // If empty position, return if (val(i) == null) return; - // Pre-order traversal + // Preorder traversal if ("pre".equals(order)) res.add(val(i)); dfs(left(i), order, res); - // In-order traversal + // Inorder traversal if ("in".equals(order)) res.add(val(i)); dfs(right(i), order, res); - // Post-order traversal + // Postorder traversal if ("post".equals(order)) res.add(val(i)); } - /* Pre-order traversal */ + /* Preorder traversal */ public List preOrder() { List res = new ArrayList<>(); dfs(0, "pre", res); return res; } - /* In-order traversal */ + /* Inorder traversal */ public List inOrder() { List res = new ArrayList<>(); dfs(0, "in", res); return res; } - /* Post-order traversal */ + /* Postorder traversal */ public List postOrder() { List res = new ArrayList<>(); dfs(0, "post", res); @@ -100,17 +100,17 @@ class ArrayBinaryTree { public class array_binary_tree { public static void main(String[] args) { // Initialize binary tree - // Use a specific function to convert an array into a binary tree + // Here we use a function to generate a binary tree directly from an array List arr = Arrays.asList(1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15); TreeNode root = TreeNode.listToTree(arr); System.out.println("\nInitialize binary tree\n"); - System.out.println("Array representation of the binary tree:"); + System.out.println("Array representation of binary tree:"); System.out.println(arr); - System.out.println("Linked list representation of the binary tree:"); + System.out.println("Linked list representation of binary tree:"); PrintUtil.printTree(root); - // Array-based binary tree class + // Binary tree class represented by array ArrayBinaryTree abt = new ArrayBinaryTree(arr); // Access node @@ -118,19 +118,19 @@ public class array_binary_tree { Integer l = abt.left(i); Integer r = abt.right(i); Integer p = abt.parent(i); - System.out.println("\nThe current node's index is " + i + ", value = " + abt.val(i)); - System.out.println("Its left child's index is " + l + ", value = " + (l == null ? "null" : abt.val(l))); - System.out.println("Its right child's index is " + r + ", value = " + (r == null ? "null" : abt.val(r))); - System.out.println("Its parent's index is " + p + ", value = " + (p == null ? "null" : abt.val(p))); + System.out.println("\nCurrent node index is " + i + ", value is " + abt.val(i)); + System.out.println("Its left child node index is " + l + ", value is " + (l == null ? "null" : abt.val(l))); + System.out.println("Its right child node index is " + r + ", value is " + (r == null ? "null" : abt.val(r))); + System.out.println("Its parent node index is " + p + ", value is " + (p == null ? "null" : abt.val(p))); // Traverse tree List res = abt.levelOrder(); System.out.println("\nLevel-order traversal is:" + res); res = abt.preOrder(); - System.out.println("Pre-order traversal is:" + res); + System.out.println("Preorder traversal is:" + res); res = abt.inOrder(); - System.out.println("In-order traversal is:" + res); + System.out.println("Inorder traversal is:" + res); res = abt.postOrder(); - System.out.println("Post-order traversal is:" + res); + System.out.println("Postorder traversal is:" + res); } } diff --git a/en/codes/java/chapter_tree/avl_tree.java b/en/codes/java/chapter_tree/avl_tree.java index 6f3451047..2740a518b 100644 --- a/en/codes/java/chapter_tree/avl_tree.java +++ b/en/codes/java/chapter_tree/avl_tree.java @@ -37,13 +37,13 @@ class AVLTree { private TreeNode rightRotate(TreeNode node) { TreeNode child = node.left; TreeNode grandChild = child.right; - // Rotate node to the right around child + // Using child as pivot, rotate node to the right child.right = node; node.left = grandChild; // Update node height updateHeight(node); updateHeight(child); - // Return the root of the subtree after rotation + // Return root node of subtree after rotation return child; } @@ -51,19 +51,19 @@ class AVLTree { private TreeNode leftRotate(TreeNode node) { TreeNode child = node.right; TreeNode grandChild = child.left; - // Rotate node to the left around child + // Using child as pivot, rotate node to the left child.left = node; node.right = grandChild; // Update node height updateHeight(node); updateHeight(child); - // Return the root of the subtree after rotation + // Return root node of subtree after rotation return child; } - /* Perform rotation operation to restore balance to the subtree */ + /* Perform rotation operation to restore balance to this subtree */ private TreeNode rotate(TreeNode node) { - // Get the balance factor of node + // Get balance factor of node int balanceFactor = balanceFactor(node); // Left-leaning tree if (balanceFactor > 1) { @@ -87,7 +87,7 @@ class AVLTree { return leftRotate(node); } } - // Balanced tree, no rotation needed, return + // Balanced tree, no rotation needed, return directly return node; } @@ -106,11 +106,11 @@ class AVLTree { else if (val > node.val) node.right = insertHelper(node.right, val); else - return node; // Do not insert duplicate nodes, return + return node; // Duplicate node not inserted, return directly updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to the subtree */ + /* 2. Perform rotation operation to restore balance to this subtree */ node = rotate(node); - // Return the root node of the subtree + // Return root node of subtree return node; } @@ -119,11 +119,11 @@ class AVLTree { root = removeHelper(root, val); } - /* Recursively remove node (helper method) */ + /* Recursively delete node (helper method) */ private TreeNode removeHelper(TreeNode node, int val) { if (node == null) return null; - /* 1. Find and remove the node */ + /* 1. Find node and delete */ if (val < node.val) node.left = removeHelper(node.left, val); else if (val > node.val) @@ -131,14 +131,14 @@ class AVLTree { else { if (node.left == null || node.right == null) { TreeNode child = node.left != null ? node.left : node.right; - // Number of child nodes = 0, remove node and return + // Number of child nodes = 0, delete node directly and return if (child == null) return null; - // Number of child nodes = 1, remove node + // Number of child nodes = 1, delete node directly else node = child; } else { - // Number of child nodes = 2, remove the next node in in-order traversal and replace the current node with it + // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it TreeNode temp = node.right; while (temp.left != null) { temp = temp.left; @@ -148,16 +148,16 @@ class AVLTree { } } updateHeight(node); // Update node height - /* 2. Perform rotation operation to restore balance to the subtree */ + /* 2. Perform rotation operation to restore balance to this subtree */ node = rotate(node); - // Return the root node of the subtree + // Return root node of subtree return node; } /* Search node */ public TreeNode search(int val) { TreeNode cur = root; - // Loop find, break after passing leaf nodes + // Loop search, exit after passing leaf node while (cur != null) { // Target node is in cur's right subtree if (cur.val < val) @@ -165,7 +165,7 @@ class AVLTree { // Target node is in cur's left subtree else if (cur.val > val) cur = cur.left; - // Found target node, break loop + // Found target node, exit loop else break; } @@ -177,22 +177,22 @@ class AVLTree { public class avl_tree { static void testInsert(AVLTree tree, int val) { tree.insert(val); - System.out.println("\nAfter inserting node " + val + ", the AVL tree is "); + System.out.println("\nAfter inserting node " + val + ", AVL tree is"); PrintUtil.printTree(tree.root); } static void testRemove(AVLTree tree, int val) { tree.remove(val); - System.out.println("\nAfter removing node " + val + ", the AVL tree is "); + System.out.println("\nAfter removing node " + val + ", AVL tree is"); PrintUtil.printTree(tree.root); } public static void main(String[] args) { - /* Initialize empty AVL tree */ + /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ AVLTree avlTree = new AVLTree(); /* Insert node */ - // Notice how the AVL tree maintains balance after inserting nodes + // Delete nodes testInsert(avlTree, 1); testInsert(avlTree, 2); testInsert(avlTree, 3); @@ -204,17 +204,17 @@ public class avl_tree { testInsert(avlTree, 10); testInsert(avlTree, 6); - /* Insert duplicate node */ + /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ testInsert(avlTree, 7); /* Remove node */ - // Notice how the AVL tree maintains balance after removing nodes - testRemove(avlTree, 8); // Remove node with degree 0 + // Delete node with degree 1 + testRemove(avlTree, 8); // Delete node with degree 2 testRemove(avlTree, 5); // Remove node with degree 1 testRemove(avlTree, 4); // Remove node with degree 2 /* Search node */ TreeNode node = avlTree.search(7); - System.out.println("\nThe found node object is " + node + ", node value = " + node.val); + System.out.println("\nFound node object is " + node + ", node value = " + node.val); } } diff --git a/en/codes/java/chapter_tree/binary_search_tree.java b/en/codes/java/chapter_tree/binary_search_tree.java index 365290ac8..563e3d483 100644 --- a/en/codes/java/chapter_tree/binary_search_tree.java +++ b/en/codes/java/chapter_tree/binary_search_tree.java @@ -26,7 +26,7 @@ class BinarySearchTree { /* Search node */ public TreeNode search(int num) { TreeNode cur = root; - // Loop find, break after passing leaf nodes + // Loop search, exit after passing leaf node while (cur != null) { // Target node is in cur's right subtree if (cur.val < num) @@ -34,7 +34,7 @@ class BinarySearchTree { // Target node is in cur's left subtree else if (cur.val > num) cur = cur.left; - // Found target node, break loop + // Found target node, exit loop else break; } @@ -50,9 +50,9 @@ class BinarySearchTree { return; } TreeNode cur = root, pre = null; - // Loop find, break after passing leaf nodes + // Loop search, exit after passing leaf node while (cur != null) { - // Found duplicate node, thus return + // Found duplicate node, return directly if (cur.val == num) return; pre = cur; @@ -73,49 +73,49 @@ class BinarySearchTree { /* Remove node */ public void remove(int num) { - // If tree is empty, return + // If tree is empty, return directly if (root == null) return; TreeNode cur = root, pre = null; - // Loop find, break after passing leaf nodes + // Loop search, exit after passing leaf node while (cur != null) { - // Found node to be removed, break loop + // Found node to delete, exit loop if (cur.val == num) break; pre = cur; - // Node to be removed is in cur's right subtree + // Node to delete is in cur's right subtree if (cur.val < num) cur = cur.right; - // Node to be removed is in cur's left subtree + // Node to delete is in cur's left subtree else cur = cur.left; } - // If no node to be removed, return + // If no node to delete, return directly if (cur == null) return; // Number of child nodes = 0 or 1 if (cur.left == null || cur.right == null) { - // When the number of child nodes = 0/1, child = null/that child node + // When number of child nodes = 0 / 1, child = null / that child node TreeNode child = cur.left != null ? cur.left : cur.right; - // Remove node cur + // Delete node cur if (cur != root) { if (pre.left == cur) pre.left = child; else pre.right = child; } else { - // If the removed node is the root, reassign the root + // If deleted node is root node, reassign root node root = child; } } // Number of child nodes = 2 else { - // Get the next node in in-order traversal of cur + // Get next node of cur in inorder traversal TreeNode tmp = cur.right; while (tmp.left != null) { tmp = tmp.left; } - // Recursively remove node tmp + // Recursively delete node tmp remove(tmp.val); // Replace cur with tmp cur.val = tmp.val; @@ -127,7 +127,7 @@ public class binary_search_tree { public static void main(String[] args) { /* Initialize binary search tree */ BinarySearchTree bst = new BinarySearchTree(); - // Note that different insertion orders can result in various tree structures. This particular sequence creates a perfect binary tree + // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree int[] nums = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }; for (int num : nums) { bst.insert(num); @@ -137,22 +137,22 @@ public class binary_search_tree { /* Search node */ TreeNode node = bst.search(7); - System.out.println("\nThe found node object is " + node + ", node value = " + node.val); + System.out.println("\nFound node object is " + node + ", node value = " + node.val); /* Insert node */ bst.insert(16); - System.out.println("\nAfter inserting node 16, the binary tree is\n"); + System.out.println("\nAfter inserting node 16, binary tree is\n"); PrintUtil.printTree(bst.getRoot()); /* Remove node */ bst.remove(1); - System.out.println("\nAfter removing node 1, the binary tree is\n"); + System.out.println("\nAfter removing node 1, binary tree is\n"); PrintUtil.printTree(bst.getRoot()); bst.remove(2); - System.out.println("\nAfter removing node 2, the binary tree is\n"); + System.out.println("\nAfter removing node 2, binary tree is\n"); PrintUtil.printTree(bst.getRoot()); bst.remove(4); - System.out.println("\nAfter removing node 4, the binary tree is\n"); + System.out.println("\nAfter removing node 4, binary tree is\n"); PrintUtil.printTree(bst.getRoot()); } } diff --git a/en/codes/java/chapter_tree/binary_tree.java b/en/codes/java/chapter_tree/binary_tree.java index df209d2e4..d2edff09a 100644 --- a/en/codes/java/chapter_tree/binary_tree.java +++ b/en/codes/java/chapter_tree/binary_tree.java @@ -11,13 +11,13 @@ import utils.*; public class binary_tree { public static void main(String[] args) { /* Initialize binary tree */ - // Initialize node + // Initialize nodes TreeNode n1 = new TreeNode(1); TreeNode n2 = new TreeNode(2); TreeNode n3 = new TreeNode(3); TreeNode n4 = new TreeNode(4); TreeNode n5 = new TreeNode(5); - // Construct node references (pointers) + // Build references (pointers) between nodes n1.left = n2; n1.right = n3; n2.left = n4; @@ -25,9 +25,9 @@ public class binary_tree { System.out.println("\nInitialize binary tree\n"); PrintUtil.printTree(n1); - /* Insert and remove nodes */ + /* Insert node P between n1 -> n2 */ TreeNode P = new TreeNode(0); - // Insert node P between n1 -> n2 + // Delete node n1.left = P; P.left = n2; System.out.println("\nAfter inserting node P\n"); diff --git a/en/codes/java/chapter_tree/binary_tree_bfs.java b/en/codes/java/chapter_tree/binary_tree_bfs.java index 3a398500f..c1a2f575d 100644 --- a/en/codes/java/chapter_tree/binary_tree_bfs.java +++ b/en/codes/java/chapter_tree/binary_tree_bfs.java @@ -15,28 +15,28 @@ public class binary_tree_bfs { // Initialize queue, add root node Queue queue = new LinkedList<>(); queue.add(root); - // Initialize a list to store the traversal sequence + // Initialize a list to save the traversal sequence List list = new ArrayList<>(); while (!queue.isEmpty()) { - TreeNode node = queue.poll(); // Queue dequeues + TreeNode node = queue.poll(); // Dequeue list.add(node.val); // Save node value if (node.left != null) - queue.offer(node.left); // Left child node enqueues + queue.offer(node.left); // Left child node enqueue if (node.right != null) - queue.offer(node.right); // Right child node enqueues + queue.offer(node.right); // Right child node enqueue } return list; } public static void main(String[] args) { /* Initialize binary tree */ - // Use a specific function to convert an array into a binary tree + // Here we use a function to generate a binary tree directly from an array TreeNode root = TreeNode.listToTree(Arrays.asList(1, 2, 3, 4, 5, 6, 7)); System.out.println("\nInitialize binary tree\n"); PrintUtil.printTree(root); /* Level-order traversal */ List list = levelOrder(root); - System.out.println("\nPrint sequence of nodes from level-order traversal = " + list); + System.out.println("\nLevel-order traversal node print sequence = " + list); } } diff --git a/en/codes/java/chapter_tree/binary_tree_dfs.java b/en/codes/java/chapter_tree/binary_tree_dfs.java index 2ca14ccaa..485550f2f 100644 --- a/en/codes/java/chapter_tree/binary_tree_dfs.java +++ b/en/codes/java/chapter_tree/binary_tree_dfs.java @@ -10,10 +10,10 @@ import utils.*; import java.util.*; public class binary_tree_dfs { - // Initialize the list for storing traversal sequences + // Initialize list for storing traversal sequence static ArrayList list = new ArrayList<>(); - /* Pre-order traversal */ + /* Preorder traversal */ static void preOrder(TreeNode root) { if (root == null) return; @@ -23,7 +23,7 @@ public class binary_tree_dfs { preOrder(root.right); } - /* In-order traversal */ + /* Inorder traversal */ static void inOrder(TreeNode root) { if (root == null) return; @@ -33,7 +33,7 @@ public class binary_tree_dfs { inOrder(root.right); } - /* Post-order traversal */ + /* Postorder traversal */ static void postOrder(TreeNode root) { if (root == null) return; @@ -45,24 +45,24 @@ public class binary_tree_dfs { public static void main(String[] args) { /* Initialize binary tree */ - // Use a specific function to convert an array into a binary tree + // Here we use a function to generate a binary tree directly from an array TreeNode root = TreeNode.listToTree(Arrays.asList(1, 2, 3, 4, 5, 6, 7)); System.out.println("\nInitialize binary tree\n"); PrintUtil.printTree(root); - /* Pre-order traversal */ + /* Preorder traversal */ list.clear(); preOrder(root); - System.out.println("\nPrint sequence of nodes from pre-order traversal = " + list); + System.out.println("\nPreorder traversal node print sequence = " + list); - /* In-order traversal */ + /* Inorder traversal */ list.clear(); inOrder(root); - System.out.println("\nPrint sequence of nodes from in-order traversal = " + list); + System.out.println("\nInorder traversal node print sequence = " + list); - /* Post-order traversal */ + /* Postorder traversal */ list.clear(); postOrder(root); - System.out.println("\nPrint sequence of nodes from post-order traversal = " + list); + System.out.println("\nPostorder traversal node print sequence = " + list); } } diff --git a/en/codes/java/utils/PrintUtil.java b/en/codes/java/utils/PrintUtil.java index 2ce4044f8..9f6a8208d 100644 --- a/en/codes/java/utils/PrintUtil.java +++ b/en/codes/java/utils/PrintUtil.java @@ -53,7 +53,7 @@ public class PrintUtil { } /** - * Print binary tree + * 打印二叉树 * This tree printer is borrowed from TECHIE DELIGHT * https://www.techiedelight.com/c-program-print-binary-tree/ */ @@ -104,12 +104,12 @@ public class PrintUtil { } } - /* Print heap (Priority queue) */ + /* Print heap (priority queue) */ public static void printHeap(Queue queue) { List list = new ArrayList<>(queue); - System.out.print("Array representation of the heap:"); + System.out.print("Heap array representation:"); System.out.println(list); - System.out.println("Tree representation of the heap:"); + System.out.println("Heap tree representation:"); TreeNode root = TreeNode.listToTree(list); printTree(root); } diff --git a/en/codes/java/utils/TreeNode.java b/en/codes/java/utils/TreeNode.java index b9af43219..b70544503 100644 --- a/en/codes/java/utils/TreeNode.java +++ b/en/codes/java/utils/TreeNode.java @@ -12,19 +12,19 @@ import java.util.*; public class TreeNode { public int val; // Node value public int height; // Node height - public TreeNode left; // Reference to the left child node - public TreeNode right; // Reference to the right child node + public TreeNode left; // Reference to left child node + public TreeNode right; // Reference to right child node /* Constructor */ public TreeNode(int x) { val = x; } - // For serialization encoding rules, refer to: + // For the serialization encoding rules, please refer to: // https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ - // Array representation of the binary tree: + // Array representation of binary tree: // [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] - // Linked list representation of the binary tree: + // Linked list representation of binary tree: // /——— 15 // /——— 7 // /——— 3 @@ -36,7 +36,7 @@ public class TreeNode { // \——— 4 // \——— 8 - /* Deserialize a list into a binary tree: Recursively */ + /* Deserialize a list into a binary tree: recursion */ private static TreeNode listToTreeDFS(List arr, int i) { if (i < 0 || i >= arr.size() || arr.get(i) == null) { return null; @@ -52,7 +52,7 @@ public class TreeNode { return listToTreeDFS(arr, 0); } - /* Serialize a binary tree into a list: Recursively */ + /* Serialize a binary tree into a list: recursion */ private static void treeToListDFS(TreeNode root, int i, List res) { if (root == null) return; diff --git a/en/codes/java/utils/Vertex.java b/en/codes/java/utils/Vertex.java index 92e950abe..91f369387 100644 --- a/en/codes/java/utils/Vertex.java +++ b/en/codes/java/utils/Vertex.java @@ -16,7 +16,7 @@ public class Vertex { this.val = val; } - /* Input a list of values vals, return a list of vertices vets */ + /* Input value list vals, return vertex list vets */ public static Vertex[] valsToVets(int[] vals) { Vertex[] vets = new Vertex[vals.length]; for (int i = 0; i < vals.length; i++) { @@ -25,7 +25,7 @@ public class Vertex { return vets; } - /* Input a list of vertices vets, return a list of values vals */ + /* Input vertex list vets, return value list vals */ public static List vetsToVals(List vets) { List vals = new ArrayList<>(); for (Vertex vet : vets) { diff --git a/en/codes/javascript/.prettierrc b/en/codes/javascript/.prettierrc new file mode 100644 index 000000000..3f4aa8cb6 --- /dev/null +++ b/en/codes/javascript/.prettierrc @@ -0,0 +1,6 @@ +{ + "tabWidth": 4, + "useTabs": false, + "semi": true, + "singleQuote": true +} diff --git a/en/codes/javascript/chapter_array_and_linkedlist/array.js b/en/codes/javascript/chapter_array_and_linkedlist/array.js new file mode 100644 index 000000000..9fd628b9e --- /dev/null +++ b/en/codes/javascript/chapter_array_and_linkedlist/array.js @@ -0,0 +1,97 @@ +/** + * File: array.js + * Created Time: 2022-11-27 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +/* Random access to element */ +function randomAccess(nums) { + // Randomly select a number in the interval [0, nums.length) + const random_index = Math.floor(Math.random() * nums.length); + // Retrieve and return the random element + const random_num = nums[random_index]; + return random_num; +} + +/* Extend array length */ +// Note: JavaScript's Array is dynamic array, can be directly expanded +// For learning purposes, this function treats Array as fixed-length array +function extend(nums, enlarge) { + // Initialize an array with extended length + const res = new Array(nums.length + enlarge).fill(0); + // Copy all elements from the original array to the new array + for (let i = 0; i < nums.length; i++) { + res[i] = nums[i]; + } + // Return the extended new array + return res; +} + +/* Insert element num at index index in the array */ +function insert(nums, num, index) { + // Move all elements at and after index index backward by one position + for (let i = nums.length - 1; i > index; i--) { + nums[i] = nums[i - 1]; + } + // Assign num to the element at index index + nums[index] = num; +} + +/* Remove the element at index index */ +function remove(nums, index) { + // Move all elements after index index forward by one position + for (let i = index; i < nums.length - 1; i++) { + nums[i] = nums[i + 1]; + } +} + +/* Traverse array */ +function traverse(nums) { + let count = 0; + // Traverse array by index + for (let i = 0; i < nums.length; i++) { + count += nums[i]; + } + // Direct traversal of array elements + for (const num of nums) { + count += num; + } +} + +/* Find the specified element in the array */ +function find(nums, target) { + for (let i = 0; i < nums.length; i++) { + if (nums[i] === target) return i; + } + return -1; +} + +/* Driver Code */ +/* Initialize array */ +const arr = new Array(5).fill(0); +console.log('Array arr =', arr); +let nums = [1, 3, 2, 5, 4]; +console.log('Array nums =', nums); + +/* Insert element */ +let random_num = randomAccess(nums); +console.log('Get random element in nums', random_num); + +/* Traverse array */ +nums = extend(nums, 3); +console.log('Extend array length to 8, get nums =', nums); + +/* Insert element */ +insert(nums, 6, 3); +console.log('Insert number 6 at index 3, get nums =', nums); + +/* Remove element */ +remove(nums, 2); +console.log('Remove element at index 2, get nums =', nums); + +/* Traverse array */ +traverse(nums); + +/* Find element */ +let index = find(nums, 3); +console.log('Find element 3 in nums, get index =', index); diff --git a/en/codes/javascript/chapter_array_and_linkedlist/linked_list.js b/en/codes/javascript/chapter_array_and_linkedlist/linked_list.js new file mode 100644 index 000000000..ed2756030 --- /dev/null +++ b/en/codes/javascript/chapter_array_and_linkedlist/linked_list.js @@ -0,0 +1,82 @@ +/** + * File: linked_list.js + * Created Time: 2022-12-12 + * Author: IsChristina (christinaxia77@foxmail.com), Justin (xiefahit@gmail.com) + */ + +const { printLinkedList } = require('../modules/PrintUtil'); +const { ListNode } = require('../modules/ListNode'); + +/* Insert node P after node n0 in the linked list */ +function insert(n0, P) { + const n1 = n0.next; + P.next = n1; + n0.next = P; +} + +/* Remove the first node after node n0 in the linked list */ +function remove(n0) { + if (!n0.next) return; + // n0 -> P -> n1 + const P = n0.next; + const n1 = P.next; + n0.next = n1; +} + +/* Access the node at index index in the linked list */ +function access(head, index) { + for (let i = 0; i < index; i++) { + if (!head) { + return null; + } + head = head.next; + } + return head; +} + +/* Find the first node with value target in the linked list */ +function find(head, target) { + let index = 0; + while (head !== null) { + if (head.val === target) { + return index; + } + head = head.next; + index += 1; + } + return -1; +} + +/* Driver Code */ +/* Initialize linked list */ +// Initialize each node +const n0 = new ListNode(1); +const n1 = new ListNode(3); +const n2 = new ListNode(2); +const n3 = new ListNode(5); +const n4 = new ListNode(4); +// Build references between nodes +n0.next = n1; +n1.next = n2; +n2.next = n3; +n3.next = n4; +console.log('Initialized linked list is'); +printLinkedList(n0); + +/* Insert node */ +insert(n0, new ListNode(0)); +console.log('Linked list after inserting node is'); +printLinkedList(n0); + +/* Remove node */ +remove(n0); +console.log('Linked list after removing node is'); +printLinkedList(n0); + +/* Access node */ +const node = access(n0, 3); +console.log('Value of node at index 3 in linked list = ' + node.val); + +/* Search node */ +const index = find(n0, 2); +console.log('Index of node with value 2 in linked list = ' + index); diff --git a/en/codes/javascript/chapter_array_and_linkedlist/list.js b/en/codes/javascript/chapter_array_and_linkedlist/list.js new file mode 100644 index 000000000..f22517efd --- /dev/null +++ b/en/codes/javascript/chapter_array_and_linkedlist/list.js @@ -0,0 +1,57 @@ +/** + * File: list.js + * Created Time: 2022-12-12 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Initialize list */ +const nums = [1, 3, 2, 5, 4]; +console.log(`List nums = ${nums}`); + +/* Update element */ +const num = nums[1]; +console.log(`Access element at index 1, get num = ${num}`); + +/* Add elements at the end */ +nums[1] = 0; +console.log(`Update element at index 1 to 0, get nums = ${nums}`); + +/* Remove element */ +nums.length = 0; +console.log(`After clearing list, nums = ${nums}`); + +/* Direct traversal of list elements */ +nums.push(1); +nums.push(3); +nums.push(2); +nums.push(5); +nums.push(4); +console.log(`After adding elements, nums = ${nums}`); + +/* Sort list */ +nums.splice(3, 0, 6); +console.log(`Insert number 6 at index 3, get nums = ${nums}`); + +/* Remove element */ +nums.splice(3, 1); +console.log(`Delete element at index 3, get nums = ${nums}`); + +/* Traverse list by index */ +let count = 0; +for (let i = 0; i < nums.length; i++) { + count += nums[i]; +} +/* Directly traverse list elements */ +count = 0; +for (const x of nums) { + count += x; +} + +/* Concatenate two lists */ +const nums1 = [6, 8, 7, 10, 9]; +nums.push(...nums1); +console.log(`After concatenating list nums1 to nums, get nums = ${nums}`); + +/* Sort list */ +nums.sort((a, b) => a - b); +console.log(`After sorting list, nums = ${nums}`); diff --git a/en/codes/javascript/chapter_array_and_linkedlist/my_list.js b/en/codes/javascript/chapter_array_and_linkedlist/my_list.js new file mode 100644 index 000000000..7b728bcb2 --- /dev/null +++ b/en/codes/javascript/chapter_array_and_linkedlist/my_list.js @@ -0,0 +1,141 @@ +/** + * File: my_list.js + * Created Time: 2022-12-12 + * Author: Justin (xiefahit@gmail.com) + */ + +/* List class */ +class MyList { + #arr = new Array(); // Array (stores list elements) + #capacity = 10; // List capacity + #size = 0; // List length (current number of elements) + #extendRatio = 2; // Multiple by which the list capacity is extended each time + + /* Constructor */ + constructor() { + this.#arr = new Array(this.#capacity); + } + + /* Get list length (current number of elements) */ + size() { + return this.#size; + } + + /* Get list capacity */ + capacity() { + return this.#capacity; + } + + /* Update element */ + get(index) { + // If the index is out of bounds, throw an exception, as below + if (index < 0 || index >= this.#size) throw new Error('Index out of bounds'); + return this.#arr[index]; + } + + /* Add elements at the end */ + set(index, num) { + if (index < 0 || index >= this.#size) throw new Error('Index out of bounds'); + this.#arr[index] = num; + } + + /* Direct traversal of list elements */ + add(num) { + // If length equals capacity, need to expand + if (this.#size === this.#capacity) { + this.extendCapacity(); + } + // Add new element to end of list + this.#arr[this.#size] = num; + this.#size++; + } + + /* Sort list */ + insert(index, num) { + if (index < 0 || index >= this.#size) throw new Error('Index out of bounds'); + // When the number of elements exceeds capacity, trigger the extension mechanism + if (this.#size === this.#capacity) { + this.extendCapacity(); + } + // Move all elements after index index forward by one position + for (let j = this.#size - 1; j >= index; j--) { + this.#arr[j + 1] = this.#arr[j]; + } + // Update the number of elements + this.#arr[index] = num; + this.#size++; + } + + /* Remove element */ + remove(index) { + if (index < 0 || index >= this.#size) throw new Error('Index out of bounds'); + let num = this.#arr[index]; + // Create a new array with length _extend_ratio times the original array, and copy the original array to the new array + for (let j = index; j < this.#size - 1; j++) { + this.#arr[j] = this.#arr[j + 1]; + } + // Update the number of elements + this.#size--; + // Return the removed element + return num; + } + + /* Driver Code */ + extendCapacity() { + // Create a new array with length extendRatio times the original array and copy the original array to the new array + this.#arr = this.#arr.concat( + new Array(this.capacity() * (this.#extendRatio - 1)) + ); + // Add elements at the end + this.#capacity = this.#arr.length; + } + + /* Convert list to array */ + toArray() { + let size = this.size(); + // Elements enqueue + const arr = new Array(size); + for (let i = 0; i < size; i++) { + arr[i] = this.get(i); + } + return arr; + } +} + +/* Driver Code */ +/* Initialize list */ +const nums = new MyList(); +/* Direct traversal of list elements */ +nums.add(1); +nums.add(3); +nums.add(2); +nums.add(5); +nums.add(4); +console.log( + `List nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}` +); + +/* Sort list */ +nums.insert(3, 6); +console.log(`Insert number 6 at index 3, get nums = ${nums.toArray()}`); + +/* Remove element */ +nums.remove(3); +console.log(`Delete element at index 3, get nums = ${nums.toArray()}`); + +/* Update element */ +const num = nums.get(1); +console.log(`Access element at index 1, get num = ${num}`); + +/* Add elements at the end */ +nums.set(1, 0); +console.log(`Update element at index 1 to 0, get nums = ${nums.toArray()}`); + +/* Test capacity expansion mechanism */ +for (let i = 0; i < 10; i++) { + // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + nums.add(i); +} +console.log( + `After expansion, list nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}` +); diff --git a/en/codes/javascript/chapter_backtracking/n_queens.js b/en/codes/javascript/chapter_backtracking/n_queens.js new file mode 100644 index 000000000..2e6997311 --- /dev/null +++ b/en/codes/javascript/chapter_backtracking/n_queens.js @@ -0,0 +1,55 @@ +/** + * File: n_queens.js + * Created Time: 2023-05-13 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Backtracking algorithm: N queens */ +function backtrack(row, n, state, res, cols, diags1, diags2) { + // When all rows are placed, record the solution + if (row === n) { + res.push(state.map((row) => row.slice())); + return; + } + // Traverse all columns + for (let col = 0; col < n; col++) { + // Calculate the main diagonal and anti-diagonal corresponding to this cell + const diag1 = row - col + n - 1; + const diag2 = row + col; + // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { + // Attempt: place the queen in this cell + state[row][col] = 'Q'; + cols[col] = diags1[diag1] = diags2[diag2] = true; + // Place the next row + backtrack(row + 1, n, state, res, cols, diags1, diags2); + // Backtrack: restore this cell to an empty cell + state[row][col] = '#'; + cols[col] = diags1[diag1] = diags2[diag2] = false; + } + } +} + +/* Solve N queens */ +function nQueens(n) { + // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + const state = Array.from({ length: n }, () => Array(n).fill('#')); + const cols = Array(n).fill(false); // Record whether there is a queen in the column + const diags1 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the main diagonal + const diags2 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the anti-diagonal + const res = []; + + backtrack(0, n, state, res, cols, diags1, diags2); + return res; +} + +// Driver Code +const n = 4; +const res = nQueens(n); + +console.log(`Input board size is ${n}`); +console.log(`Total queen placement solutions: ${res.length}`); +res.forEach((state) => { + console.log('--------------------'); + state.forEach((row) => console.log(row)); +}); diff --git a/en/codes/javascript/chapter_backtracking/permutations_i.js b/en/codes/javascript/chapter_backtracking/permutations_i.js new file mode 100644 index 000000000..156772f37 --- /dev/null +++ b/en/codes/javascript/chapter_backtracking/permutations_i.js @@ -0,0 +1,42 @@ +/** + * File: permutations_i.js + * Created Time: 2023-05-13 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Backtracking algorithm: Permutations I */ +function backtrack(state, choices, selected, res) { + // When the state length equals the number of elements, record the solution + if (state.length === choices.length) { + res.push([...state]); + return; + } + // Traverse all choices + choices.forEach((choice, i) => { + // Pruning: do not allow repeated selection of elements + if (!selected[i]) { + // Attempt: make choice, update state + selected[i] = true; + state.push(choice); + // Proceed to the next round of selection + backtrack(state, choices, selected, res); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + state.pop(); + } + }); +} + +/* Permutations I */ +function permutationsI(nums) { + const res = []; + backtrack([], nums, Array(nums.length).fill(false), res); + return res; +} + +// Driver Code +const nums = [1, 2, 3]; +const res = permutationsI(nums); + +console.log(`Input array nums = ${JSON.stringify(nums)}`); +console.log(`All permutations res = ${JSON.stringify(res)}`); diff --git a/en/codes/javascript/chapter_backtracking/permutations_ii.js b/en/codes/javascript/chapter_backtracking/permutations_ii.js new file mode 100644 index 000000000..7909ac3e0 --- /dev/null +++ b/en/codes/javascript/chapter_backtracking/permutations_ii.js @@ -0,0 +1,44 @@ +/** + * File: permutations_ii.js + * Created Time: 2023-05-13 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Backtracking algorithm: Permutations II */ +function backtrack(state, choices, selected, res) { + // When the state length equals the number of elements, record the solution + if (state.length === choices.length) { + res.push([...state]); + return; + } + // Traverse all choices + const duplicated = new Set(); + choices.forEach((choice, i) => { + // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if (!selected[i] && !duplicated.has(choice)) { + // Attempt: make choice, update state + duplicated.add(choice); // Record the selected element value + selected[i] = true; + state.push(choice); + // Proceed to the next round of selection + backtrack(state, choices, selected, res); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + state.pop(); + } + }); +} + +/* Permutations II */ +function permutationsII(nums) { + const res = []; + backtrack([], nums, Array(nums.length).fill(false), res); + return res; +} + +// Driver Code +const nums = [1, 2, 2]; +const res = permutationsII(nums); + +console.log(`Input array nums = ${JSON.stringify(nums)}`); +console.log(`All permutations res = ${JSON.stringify(res)}`); diff --git a/en/codes/javascript/chapter_backtracking/preorder_traversal_i_compact.js b/en/codes/javascript/chapter_backtracking/preorder_traversal_i_compact.js new file mode 100644 index 000000000..2e2ef4699 --- /dev/null +++ b/en/codes/javascript/chapter_backtracking/preorder_traversal_i_compact.js @@ -0,0 +1,33 @@ +/** + * File: preorder_traversal_i_compact.js + * Created Time: 2023-05-09 + * Author: Justin (xiefahit@gmail.com) + */ + +const { arrToTree } = require('../modules/TreeNode'); +const { printTree } = require('../modules/PrintUtil'); + +/* Preorder traversal: Example 1 */ +function preOrder(root, res) { + if (root === null) { + return; + } + if (root.val === 7) { + // Record solution + res.push(root); + } + preOrder(root.left, res); + preOrder(root.right, res); +} + +// Driver Code +const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree'); +printTree(root); + +// Preorder traversal +const res = []; +preOrder(root, res); + +console.log('\nOutput all nodes with value 7'); +console.log(res.map((node) => node.val)); diff --git a/en/codes/javascript/chapter_backtracking/preorder_traversal_ii_compact.js b/en/codes/javascript/chapter_backtracking/preorder_traversal_ii_compact.js new file mode 100644 index 000000000..6898ae5dd --- /dev/null +++ b/en/codes/javascript/chapter_backtracking/preorder_traversal_ii_compact.js @@ -0,0 +1,40 @@ +/** + * File: preorder_traversal_ii_compact.js + * Created Time: 2023-05-09 + * Author: Justin (xiefahit@gmail.com) + */ + +const { arrToTree } = require('../modules/TreeNode'); +const { printTree } = require('../modules/PrintUtil'); + +/* Preorder traversal: Example 2 */ +function preOrder(root, path, res) { + if (root === null) { + return; + } + // Attempt + path.push(root); + if (root.val === 7) { + // Record solution + res.push([...path]); + } + preOrder(root.left, path, res); + preOrder(root.right, path, res); + // Backtrack + path.pop(); +} + +// Driver Code +const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree'); +printTree(root); + +// Preorder traversal +const path = []; +const res = []; +preOrder(root, path, res); + +console.log('\nOutput all paths from root node to node 7'); +res.forEach((path) => { + console.log(path.map((node) => node.val)); +}); diff --git a/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_compact.js b/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_compact.js new file mode 100644 index 000000000..eb51e0ed8 --- /dev/null +++ b/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_compact.js @@ -0,0 +1,41 @@ +/** + * File: preorder_traversal_iii_compact.js + * Created Time: 2023-05-09 + * Author: Justin (xiefahit@gmail.com) + */ + +const { arrToTree } = require('../modules/TreeNode'); +const { printTree } = require('../modules/PrintUtil'); + +/* Preorder traversal: Example 3 */ +function preOrder(root, path, res) { + // Pruning + if (root === null || root.val === 3) { + return; + } + // Attempt + path.push(root); + if (root.val === 7) { + // Record solution + res.push([...path]); + } + preOrder(root.left, path, res); + preOrder(root.right, path, res); + // Backtrack + path.pop(); +} + +// Driver Code +const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree'); +printTree(root); + +// Preorder traversal +const path = []; +const res = []; +preOrder(root, path, res); + +console.log('\nOutput all paths from root node to node 7, paths do not include nodes with value 3'); +res.forEach((path) => { + console.log(path.map((node) => node.val)); +}); diff --git a/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_template.js b/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_template.js new file mode 100644 index 000000000..594a2b475 --- /dev/null +++ b/en/codes/javascript/chapter_backtracking/preorder_traversal_iii_template.js @@ -0,0 +1,68 @@ +/** + * File: preorder_traversal_iii_template.js + * Created Time: 2023-05-09 + * Author: Justin (xiefahit@gmail.com) + */ + +const { arrToTree } = require('../modules/TreeNode'); +const { printTree } = require('../modules/PrintUtil'); + +/* Check if the current state is a solution */ +function isSolution(state) { + return state && state[state.length - 1]?.val === 7; +} + +/* Record solution */ +function recordSolution(state, res) { + res.push([...state]); +} + +/* Check if the choice is valid under the current state */ +function isValid(state, choice) { + return choice !== null && choice.val !== 3; +} + +/* Update state */ +function makeChoice(state, choice) { + state.push(choice); +} + +/* Restore state */ +function undoChoice(state) { + state.pop(); +} + +/* Backtracking algorithm: Example 3 */ +function backtrack(state, choices, res) { + // Check if it is a solution + if (isSolution(state)) { + // Record solution + recordSolution(state, res); + } + // Traverse all choices + for (const choice of choices) { + // Pruning: check if the choice is valid + if (isValid(state, choice)) { + // Attempt: make choice, update state + makeChoice(state, choice); + // Proceed to the next round of selection + backtrack(state, [choice.left, choice.right], res); + // Backtrack: undo choice, restore to previous state + undoChoice(state); + } + } +} + +// Driver Code +const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree'); +printTree(root); + +// Backtracking algorithm +const res = []; +backtrack([], [root], res); + +console.log('\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3'); +res.forEach((path) => { + console.log(path.map((node) => node.val)); +}); diff --git a/en/codes/javascript/chapter_backtracking/subset_sum_i.js b/en/codes/javascript/chapter_backtracking/subset_sum_i.js new file mode 100644 index 000000000..6fde6ae12 --- /dev/null +++ b/en/codes/javascript/chapter_backtracking/subset_sum_i.js @@ -0,0 +1,46 @@ +/** + * File: subset_sum_i.js + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Backtracking algorithm: Subset sum I */ +function backtrack(state, target, choices, start, res) { + // When the subset sum equals target, record the solution + if (target === 0) { + res.push([...state]); + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + for (let i = start; i < choices.length; i++) { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if (target - choices[i] < 0) { + break; + } + // Attempt: make choice, update target, start + state.push(choices[i]); + // Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i, res); + // Backtrack: undo choice, restore to previous state + state.pop(); + } +} + +/* Solve subset sum I */ +function subsetSumI(nums, target) { + const state = []; // State (subset) + nums.sort((a, b) => a - b); // Sort nums + const start = 0; // Start point for traversal + const res = []; // Result list (subset list) + backtrack(state, target, nums, start, res); + return res; +} + +/* Driver Code */ +const nums = [3, 4, 5]; +const target = 9; +const res = subsetSumI(nums, target); +console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); +console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); diff --git a/en/codes/javascript/chapter_backtracking/subset_sum_i_naive.js b/en/codes/javascript/chapter_backtracking/subset_sum_i_naive.js new file mode 100644 index 000000000..2e02a3c16 --- /dev/null +++ b/en/codes/javascript/chapter_backtracking/subset_sum_i_naive.js @@ -0,0 +1,44 @@ +/** + * File: subset_sum_i_naive.js + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Backtracking algorithm: Subset sum I */ +function backtrack(state, target, total, choices, res) { + // When the subset sum equals target, record the solution + if (total === target) { + res.push([...state]); + return; + } + // Traverse all choices + for (let i = 0; i < choices.length; i++) { + // Pruning: if the subset sum exceeds target, skip this choice + if (total + choices[i] > target) { + continue; + } + // Attempt: make choice, update element sum total + state.push(choices[i]); + // Proceed to the next round of selection + backtrack(state, target, total + choices[i], choices, res); + // Backtrack: undo choice, restore to previous state + state.pop(); + } +} + +/* Solve subset sum I (including duplicate subsets) */ +function subsetSumINaive(nums, target) { + const state = []; // State (subset) + const total = 0; // Subset sum + const res = []; // Result list (subset list) + backtrack(state, target, total, nums, res); + return res; +} + +/* Driver Code */ +const nums = [3, 4, 5]; +const target = 9; +const res = subsetSumINaive(nums, target); +console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); +console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); +console.log('Please note that this method outputs results containing duplicate sets'); diff --git a/en/codes/javascript/chapter_backtracking/subset_sum_ii.js b/en/codes/javascript/chapter_backtracking/subset_sum_ii.js new file mode 100644 index 000000000..ea5e3d33a --- /dev/null +++ b/en/codes/javascript/chapter_backtracking/subset_sum_ii.js @@ -0,0 +1,51 @@ +/** + * File: subset_sum_ii.js + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Backtracking algorithm: Subset sum II */ +function backtrack(state, target, choices, start, res) { + // When the subset sum equals target, record the solution + if (target === 0) { + res.push([...state]); + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + for (let i = start; i < choices.length; i++) { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if (target - choices[i] < 0) { + break; + } + // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + if (i > start && choices[i] === choices[i - 1]) { + continue; + } + // Attempt: make choice, update target, start + state.push(choices[i]); + // Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i + 1, res); + // Backtrack: undo choice, restore to previous state + state.pop(); + } +} + +/* Solve subset sum II */ +function subsetSumII(nums, target) { + const state = []; // State (subset) + nums.sort((a, b) => a - b); // Sort nums + const start = 0; // Start point for traversal + const res = []; // Result list (subset list) + backtrack(state, target, nums, start, res); + return res; +} + +/* Driver Code */ +const nums = [4, 4, 5]; +const target = 9; +const res = subsetSumII(nums, target); +console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); +console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); diff --git a/en/codes/javascript/chapter_computational_complexity/iteration.js b/en/codes/javascript/chapter_computational_complexity/iteration.js new file mode 100644 index 000000000..67fcffc83 --- /dev/null +++ b/en/codes/javascript/chapter_computational_complexity/iteration.js @@ -0,0 +1,70 @@ +/** + * File: iteration.js + * Created Time: 2023-08-28 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* for loop */ +function forLoop(n) { + let res = 0; + // Sum 1, 2, ..., n-1, n + for (let i = 1; i <= n; i++) { + res += i; + } + return res; +} + +/* while loop */ +function whileLoop(n) { + let res = 0; + let i = 1; // Initialize condition variable + // Sum 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // Update condition variable + } + return res; +} + +/* while loop (two updates) */ +function whileLoopII(n) { + let res = 0; + let i = 1; // Initialize condition variable + // Sum 1, 4, 10, ... + while (i <= n) { + res += i; + // Update condition variable + i++; + i *= 2; + } + return res; +} + +/* Nested for loop */ +function nestedForLoop(n) { + let res = ''; + // Loop i = 1, 2, ..., n-1, n + for (let i = 1; i <= n; i++) { + // Loop j = 1, 2, ..., n-1, n + for (let j = 1; j <= n; j++) { + res += `(${i}, ${j}), `; + } + } + return res; +} + +/* Driver Code */ +const n = 5; +let res; + +res = forLoop(n); +console.log(`For loop sum result res = ${res}`); + +res = whileLoop(n); +console.log(`While loop sum result res = ${res}`); + +res = whileLoopII(n); +console.log(`While loop (two updates) sum result res = ${res}`); + +const resStr = nestedForLoop(n); +console.log(`Nested for loop traversal result ${resStr}`); diff --git a/en/codes/javascript/chapter_computational_complexity/recursion.js b/en/codes/javascript/chapter_computational_complexity/recursion.js new file mode 100644 index 000000000..6bc62355e --- /dev/null +++ b/en/codes/javascript/chapter_computational_complexity/recursion.js @@ -0,0 +1,69 @@ +/** + * File: recursion.js + * Created Time: 2023-08-28 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Recursion */ +function recur(n) { + // Termination condition + if (n === 1) return 1; + // Recurse: recursive call + const res = recur(n - 1); + // Return: return result + return n + res; +} + +/* Simulate recursion using iteration */ +function forLoopRecur(n) { + // Use an explicit stack to simulate the system call stack + const stack = []; + let res = 0; + // Recurse: recursive call + for (let i = n; i > 0; i--) { + // Simulate "recurse" with "push" + stack.push(i); + } + // Return: return result + while (stack.length) { + // Simulate "return" with "pop" + res += stack.pop(); + } + // res = 1+2+3+...+n + return res; +} + +/* Tail recursion */ +function tailRecur(n, res) { + // Termination condition + if (n === 0) return res; + // Tail recursive call + return tailRecur(n - 1, res + n); +} + +/* Fibonacci sequence: recursion */ +function fib(n) { + // Termination condition f(1) = 0, f(2) = 1 + if (n === 1 || n === 2) return n - 1; + // Recursive call f(n) = f(n-1) + f(n-2) + const res = fib(n - 1) + fib(n - 2); + // Return result f(n) + return res; +} + +/* Driver Code */ +const n = 5; +let res; + +res = recur(n); +console.log(`Recursion sum result res = ${res}`); + +res = forLoopRecur(n); +console.log(`Using iteration to simulate recursion sum result res = ${res}`); + +res = tailRecur(n, 0); +console.log(`Tail recursion sum result res = ${res}`); + +res = fib(n); +console.log(`The ${n}th Fibonacci number is ${res}`); + diff --git a/en/codes/javascript/chapter_computational_complexity/space_complexity.js b/en/codes/javascript/chapter_computational_complexity/space_complexity.js new file mode 100644 index 000000000..47b41e48d --- /dev/null +++ b/en/codes/javascript/chapter_computational_complexity/space_complexity.js @@ -0,0 +1,103 @@ +/** + * File: space_complexity.js + * Created Time: 2023-02-05 + * Author: Justin (xiefahit@gmail.com) + */ + +const { ListNode } = require('../modules/ListNode'); +const { TreeNode } = require('../modules/TreeNode'); +const { printTree } = require('../modules/PrintUtil'); + +/* Function */ +function constFunc() { + // Perform some operations + return 0; +} + +/* Constant order */ +function constant(n) { + // Constants, variables, objects occupy O(1) space + const a = 0; + const b = 0; + const nums = new Array(10000); + const node = new ListNode(0); + // Variables in the loop occupy O(1) space + for (let i = 0; i < n; i++) { + const c = 0; + } + // Functions in the loop occupy O(1) space + for (let i = 0; i < n; i++) { + constFunc(); + } +} + +/* Linear order */ +function linear(n) { + // Array of length n uses O(n) space + const nums = new Array(n); + // A list of length n occupies O(n) space + const nodes = []; + for (let i = 0; i < n; i++) { + nodes.push(new ListNode(i)); + } + // A hash table of length n occupies O(n) space + const map = new Map(); + for (let i = 0; i < n; i++) { + map.set(i, i.toString()); + } +} + +/* Linear order (recursive implementation) */ +function linearRecur(n) { + console.log(`Recursion n = ${n}`); + if (n === 1) return; + linearRecur(n - 1); +} + +/* Exponential order */ +function quadratic(n) { + // Matrix uses O(n^2) space + const numMatrix = Array(n) + .fill(null) + .map(() => Array(n).fill(null)); + // 2D list uses O(n^2) space + const numList = []; + for (let i = 0; i < n; i++) { + const tmp = []; + for (let j = 0; j < n; j++) { + tmp.push(0); + } + numList.push(tmp); + } +} + +/* Quadratic order (recursive implementation) */ +function quadraticRecur(n) { + if (n <= 0) return 0; + const nums = new Array(n); + console.log(`In recursion n = ${n}, nums length = ${nums.length}`); + return quadraticRecur(n - 1); +} + +/* Driver Code */ +function buildTree(n) { + if (n === 0) return null; + const root = new TreeNode(0); + root.left = buildTree(n - 1); + root.right = buildTree(n - 1); + return root; +} + +/* Driver Code */ +const n = 5; +// Constant order +constant(n); +// Linear order +linear(n); +linearRecur(n); +// Exponential order +quadratic(n); +quadraticRecur(n); +// Exponential order +const root = buildTree(n); +printTree(root); diff --git a/en/codes/javascript/chapter_computational_complexity/time_complexity.js b/en/codes/javascript/chapter_computational_complexity/time_complexity.js new file mode 100644 index 000000000..399c4699a --- /dev/null +++ b/en/codes/javascript/chapter_computational_complexity/time_complexity.js @@ -0,0 +1,155 @@ +/** + * File: time_complexity.js + * Created Time: 2023-01-02 + * Author: RiverTwilight (contact@rene.wang) + */ + +/* Constant order */ +function constant(n) { + let count = 0; + const size = 100000; + for (let i = 0; i < size; i++) count++; + return count; +} + +/* Linear order */ +function linear(n) { + let count = 0; + for (let i = 0; i < n; i++) count++; + return count; +} + +/* Linear order (traversing array) */ +function arrayTraversal(nums) { + let count = 0; + // Number of iterations is proportional to the array length + for (let i = 0; i < nums.length; i++) { + count++; + } + return count; +} + +/* Exponential order */ +function quadratic(n) { + let count = 0; + // Number of iterations is quadratically related to the data size n + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + count++; + } + } + return count; +} + +/* Quadratic order (bubble sort) */ +function bubbleSort(nums) { + let count = 0; // Counter + // Outer loop: unsorted range is [0, i] + for (let i = nums.length - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // Element swap includes 3 unit operations + } + } + } + return count; +} + +/* Exponential order (loop implementation) */ +function exponential(n) { + let count = 0, + base = 1; + // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + for (let i = 0; i < n; i++) { + for (let j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; +} + +/* Exponential order (recursive implementation) */ +function expRecur(n) { + if (n === 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; +} + +/* Logarithmic order (loop implementation) */ +function logarithmic(n) { + let count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; +} + +/* Logarithmic order (recursive implementation) */ +function logRecur(n) { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; +} + +/* Linearithmic order */ +function linearLogRecur(n) { + if (n <= 1) return 1; + let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (let i = 0; i < n; i++) { + count++; + } + return count; +} + +/* Factorial order (recursive implementation) */ +function factorialRecur(n) { + if (n === 0) return 1; + let count = 0; + // Split from 1 into n + for (let i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; +} + +/* Driver Code */ +// You can modify n to run and observe the trend of the number of operations for various complexities +const n = 8; +console.log('Input data size n = ' + n); + +let count = constant(n); +console.log('Constant order operation count = ' + count); + +count = linear(n); +console.log('Linear order operation count = ' + count); +count = arrayTraversal(new Array(n)); +console.log('Linear order (array traversal) operation count = ' + count); + +count = quadratic(n); +console.log('Quadratic order operation count = ' + count); +let nums = new Array(n); +for (let i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] +count = bubbleSort(nums); +console.log('Quadratic order (bubble sort) operation count = ' + count); + +count = exponential(n); +console.log('Exponential order (loop implementation) operation count = ' + count); +count = expRecur(n); +console.log('Exponential order (recursive implementation) operation count = ' + count); + +count = logarithmic(n); +console.log('Logarithmic order (loop implementation) operation count = ' + count); +count = logRecur(n); +console.log('Logarithmic order (recursive implementation) operation count = ' + count); + +count = linearLogRecur(n); +console.log('Linearithmic order (recursive implementation) operation count = ' + count); + +count = factorialRecur(n); +console.log('Factorial order (recursive implementation) operation count = ' + count); diff --git a/en/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js b/en/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js new file mode 100644 index 000000000..256faa5e8 --- /dev/null +++ b/en/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js @@ -0,0 +1,43 @@ +/** + * File: worst_best_time_complexity.js + * Created Time: 2023-01-05 + * Author: RiverTwilight (contact@rene.wang) + */ + +/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +function randomNumbers(n) { + const nums = Array(n); + // Generate array nums = { 1, 2, 3, ..., n } + for (let i = 0; i < n; i++) { + nums[i] = i + 1; + } + // Randomly shuffle array elements + for (let i = 0; i < n; i++) { + const r = Math.floor(Math.random() * (i + 1)); + const temp = nums[i]; + nums[i] = nums[r]; + nums[r] = temp; + } + return nums; +} + +/* Find the index of number 1 in array nums */ +function findOne(nums) { + for (let i = 0; i < nums.length; i++) { + // When element 1 is at the head of the array, best time complexity O(1) is achieved + // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + if (nums[i] === 1) { + return i; + } + } + return -1; +} + +/* Driver Code */ +for (let i = 0; i < 10; i++) { + const n = 100; + const nums = randomNumbers(n); + const index = findOne(nums); + console.log('\nArray [ 1, 2, ..., n ] after shuffling = [' + nums.join(', ') + ']'); + console.log('Index of number 1 is ' + index); +} diff --git a/en/codes/javascript/chapter_divide_and_conquer/binary_search_recur.js b/en/codes/javascript/chapter_divide_and_conquer/binary_search_recur.js new file mode 100644 index 000000000..170ed9c70 --- /dev/null +++ b/en/codes/javascript/chapter_divide_and_conquer/binary_search_recur.js @@ -0,0 +1,39 @@ +/** + * File: binary_search_recur.js + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Binary search: problem f(i, j) */ +function dfs(nums, target, i, j) { + // If the interval is empty, it means there is no target element, return -1 + if (i > j) { + return -1; + } + // Calculate the midpoint index m + const m = i + ((j - i) >> 1); + if (nums[m] < target) { + // Recursion subproblem f(m+1, j) + return dfs(nums, target, m + 1, j); + } else if (nums[m] > target) { + // Recursion subproblem f(i, m-1) + return dfs(nums, target, i, m - 1); + } else { + // Found the target element, return its index + return m; + } +} + +/* Binary search */ +function binarySearch(nums, target) { + const n = nums.length; + // Solve the problem f(0, n-1) + return dfs(nums, target, 0, n - 1); +} + +/* Driver Code */ +const target = 6; +const nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; +// Binary search (closed interval on both sides) +const index = binarySearch(nums, target); +console.log(`Index of target element 6 is ${index}`); diff --git a/en/codes/javascript/chapter_divide_and_conquer/build_tree.js b/en/codes/javascript/chapter_divide_and_conquer/build_tree.js new file mode 100644 index 000000000..e7c26039a --- /dev/null +++ b/en/codes/javascript/chapter_divide_and_conquer/build_tree.js @@ -0,0 +1,44 @@ +/** + * File: build_tree.js + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +const { printTree } = require('../modules/PrintUtil'); +const { TreeNode } = require('../modules/TreeNode'); + +/* Build binary tree: divide and conquer */ +function dfs(preorder, inorderMap, i, l, r) { + // Terminate when the subtree interval is empty + if (r - l < 0) return null; + // Initialize the root node + const root = new TreeNode(preorder[i]); + // Query m to divide the left and right subtrees + const m = inorderMap.get(preorder[i]); + // Subproblem: build the left subtree + root.left = dfs(preorder, inorderMap, i + 1, l, m - 1); + // Subproblem: build the right subtree + root.right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r); + // Return the root node + return root; +} + +/* Build binary tree */ +function buildTree(preorder, inorder) { + // Initialize hash map, storing the mapping from inorder elements to indices + let inorderMap = new Map(); + for (let i = 0; i < inorder.length; i++) { + inorderMap.set(inorder[i], i); + } + const root = dfs(preorder, inorderMap, 0, 0, inorder.length - 1); + return root; +} + +/* Driver Code */ +const preorder = [3, 9, 2, 1, 7]; +const inorder = [9, 3, 1, 2, 7]; +console.log('Preorder traversal = ' + JSON.stringify(preorder)); +console.log('Inorder traversal = ' + JSON.stringify(inorder)); +const root = buildTree(preorder, inorder); +console.log('The constructed binary tree is:'); +printTree(root); diff --git a/en/codes/javascript/chapter_divide_and_conquer/hanota.js b/en/codes/javascript/chapter_divide_and_conquer/hanota.js new file mode 100644 index 000000000..c943f7805 --- /dev/null +++ b/en/codes/javascript/chapter_divide_and_conquer/hanota.js @@ -0,0 +1,52 @@ +/** + * File: hanota.js + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Move a disk */ +function move(src, tar) { + // Take out a disk from the top of src + const pan = src.pop(); + // Place the disk on top of tar + tar.push(pan); +} + +/* Solve the Tower of Hanoi problem f(i) */ +function dfs(i, src, buf, tar) { + // If there is only one disk left in src, move it directly to tar + if (i === 1) { + move(src, tar); + return; + } + // Subproblem f(i-1): move the top i-1 disks from src to buf using tar + dfs(i - 1, src, tar, buf); + // Subproblem f(1): move the remaining disk from src to tar + move(src, tar); + // Subproblem f(i-1): move the top i-1 disks from buf to tar using src + dfs(i - 1, buf, src, tar); +} + +/* Solve the Tower of Hanoi problem */ +function solveHanota(A, B, C) { + const n = A.length; + // Move the top n disks from A to C using B + dfs(n, A, B, C); +} + +/* Driver Code */ +// The tail of the list is the top of the rod +const A = [5, 4, 3, 2, 1]; +const B = []; +const C = []; +console.log('In initial state:'); +console.log(`A = ${JSON.stringify(A)}`); +console.log(`B = ${JSON.stringify(B)}`); +console.log(`C = ${JSON.stringify(C)}`); + +solveHanota(A, B, C); + +console.log('After disk movement is complete:'); +console.log(`A = ${JSON.stringify(A)}`); +console.log(`B = ${JSON.stringify(B)}`); +console.log(`C = ${JSON.stringify(C)}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_backtrack.js b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_backtrack.js new file mode 100644 index 000000000..d08f819f9 --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_backtrack.js @@ -0,0 +1,34 @@ +/** + * File: climbing_stairs_backtrack.js + * Created Time: 2023-07-26 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Backtracking */ +function backtrack(choices, state, n, res) { + // When climbing to the n-th stair, add 1 to the solution count + if (state === n) res.set(0, res.get(0) + 1); + // Traverse all choices + for (const choice of choices) { + // Pruning: not allowed to go beyond the n-th stair + if (state + choice > n) continue; + // Attempt: make choice, update state + backtrack(choices, state + choice, n, res); + // Backtrack + } +} + +/* Climbing stairs: Backtracking */ +function climbingStairsBacktrack(n) { + const choices = [1, 2]; // Can choose to climb up 1 or 2 stairs + const state = 0; // Start climbing from the 0-th stair + const res = new Map(); + res.set(0, 0); // Use res[0] to record the solution count + backtrack(choices, state, n, res); + return res.get(0); +} + +/* Driver Code */ +const n = 9; +const res = climbingStairsBacktrack(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); diff --git a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_constraint_dp.js b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_constraint_dp.js new file mode 100644 index 000000000..0f02bbbb3 --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_constraint_dp.js @@ -0,0 +1,30 @@ +/** + * File: climbing_stairs_constraint_dp.js + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Climbing stairs with constraint: Dynamic programming */ +function climbingStairsConstraintDP(n) { + if (n === 1 || n === 2) { + return 1; + } + // Initialize dp table, used to store solutions to subproblems + const dp = Array.from(new Array(n + 1), () => new Array(3)); + // Initial state: preset the solution to the smallest subproblem + dp[1][1] = 1; + dp[1][2] = 0; + dp[2][1] = 0; + dp[2][2] = 1; + // State transition: gradually solve larger subproblems from smaller ones + for (let i = 3; i <= n; i++) { + dp[i][1] = dp[i - 1][2]; + dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; + } + return dp[n][1] + dp[n][2]; +} + +/* Driver Code */ +const n = 9; +const res = climbingStairsConstraintDP(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); diff --git a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs.js b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs.js new file mode 100644 index 000000000..c5e47e5c7 --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs.js @@ -0,0 +1,24 @@ +/** + * File: climbing_stairs_dfs.js + * Created Time: 2023-07-26 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Search */ +function dfs(i) { + // Known dp[1] and dp[2], return them + if (i === 1 || i === 2) return i; + // dp[i] = dp[i-1] + dp[i-2] + const count = dfs(i - 1) + dfs(i - 2); + return count; +} + +/* Climbing stairs: Search */ +function climbingStairsDFS(n) { + return dfs(n); +} + +/* Driver Code */ +const n = 9; +const res = climbingStairsDFS(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); diff --git a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs_mem.js b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs_mem.js new file mode 100644 index 000000000..b476ac6da --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dfs_mem.js @@ -0,0 +1,30 @@ +/** + * File: climbing_stairs_dfs_mem.js + * Created Time: 2023-07-26 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Memoization search */ +function dfs(i, mem) { + // Known dp[1] and dp[2], return them + if (i === 1 || i === 2) return i; + // If record dp[i] exists, return it directly + if (mem[i] != -1) return mem[i]; + // dp[i] = dp[i-1] + dp[i-2] + const count = dfs(i - 1, mem) + dfs(i - 2, mem); + // Record dp[i] + mem[i] = count; + return count; +} + +/* Climbing stairs: Memoization search */ +function climbingStairsDFSMem(n) { + // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + const mem = new Array(n + 1).fill(-1); + return dfs(n, mem); +} + +/* Driver Code */ +const n = 9; +const res = climbingStairsDFSMem(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); diff --git a/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dp.js b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dp.js new file mode 100644 index 000000000..13f361e60 --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/climbing_stairs_dp.js @@ -0,0 +1,40 @@ +/** + * File: climbing_stairs_dp.js + * Created Time: 2023-07-26 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Climbing stairs: Dynamic programming */ +function climbingStairsDP(n) { + if (n === 1 || n === 2) return n; + // Initialize dp table, used to store solutions to subproblems + const dp = new Array(n + 1).fill(-1); + // Initial state: preset the solution to the smallest subproblem + dp[1] = 1; + dp[2] = 2; + // State transition: gradually solve larger subproblems from smaller ones + for (let i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; +} + +/* Climbing stairs: Space-optimized dynamic programming */ +function climbingStairsDPComp(n) { + if (n === 1 || n === 2) return n; + let a = 1, + b = 2; + for (let i = 3; i <= n; i++) { + const tmp = b; + b = a + b; + a = tmp; + } + return b; +} + +/* Driver Code */ +const n = 9; +let res = climbingStairsDP(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); +res = climbingStairsDPComp(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); diff --git a/en/codes/javascript/chapter_dynamic_programming/coin_change.js b/en/codes/javascript/chapter_dynamic_programming/coin_change.js new file mode 100644 index 000000000..e5899a558 --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/coin_change.js @@ -0,0 +1,66 @@ +/** + * File: coin_change.js + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Coin change: Dynamic programming */ +function coinChangeDP(coins, amt) { + const n = coins.length; + const MAX = amt + 1; + // Initialize dp table + const dp = Array.from({ length: n + 1 }, () => + Array.from({ length: amt + 1 }, () => 0) + ); + // State transition: first row and first column + for (let a = 1; a <= amt; a++) { + dp[0][a] = MAX; + } + // State transition: rest of the rows and columns + for (let i = 1; i <= n; i++) { + for (let a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[i][a] = Math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); + } + } + } + return dp[n][amt] !== MAX ? dp[n][amt] : -1; +} + +/* Coin change: Space-optimized dynamic programming */ +function coinChangeDPComp(coins, amt) { + const n = coins.length; + const MAX = amt + 1; + // Initialize dp table + const dp = Array.from({ length: amt + 1 }, () => MAX); + dp[0] = 0; + // State transition + for (let i = 1; i <= n; i++) { + for (let a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[a] = Math.min(dp[a], dp[a - coins[i - 1]] + 1); + } + } + } + return dp[amt] !== MAX ? dp[amt] : -1; +} + +/* Driver Code */ +const coins = [1, 2, 5]; +const amt = 4; + +// Dynamic programming +let res = coinChangeDP(coins, amt); +console.log(`Minimum coins needed to make target amount is ${res}`); + +// Space-optimized dynamic programming +res = coinChangeDPComp(coins, amt); +console.log(`Minimum coins needed to make target amount is ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/coin_change_ii.js b/en/codes/javascript/chapter_dynamic_programming/coin_change_ii.js new file mode 100644 index 000000000..2e4df5497 --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/coin_change_ii.js @@ -0,0 +1,64 @@ +/** + * File: coin_change_ii.js + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Coin change II: Dynamic programming */ +function coinChangeIIDP(coins, amt) { + const n = coins.length; + // Initialize dp table + const dp = Array.from({ length: n + 1 }, () => + Array.from({ length: amt + 1 }, () => 0) + ); + // Initialize first column + for (let i = 0; i <= n; i++) { + dp[i][0] = 1; + } + // State transition + for (let i = 1; i <= n; i++) { + for (let a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; + } + } + } + return dp[n][amt]; +} + +/* Coin change II: Space-optimized dynamic programming */ +function coinChangeIIDPComp(coins, amt) { + const n = coins.length; + // Initialize dp table + const dp = Array.from({ length: amt + 1 }, () => 0); + dp[0] = 1; + // State transition + for (let i = 1; i <= n; i++) { + for (let a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[a] = dp[a] + dp[a - coins[i - 1]]; + } + } + } + return dp[amt]; +} + +/* Driver Code */ +const coins = [1, 2, 5]; +const amt = 5; + +// Dynamic programming +let res = coinChangeIIDP(coins, amt); +console.log(`Number of coin combinations to make target amount is ${res}`); + +// Space-optimized dynamic programming +res = coinChangeIIDPComp(coins, amt); +console.log(`Number of coin combinations to make target amount is ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/edit_distance.js b/en/codes/javascript/chapter_dynamic_programming/edit_distance.js new file mode 100644 index 000000000..cd8259947 --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/edit_distance.js @@ -0,0 +1,135 @@ +/** + * File: edit_distance.js + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Edit distance: Brute-force search */ +function editDistanceDFS(s, t, i, j) { + // If both s and t are empty, return 0 + if (i === 0 && j === 0) return 0; + + // If s is empty, return length of t + if (i === 0) return j; + + // If t is empty, return length of s + if (j === 0) return i; + + // If two characters are equal, skip both characters + if (s.charAt(i - 1) === t.charAt(j - 1)) + return editDistanceDFS(s, t, i - 1, j - 1); + + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + const insert = editDistanceDFS(s, t, i, j - 1); + const del = editDistanceDFS(s, t, i - 1, j); + const replace = editDistanceDFS(s, t, i - 1, j - 1); + // Return minimum edit steps + return Math.min(insert, del, replace) + 1; +} + +/* Edit distance: Memoization search */ +function editDistanceDFSMem(s, t, mem, i, j) { + // If both s and t are empty, return 0 + if (i === 0 && j === 0) return 0; + + // If s is empty, return length of t + if (i === 0) return j; + + // If t is empty, return length of s + if (j === 0) return i; + + // If there's a record, return it directly + if (mem[i][j] !== -1) return mem[i][j]; + + // If two characters are equal, skip both characters + if (s.charAt(i - 1) === t.charAt(j - 1)) + return editDistanceDFSMem(s, t, mem, i - 1, j - 1); + + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + const insert = editDistanceDFSMem(s, t, mem, i, j - 1); + const del = editDistanceDFSMem(s, t, mem, i - 1, j); + const replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1); + // Record and return minimum edit steps + mem[i][j] = Math.min(insert, del, replace) + 1; + return mem[i][j]; +} + +/* Edit distance: Dynamic programming */ +function editDistanceDP(s, t) { + const n = s.length, + m = t.length; + const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0)); + // State transition: first row and first column + for (let i = 1; i <= n; i++) { + dp[i][0] = i; + } + for (let j = 1; j <= m; j++) { + dp[0][j] = j; + } + // State transition: rest of the rows and columns + for (let i = 1; i <= n; i++) { + for (let j = 1; j <= m; j++) { + if (s.charAt(i - 1) === t.charAt(j - 1)) { + // If two characters are equal, skip both characters + dp[i][j] = dp[i - 1][j - 1]; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[i][j] = + Math.min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1; + } + } + } + return dp[n][m]; +} + +/* Edit distance: Space-optimized dynamic programming */ +function editDistanceDPComp(s, t) { + const n = s.length, + m = t.length; + const dp = new Array(m + 1).fill(0); + // State transition: first row + for (let j = 1; j <= m; j++) { + dp[j] = j; + } + // State transition: rest of the rows + for (let i = 1; i <= n; i++) { + // State transition: first column + let leftup = dp[0]; // Temporarily store dp[i-1, j-1] + dp[0] = i; + // State transition: rest of the columns + for (let j = 1; j <= m; j++) { + const temp = dp[j]; + if (s.charAt(i - 1) === t.charAt(j - 1)) { + // If two characters are equal, skip both characters + dp[j] = leftup; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[j] = Math.min(dp[j - 1], dp[j], leftup) + 1; + } + leftup = temp; // Update for next round's dp[i-1, j-1] + } + } + return dp[m]; +} + +const s = 'bag'; +const t = 'pack'; +const n = s.length, + m = t.length; + +// Brute-force search +let res = editDistanceDFS(s, t, n, m); +console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); + +// Memoization search +const mem = Array.from(new Array(n + 1), () => new Array(m + 1).fill(-1)); +res = editDistanceDFSMem(s, t, mem, n, m); +console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); + +// Dynamic programming +res = editDistanceDP(s, t); +console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); + +// Space-optimized dynamic programming +res = editDistanceDPComp(s, t); +console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); diff --git a/en/codes/javascript/chapter_dynamic_programming/knapsack.js b/en/codes/javascript/chapter_dynamic_programming/knapsack.js new file mode 100644 index 000000000..6544a0349 --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/knapsack.js @@ -0,0 +1,113 @@ +/** + * File: knapsack.js + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* 0-1 knapsack: Brute-force search */ +function knapsackDFS(wgt, val, i, c) { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i === 0 || c === 0) { + return 0; + } + // If exceeds knapsack capacity, can only choose not to put it in + if (wgt[i - 1] > c) { + return knapsackDFS(wgt, val, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + const no = knapsackDFS(wgt, val, i - 1, c); + const yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; + // Return the larger value of the two options + return Math.max(no, yes); +} + +/* 0-1 knapsack: Memoization search */ +function knapsackDFSMem(wgt, val, mem, i, c) { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i === 0 || c === 0) { + return 0; + } + // If there's a record, return it directly + if (mem[i][c] !== -1) { + return mem[i][c]; + } + // If exceeds knapsack capacity, can only choose not to put it in + if (wgt[i - 1] > c) { + return knapsackDFSMem(wgt, val, mem, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + const no = knapsackDFSMem(wgt, val, mem, i - 1, c); + const yes = + knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; + // Record and return the larger value of the two options + mem[i][c] = Math.max(no, yes); + return mem[i][c]; +} + +/* 0-1 knapsack: Dynamic programming */ +function knapsackDP(wgt, val, cap) { + const n = wgt.length; + // Initialize dp table + const dp = Array(n + 1) + .fill(0) + .map(() => Array(cap + 1).fill(0)); + // State transition + for (let i = 1; i <= n; i++) { + for (let c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c]; + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = Math.max( + dp[i - 1][c], + dp[i - 1][c - wgt[i - 1]] + val[i - 1] + ); + } + } + } + return dp[n][cap]; +} + +/* 0-1 knapsack: Space-optimized dynamic programming */ +function knapsackDPComp(wgt, val, cap) { + const n = wgt.length; + // Initialize dp table + const dp = Array(cap + 1).fill(0); + // State transition + for (let i = 1; i <= n; i++) { + // Traverse in reverse order + for (let c = cap; c >= 1; c--) { + if (wgt[i - 1] <= c) { + // The larger value between not selecting and selecting item i + dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; +} + +/* Driver Code */ +const wgt = [10, 20, 30, 40, 50]; +const val = [50, 120, 150, 210, 240]; +const cap = 50; +const n = wgt.length; + +// Brute-force search +let res = knapsackDFS(wgt, val, n, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); + +// Memoization search +const mem = Array.from({ length: n + 1 }, () => + Array.from({ length: cap + 1 }, () => -1) +); +res = knapsackDFSMem(wgt, val, mem, n, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); + +// Dynamic programming +res = knapsackDP(wgt, val, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); + +// Space-optimized dynamic programming +res = knapsackDPComp(wgt, val, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.js b/en/codes/javascript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.js new file mode 100644 index 000000000..69d4bb379 --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.js @@ -0,0 +1,49 @@ +/** + * File: min_cost_climbing_stairs_dp.js + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Minimum cost climbing stairs: Dynamic programming */ +function minCostClimbingStairsDP(cost) { + const n = cost.length - 1; + if (n === 1 || n === 2) { + return cost[n]; + } + // Initialize dp table, used to store solutions to subproblems + const dp = new Array(n + 1); + // Initial state: preset the solution to the smallest subproblem + dp[1] = cost[1]; + dp[2] = cost[2]; + // State transition: gradually solve larger subproblems from smaller ones + for (let i = 3; i <= n; i++) { + dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; + } + return dp[n]; +} + +/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +function minCostClimbingStairsDPComp(cost) { + const n = cost.length - 1; + if (n === 1 || n === 2) { + return cost[n]; + } + let a = cost[1], + b = cost[2]; + for (let i = 3; i <= n; i++) { + const tmp = b; + b = Math.min(a, tmp) + cost[i]; + a = tmp; + } + return b; +} + +/* Driver Code */ +const cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]; +console.log('Input stair cost list is:', cost); + +let res = minCostClimbingStairsDP(cost); +console.log(`Minimum cost to climb stairs is: ${res}`); + +res = minCostClimbingStairsDPComp(cost); +console.log(`Minimum cost to climb stairs is: ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/min_path_sum.js b/en/codes/javascript/chapter_dynamic_programming/min_path_sum.js new file mode 100644 index 000000000..ef6e6a869 --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/min_path_sum.js @@ -0,0 +1,121 @@ +/** + * File: min_path_sum.js + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Minimum path sum: Brute-force search */ +function minPathSumDFS(grid, i, j) { + // If it's the top-left cell, terminate the search + if (i === 0 && j === 0) { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + return Infinity; + } + // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + const up = minPathSumDFS(grid, i - 1, j); + const left = minPathSumDFS(grid, i, j - 1); + // Return the minimum path cost from top-left to (i, j) + return Math.min(left, up) + grid[i][j]; +} + +/* Minimum path sum: Memoization search */ +function minPathSumDFSMem(grid, mem, i, j) { + // If it's the top-left cell, terminate the search + if (i === 0 && j === 0) { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + return Infinity; + } + // If there's a record, return it directly + if (mem[i][j] !== -1) { + return mem[i][j]; + } + // Minimum path cost for left and upper cells + const up = minPathSumDFSMem(grid, mem, i - 1, j); + const left = minPathSumDFSMem(grid, mem, i, j - 1); + // Record and return the minimum path cost from top-left to (i, j) + mem[i][j] = Math.min(left, up) + grid[i][j]; + return mem[i][j]; +} + +/* Minimum path sum: Dynamic programming */ +function minPathSumDP(grid) { + const n = grid.length, + m = grid[0].length; + // Initialize dp table + const dp = Array.from({ length: n }, () => + Array.from({ length: m }, () => 0) + ); + dp[0][0] = grid[0][0]; + // State transition: first row + for (let j = 1; j < m; j++) { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + // State transition: first column + for (let i = 1; i < n; i++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + // State transition: rest of the rows and columns + for (let i = 1; i < n; i++) { + for (let j = 1; j < m; j++) { + dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; + } + } + return dp[n - 1][m - 1]; +} + +/* Minimum path sum: Space-optimized dynamic programming */ +function minPathSumDPComp(grid) { + const n = grid.length, + m = grid[0].length; + // Initialize dp table + const dp = new Array(m); + // State transition: first row + dp[0] = grid[0][0]; + for (let j = 1; j < m; j++) { + dp[j] = dp[j - 1] + grid[0][j]; + } + // State transition: rest of the rows + for (let i = 1; i < n; i++) { + // State transition: first column + dp[0] = dp[0] + grid[i][0]; + // State transition: rest of the columns + for (let j = 1; j < m; j++) { + dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + return dp[m - 1]; +} + +/* Driver Code */ +const grid = [ + [1, 3, 1, 5], + [2, 2, 4, 2], + [5, 3, 2, 1], + [4, 3, 5, 2], +]; +const n = grid.length, + m = grid[0].length; +// Brute-force search +let res = minPathSumDFS(grid, n - 1, m - 1); +console.log(`Minimum path sum from top-left to bottom-right is ${res}`); + +// Memoization search +const mem = Array.from({ length: n }, () => + Array.from({ length: m }, () => -1) +); +res = minPathSumDFSMem(grid, mem, n - 1, m - 1); +console.log(`Minimum path sum from top-left to bottom-right is ${res}`); + +// Dynamic programming +res = minPathSumDP(grid); +console.log(`Minimum path sum from top-left to bottom-right is ${res}`); + +// Space-optimized dynamic programming +res = minPathSumDPComp(grid); +console.log(`Minimum path sum from top-left to bottom-right is ${res}`); diff --git a/en/codes/javascript/chapter_dynamic_programming/unbounded_knapsack.js b/en/codes/javascript/chapter_dynamic_programming/unbounded_knapsack.js new file mode 100644 index 000000000..e3c0b61c9 --- /dev/null +++ b/en/codes/javascript/chapter_dynamic_programming/unbounded_knapsack.js @@ -0,0 +1,63 @@ +/** + * File: unbounded_knapsack.js + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Unbounded knapsack: Dynamic programming */ +function unboundedKnapsackDP(wgt, val, cap) { + const n = wgt.length; + // Initialize dp table + const dp = Array.from({ length: n + 1 }, () => + Array.from({ length: cap + 1 }, () => 0) + ); + // State transition + for (let i = 1; i <= n; i++) { + for (let c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c]; + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = Math.max( + dp[i - 1][c], + dp[i][c - wgt[i - 1]] + val[i - 1] + ); + } + } + } + return dp[n][cap]; +} + +/* Unbounded knapsack: Space-optimized dynamic programming */ +function unboundedKnapsackDPComp(wgt, val, cap) { + const n = wgt.length; + // Initialize dp table + const dp = Array.from({ length: cap + 1 }, () => 0); + // State transition + for (let i = 1; i <= n; i++) { + for (let c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[c] = dp[c]; + } else { + // The larger value between not selecting and selecting item i + dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; +} + +/* Driver Code */ +const wgt = [1, 2, 3]; +const val = [5, 11, 15]; +const cap = 4; + +// Dynamic programming +let res = unboundedKnapsackDP(wgt, val, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); + +// Space-optimized dynamic programming +res = unboundedKnapsackDPComp(wgt, val, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); diff --git a/en/codes/javascript/chapter_graph/graph_adjacency_list.js b/en/codes/javascript/chapter_graph/graph_adjacency_list.js new file mode 100644 index 000000000..fac6f69c4 --- /dev/null +++ b/en/codes/javascript/chapter_graph/graph_adjacency_list.js @@ -0,0 +1,142 @@ +/** + * File: graph_adjacency_list.js + * Created Time: 2023-02-09 + * Author: Justin (xiefahit@gmail.com) + */ + +const { Vertex } = require('../modules/Vertex'); + +/* Undirected graph class based on adjacency list */ +class GraphAdjList { + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + adjList; + + /* Constructor */ + constructor(edges) { + this.adjList = new Map(); + // Add all vertices and edges + for (const edge of edges) { + this.addVertex(edge[0]); + this.addVertex(edge[1]); + this.addEdge(edge[0], edge[1]); + } + } + + /* Get the number of vertices */ + size() { + return this.adjList.size; + } + + /* Add edge */ + addEdge(vet1, vet2) { + if ( + !this.adjList.has(vet1) || + !this.adjList.has(vet2) || + vet1 === vet2 + ) { + throw new Error('Illegal Argument Exception'); + } + // Add edge vet1 - vet2 + this.adjList.get(vet1).push(vet2); + this.adjList.get(vet2).push(vet1); + } + + /* Remove edge */ + removeEdge(vet1, vet2) { + if ( + !this.adjList.has(vet1) || + !this.adjList.has(vet2) || + vet1 === vet2 || + this.adjList.get(vet1).indexOf(vet2) === -1 + ) { + throw new Error('Illegal Argument Exception'); + } + // Remove edge vet1 - vet2 + this.adjList.get(vet1).splice(this.adjList.get(vet1).indexOf(vet2), 1); + this.adjList.get(vet2).splice(this.adjList.get(vet2).indexOf(vet1), 1); + } + + /* Add vertex */ + addVertex(vet) { + if (this.adjList.has(vet)) return; + // Add a new linked list in the adjacency list + this.adjList.set(vet, []); + } + + /* Remove vertex */ + removeVertex(vet) { + if (!this.adjList.has(vet)) { + throw new Error('Illegal Argument Exception'); + } + // Remove the linked list corresponding to vertex vet in the adjacency list + this.adjList.delete(vet); + // Traverse the linked lists of other vertices and remove all edges containing vet + for (const set of this.adjList.values()) { + const index = set.indexOf(vet); + if (index > -1) { + set.splice(index, 1); + } + } + } + + /* Print adjacency list */ + print() { + console.log('Adjacency list ='); + for (const [key, value] of this.adjList) { + const tmp = []; + for (const vertex of value) { + tmp.push(vertex.val); + } + console.log(key.val + ': ' + tmp.join()); + } + } +} + +if (require.main === module) { + /* Driver Code */ + /* Add edge */ + const v0 = new Vertex(1), + v1 = new Vertex(3), + v2 = new Vertex(2), + v3 = new Vertex(5), + v4 = new Vertex(4); + const edges = [ + [v0, v1], + [v1, v2], + [v2, v3], + [v0, v3], + [v2, v4], + [v3, v4], + ]; + const graph = new GraphAdjList(edges); + console.log('\nAfter initialization, graph is'); + graph.print(); + + /* Add edge */ + // Vertices 1, 2 are v0, v2 + graph.addEdge(v0, v2); + console.log('\nAfter adding edge 1-2, graph is'); + graph.print(); + + /* Remove edge */ + // Vertices 1, 3 are v0, v1 + graph.removeEdge(v0, v1); + console.log('\nAfter removing edge 1-3, graph is'); + graph.print(); + + /* Add vertex */ + const v5 = new Vertex(6); + graph.addVertex(v5); + console.log('\nAfter adding vertex 6, graph is'); + graph.print(); + + /* Remove vertex */ + // Vertex 3 is v1 + graph.removeVertex(v1); + console.log('\nAfter removing vertex 3, graph is'); + graph.print(); +} + +module.exports = { + GraphAdjList, +}; diff --git a/en/codes/javascript/chapter_graph/graph_adjacency_matrix.js b/en/codes/javascript/chapter_graph/graph_adjacency_matrix.js new file mode 100644 index 000000000..40565487b --- /dev/null +++ b/en/codes/javascript/chapter_graph/graph_adjacency_matrix.js @@ -0,0 +1,132 @@ +/** + * File: graph_adjacency_matrix.js + * Created Time: 2023-02-09 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +/* Undirected graph class based on adjacency matrix */ +class GraphAdjMat { + vertices; // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + adjMat; // Adjacency matrix, where the row and column indices correspond to the "vertex index" + + /* Constructor */ + constructor(vertices, edges) { + this.vertices = []; + this.adjMat = []; + // Add vertex + for (const val of vertices) { + this.addVertex(val); + } + // Add edge + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + for (const e of edges) { + this.addEdge(e[0], e[1]); + } + } + + /* Get the number of vertices */ + size() { + return this.vertices.length; + } + + /* Add vertex */ + addVertex(val) { + const n = this.size(); + // Add the value of the new vertex to the vertex list + this.vertices.push(val); + // Add a row to the adjacency matrix + const newRow = []; + for (let j = 0; j < n; j++) { + newRow.push(0); + } + this.adjMat.push(newRow); + // Add a column to the adjacency matrix + for (const row of this.adjMat) { + row.push(0); + } + } + + /* Remove vertex */ + removeVertex(index) { + if (index >= this.size()) { + throw new RangeError('Index Out Of Bounds Exception'); + } + // Remove the vertex at index from the vertex list + this.vertices.splice(index, 1); + + // Remove the row at index from the adjacency matrix + this.adjMat.splice(index, 1); + // Remove the column at index from the adjacency matrix + for (const row of this.adjMat) { + row.splice(index, 1); + } + } + + /* Add edge */ + // Parameters i, j correspond to the vertices element indices + addEdge(i, j) { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { + throw new RangeError('Index Out Of Bounds Exception'); + } + // In undirected graph, adjacency matrix is symmetric about main diagonal, i.e., satisfies (i, j) === (j, i) + this.adjMat[i][j] = 1; + this.adjMat[j][i] = 1; + } + + /* Remove edge */ + // Parameters i, j correspond to the vertices element indices + removeEdge(i, j) { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { + throw new RangeError('Index Out Of Bounds Exception'); + } + this.adjMat[i][j] = 0; + this.adjMat[j][i] = 0; + } + + /* Print adjacency matrix */ + print() { + console.log('Vertex list = ', this.vertices); + console.log('Adjacency matrix =', this.adjMat); + } +} + +/* Driver Code */ +/* Add edge */ +// Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices +const vertices = [1, 3, 2, 5, 4]; +const edges = [ + [0, 1], + [1, 2], + [2, 3], + [0, 3], + [2, 4], + [3, 4], +]; +const graph = new GraphAdjMat(vertices, edges); +console.log('\nAfter initialization, graph is'); +graph.print(); + +/* Add edge */ +// Add vertex +graph.addEdge(0, 2); +console.log('\nAfter adding edge 1-2, graph is'); +graph.print(); + +/* Remove edge */ +// Vertices 1, 3 have indices 0, 1 respectively +graph.removeEdge(0, 1); +console.log('\nAfter removing edge 1-3, graph is'); +graph.print(); + +/* Add vertex */ +graph.addVertex(6); +console.log('\nAfter adding vertex 6, graph is'); +graph.print(); + +/* Remove vertex */ +// Vertex 3 has index 1 +graph.removeVertex(1); +console.log('\nAfter removing vertex 3, graph is'); +graph.print(); diff --git a/en/codes/javascript/chapter_graph/graph_bfs.js b/en/codes/javascript/chapter_graph/graph_bfs.js new file mode 100644 index 000000000..6330f571f --- /dev/null +++ b/en/codes/javascript/chapter_graph/graph_bfs.js @@ -0,0 +1,61 @@ +/** + * File: graph_bfs.js + * Created Time: 2023-02-21 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +const { GraphAdjList } = require('./graph_adjacency_list'); +const { Vertex } = require('../modules/Vertex'); + +/* Breadth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +function graphBFS(graph, startVet) { + // Vertex traversal sequence + const res = []; + // Hash set for recording vertices that have been visited + const visited = new Set(); + visited.add(startVet); + // Queue used to implement BFS + const que = [startVet]; + // Starting from vertex vet, loop until all vertices are visited + while (que.length) { + const vet = que.shift(); // Dequeue the front vertex + res.push(vet); // Record visited vertex + // Traverse all adjacent vertices of this vertex + for (const adjVet of graph.adjList.get(vet) ?? []) { + if (visited.has(adjVet)) { + continue; // Skip vertices that have been visited + } + que.push(adjVet); // Only enqueue unvisited vertices + visited.add(adjVet); // Mark this vertex as visited + } + } + // Return vertex traversal sequence + return res; +} + +/* Driver Code */ +/* Add edge */ +const v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); +const edges = [ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[1], v[4]], + [v[2], v[5]], + [v[3], v[4]], + [v[3], v[6]], + [v[4], v[5]], + [v[4], v[7]], + [v[5], v[8]], + [v[6], v[7]], + [v[7], v[8]], +]; +const graph = new GraphAdjList(edges); +console.log('\nAfter initialization, graph is'); +graph.print(); + +/* Breadth-first traversal */ +const res = graphBFS(graph, v[0]); +console.log('\nBreadth-first traversal (BFS) vertex sequence is'); +console.log(Vertex.vetsToVals(res)); diff --git a/en/codes/javascript/chapter_graph/graph_dfs.js b/en/codes/javascript/chapter_graph/graph_dfs.js new file mode 100644 index 000000000..70529e826 --- /dev/null +++ b/en/codes/javascript/chapter_graph/graph_dfs.js @@ -0,0 +1,54 @@ +/** + * File: graph_dfs.js + * Created Time: 2023-02-21 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +const { Vertex } = require('../modules/Vertex'); +const { GraphAdjList } = require('./graph_adjacency_list'); + +/* Depth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +function dfs(graph, visited, res, vet) { + res.push(vet); // Record visited vertex + visited.add(vet); // Mark this vertex as visited + // Traverse all adjacent vertices of this vertex + for (const adjVet of graph.adjList.get(vet)) { + if (visited.has(adjVet)) { + continue; // Skip vertices that have been visited + } + // Recursively visit adjacent vertices + dfs(graph, visited, res, adjVet); + } +} + +/* Depth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +function graphDFS(graph, startVet) { + // Vertex traversal sequence + const res = []; + // Hash set for recording vertices that have been visited + const visited = new Set(); + dfs(graph, visited, res, startVet); + return res; +} + +/* Driver Code */ +/* Add edge */ +const v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6]); +const edges = [ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[2], v[5]], + [v[4], v[5]], + [v[5], v[6]], +]; +const graph = new GraphAdjList(edges); +console.log('\nAfter initialization, graph is'); +graph.print(); + +/* Depth-first traversal */ +const res = graphDFS(graph, v[0]); +console.log('\nDepth-first traversal (DFS) vertex sequence is'); +console.log(Vertex.vetsToVals(res)); diff --git a/en/codes/javascript/chapter_greedy/coin_change_greedy.js b/en/codes/javascript/chapter_greedy/coin_change_greedy.js new file mode 100644 index 000000000..7a2e7d6b8 --- /dev/null +++ b/en/codes/javascript/chapter_greedy/coin_change_greedy.js @@ -0,0 +1,48 @@ +/** + * File: coin_change_greedy.js + * Created Time: 2023-09-02 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Coin change: Greedy algorithm */ +function coinChangeGreedy(coins, amt) { + // Assume coins array is sorted + let i = coins.length - 1; + let count = 0; + // Loop to make greedy choices until no remaining amount + while (amt > 0) { + // Find the coin that is less than and closest to the remaining amount + while (i > 0 && coins[i] > amt) { + i--; + } + // Choose coins[i] + amt -= coins[i]; + count++; + } + // If no feasible solution is found, return -1 + return amt === 0 ? count : -1; +} + +/* Driver Code */ +// Greedy algorithm: Can guarantee finding the global optimal solution +let coins = [1, 5, 10, 20, 50, 100]; +let amt = 186; +let res = coinChangeGreedy(coins, amt); +console.log(`\ncoins = ${coins}, amt = ${amt}`); +console.log(`Minimum coins needed to make ${amt} is ${res}`); + +// Greedy algorithm: Cannot guarantee finding the global optimal solution +coins = [1, 20, 50]; +amt = 60; +res = coinChangeGreedy(coins, amt); +console.log(`\ncoins = ${coins}, amt = ${amt}`); +console.log(`Minimum coins needed to make ${amt} is ${res}`); +console.log('Actually the minimum number needed is 3, i.e., 20 + 20 + 20'); + +// Greedy algorithm: Cannot guarantee finding the global optimal solution +coins = [1, 49, 50]; +amt = 98; +res = coinChangeGreedy(coins, amt); +console.log(`\ncoins = ${coins}, amt = ${amt}`); +console.log(`Minimum coins needed to make ${amt} is ${res}`); +console.log('Actually the minimum number needed is 2, i.e., 49 + 49'); diff --git a/en/codes/javascript/chapter_greedy/fractional_knapsack.js b/en/codes/javascript/chapter_greedy/fractional_knapsack.js new file mode 100644 index 000000000..2e794d2ff --- /dev/null +++ b/en/codes/javascript/chapter_greedy/fractional_knapsack.js @@ -0,0 +1,46 @@ +/** + * File: fractional_knapsack.js + * Created Time: 2023-09-02 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Item */ +class Item { + constructor(w, v) { + this.w = w; // Item weight + this.v = v; // Item value + } +} + +/* Fractional knapsack: Greedy algorithm */ +function fractionalKnapsack(wgt, val, cap) { + // Create item list with two attributes: weight, value + const items = wgt.map((w, i) => new Item(w, val[i])); + // Sort by unit value item.v / item.w from high to low + items.sort((a, b) => b.v / b.w - a.v / a.w); + // Loop for greedy selection + let res = 0; + for (const item of items) { + if (item.w <= cap) { + // If remaining capacity is sufficient, put the entire current item into the knapsack + res += item.v; + cap -= item.w; + } else { + // If remaining capacity is insufficient, put part of the current item into the knapsack + res += (item.v / item.w) * cap; + // No remaining capacity, so break out of the loop + break; + } + } + return res; +} + +/* Driver Code */ +const wgt = [10, 20, 30, 40, 50]; +const val = [50, 120, 150, 210, 240]; +const cap = 50; +const n = wgt.length; + +// Greedy algorithm +const res = fractionalKnapsack(wgt, val, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); diff --git a/en/codes/javascript/chapter_greedy/max_capacity.js b/en/codes/javascript/chapter_greedy/max_capacity.js new file mode 100644 index 000000000..0982e2d65 --- /dev/null +++ b/en/codes/javascript/chapter_greedy/max_capacity.js @@ -0,0 +1,34 @@ +/** + * File: max_capacity.js + * Created Time: 2023-09-02 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Max capacity: Greedy algorithm */ +function maxCapacity(ht) { + // Initialize i, j to be at both ends of the array + let i = 0, + j = ht.length - 1; + // Initial max capacity is 0 + let res = 0; + // Loop for greedy selection until the two boards meet + while (i < j) { + // Update max capacity + const cap = Math.min(ht[i], ht[j]) * (j - i); + res = Math.max(res, cap); + // Move the shorter board inward + if (ht[i] < ht[j]) { + i += 1; + } else { + j -= 1; + } + } + return res; +} + +/* Driver Code */ +const ht = [3, 8, 5, 2, 7, 7, 3, 4]; + +// Greedy algorithm +const res = maxCapacity(ht); +console.log(`Maximum capacity is ${res}`); diff --git a/en/codes/javascript/chapter_greedy/max_product_cutting.js b/en/codes/javascript/chapter_greedy/max_product_cutting.js new file mode 100644 index 000000000..c653d55b9 --- /dev/null +++ b/en/codes/javascript/chapter_greedy/max_product_cutting.js @@ -0,0 +1,33 @@ +/** + * File: max_product_cutting.js + * Created Time: 2023-09-02 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Max product cutting: Greedy algorithm */ +function maxProductCutting(n) { + // When n <= 3, must cut out a 1 + if (n <= 3) { + return 1 * (n - 1); + } + // Greedily cut out 3, a is the number of 3s, b is the remainder + let a = Math.floor(n / 3); + let b = n % 3; + if (b === 1) { + // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + return Math.pow(3, a - 1) * 2 * 2; + } + if (b === 2) { + // When the remainder is 2, do nothing + return Math.pow(3, a) * 2; + } + // When the remainder is 0, do nothing + return Math.pow(3, a); +} + +/* Driver Code */ +let n = 58; + +// Greedy algorithm +let res = maxProductCutting(n); +console.log(`Maximum cutting product is ${res}`); diff --git a/en/codes/javascript/chapter_hashing/array_hash_map.js b/en/codes/javascript/chapter_hashing/array_hash_map.js new file mode 100644 index 000000000..a39939b65 --- /dev/null +++ b/en/codes/javascript/chapter_hashing/array_hash_map.js @@ -0,0 +1,128 @@ +/** + * File: array_hash_map.js + * Created Time: 2022-12-26 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Key-value pair Number -> String */ +class Pair { + constructor(key, val) { + this.key = key; + this.val = val; + } +} + +/* Hash table based on array implementation */ +class ArrayHashMap { + #buckets; + constructor() { + // Initialize array with 100 buckets + this.#buckets = new Array(100).fill(null); + } + + /* Hash function */ + #hashFunc(key) { + return key % 100; + } + + /* Query operation */ + get(key) { + let index = this.#hashFunc(key); + let pair = this.#buckets[index]; + if (pair === null) return null; + return pair.val; + } + + /* Add operation */ + set(key, val) { + let index = this.#hashFunc(key); + this.#buckets[index] = new Pair(key, val); + } + + /* Remove operation */ + delete(key) { + let index = this.#hashFunc(key); + // Set to null to represent deletion + this.#buckets[index] = null; + } + + /* Get all key-value pairs */ + entries() { + let arr = []; + for (let i = 0; i < this.#buckets.length; i++) { + if (this.#buckets[i]) { + arr.push(this.#buckets[i]); + } + } + return arr; + } + + /* Get all keys */ + keys() { + let arr = []; + for (let i = 0; i < this.#buckets.length; i++) { + if (this.#buckets[i]) { + arr.push(this.#buckets[i].key); + } + } + return arr; + } + + /* Get all values */ + values() { + let arr = []; + for (let i = 0; i < this.#buckets.length; i++) { + if (this.#buckets[i]) { + arr.push(this.#buckets[i].val); + } + } + return arr; + } + + /* Print hash table */ + print() { + let pairSet = this.entries(); + for (const pair of pairSet) { + console.info(`${pair.key} -> ${pair.val}`); + } + } +} + +/* Driver Code */ +/* Initialize hash table */ +const map = new ArrayHashMap(); +/* Add operation */ +// Add key-value pair (key, value) to the hash table +map.set(12836, 'Xiao Ha'); +map.set(15937, 'Xiao Luo'); +map.set(16750, 'Xiao Suan'); +map.set(13276, 'Xiao Fa'); +map.set(10583, 'Xiao Ya'); +console.info('\nAfter adding is complete, hash table is\nKey -> Value'); +map.print(); + +/* Query operation */ +// Input key into hash table to get value +let name = map.get(15937); +console.info('\nInput student ID 15937, query name ' + name); + +/* Remove operation */ +// Remove key-value pair (key, value) from hash table +map.delete(10583); +console.info('\nAfter removing 10583, hash table is\nKey -> Value'); +map.print(); + +/* Traverse hash table */ +console.info('\nTraverse key-value pairs Key->Value'); +for (const pair of map.entries()) { + if (!pair) continue; + console.info(pair.key + ' -> ' + pair.val); +} +console.info('\nTraverse keys only Key'); +for (const key of map.keys()) { + console.info(key); +} +console.info('\nTraverse values only Value'); +for (const val of map.values()) { + console.info(val); +} diff --git a/en/codes/javascript/chapter_hashing/hash_map.js b/en/codes/javascript/chapter_hashing/hash_map.js new file mode 100644 index 000000000..4933acb3d --- /dev/null +++ b/en/codes/javascript/chapter_hashing/hash_map.js @@ -0,0 +1,44 @@ +/** + * File: hash_map.js + * Created Time: 2022-12-26 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Driver Code */ +/* Initialize hash table */ +const map = new Map(); + +/* Add operation */ +// Add key-value pair (key, value) to the hash table +map.set(12836, 'Xiao Ha'); +map.set(15937, 'Xiao Luo'); +map.set(16750, 'Xiao Suan'); +map.set(13276, 'Xiao Fa'); +map.set(10583, 'Xiao Ya'); +console.info('\nAfter adding is complete, hash table is\nKey -> Value'); +console.info(map); + +/* Query operation */ +// Input key into hash table to get value +let name = map.get(15937); +console.info('\nInput student ID 15937, query name ' + name); + +/* Remove operation */ +// Remove key-value pair (key, value) from hash table +map.delete(10583); +console.info('\nAfter removing 10583, hash table is\nKey -> Value'); +console.info(map); + +/* Traverse hash table */ +console.info('\nTraverse key-value pairs Key->Value'); +for (const [k, v] of map.entries()) { + console.info(k + ' -> ' + v); +} +console.info('\nTraverse keys only Key'); +for (const k of map.keys()) { + console.info(k); +} +console.info('\nTraverse values only Value'); +for (const v of map.values()) { + console.info(v); +} diff --git a/en/codes/javascript/chapter_hashing/hash_map_chaining.js b/en/codes/javascript/chapter_hashing/hash_map_chaining.js new file mode 100644 index 000000000..f4e092cbc --- /dev/null +++ b/en/codes/javascript/chapter_hashing/hash_map_chaining.js @@ -0,0 +1,142 @@ +/** + * File: hash_map_chaining.js + * Created Time: 2023-08-06 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Key-value pair Number -> String */ +class Pair { + constructor(key, val) { + this.key = key; + this.val = val; + } +} + +/* Hash table with separate chaining */ +class HashMapChaining { + #size; // Number of key-value pairs + #capacity; // Hash table capacity + #loadThres; // Load factor threshold for triggering expansion + #extendRatio; // Expansion multiplier + #buckets; // Bucket array + + /* Constructor */ + constructor() { + this.#size = 0; + this.#capacity = 4; + this.#loadThres = 2.0 / 3.0; + this.#extendRatio = 2; + this.#buckets = new Array(this.#capacity).fill(null).map((x) => []); + } + + /* Hash function */ + #hashFunc(key) { + return key % this.#capacity; + } + + /* Load factor */ + #loadFactor() { + return this.#size / this.#capacity; + } + + /* Query operation */ + get(key) { + const index = this.#hashFunc(key); + const bucket = this.#buckets[index]; + // Traverse bucket, if key is found, return corresponding val + for (const pair of bucket) { + if (pair.key === key) { + return pair.val; + } + } + // If key is not found, return null + return null; + } + + /* Add operation */ + put(key, val) { + // When load factor exceeds threshold, perform expansion + if (this.#loadFactor() > this.#loadThres) { + this.#extend(); + } + const index = this.#hashFunc(key); + const bucket = this.#buckets[index]; + // Traverse bucket, if specified key is encountered, update corresponding val and return + for (const pair of bucket) { + if (pair.key === key) { + pair.val = val; + return; + } + } + // If key does not exist, append key-value pair to the end + const pair = new Pair(key, val); + bucket.push(pair); + this.#size++; + } + + /* Remove operation */ + remove(key) { + const index = this.#hashFunc(key); + let bucket = this.#buckets[index]; + // Traverse bucket and remove key-value pair from it + for (let i = 0; i < bucket.length; i++) { + if (bucket[i].key === key) { + bucket.splice(i, 1); + this.#size--; + break; + } + } + } + + /* Expand hash table */ + #extend() { + // Temporarily store the original hash table + const bucketsTmp = this.#buckets; + // Initialize expanded new hash table + this.#capacity *= this.#extendRatio; + this.#buckets = new Array(this.#capacity).fill(null).map((x) => []); + this.#size = 0; + // Move key-value pairs from original hash table to new hash table + for (const bucket of bucketsTmp) { + for (const pair of bucket) { + this.put(pair.key, pair.val); + } + } + } + + /* Print hash table */ + print() { + for (const bucket of this.#buckets) { + let res = []; + for (const pair of bucket) { + res.push(pair.key + ' -> ' + pair.val); + } + console.log(res); + } + } +} + +/* Driver Code */ +/* Initialize hash table */ +const map = new HashMapChaining(); + +/* Add operation */ +// Add key-value pair (key, value) to the hash table +map.put(12836, 'Xiao Ha'); +map.put(15937, 'Xiao Luo'); +map.put(16750, 'Xiao Suan'); +map.put(13276, 'Xiao Fa'); +map.put(10583, 'Xiao Ya'); +console.log('\nAfter adding is complete, hash table is\nKey -> Value'); +map.print(); + +/* Query operation */ +// Input key into hash table to get value +const name = map.get(13276); +console.log('\nInput student ID 13276, query name ' + name); + +/* Remove operation */ +// Remove key-value pair (key, value) from hash table +map.remove(12836); +console.log('\nAfter removing 12836, hash table is\nKey -> Value'); +map.print(); diff --git a/en/codes/javascript/chapter_hashing/hash_map_open_addressing.js b/en/codes/javascript/chapter_hashing/hash_map_open_addressing.js new file mode 100644 index 000000000..44cf433e5 --- /dev/null +++ b/en/codes/javascript/chapter_hashing/hash_map_open_addressing.js @@ -0,0 +1,177 @@ +/** + * File: hashMapOpenAddressing.js + * Created Time: 2023-06-13 + * Author: yuan0221 (yl1452491917@gmail.com), krahets (krahets@163.com) + */ + +/* Key-value pair Number -> String */ +class Pair { + constructor(key, val) { + this.key = key; + this.val = val; + } +} + +/* Hash table with open addressing */ +class HashMapOpenAddressing { + #size; // Number of key-value pairs + #capacity; // Hash table capacity + #loadThres; // Load factor threshold for triggering expansion + #extendRatio; // Expansion multiplier + #buckets; // Bucket array + #TOMBSTONE; // Removal marker + + /* Constructor */ + constructor() { + this.#size = 0; // Number of key-value pairs + this.#capacity = 4; // Hash table capacity + this.#loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion + this.#extendRatio = 2; // Expansion multiplier + this.#buckets = Array(this.#capacity).fill(null); // Bucket array + this.#TOMBSTONE = new Pair(-1, '-1'); // Removal marker + } + + /* Hash function */ + #hashFunc(key) { + return key % this.#capacity; + } + + /* Load factor */ + #loadFactor() { + return this.#size / this.#capacity; + } + + /* Search for bucket index corresponding to key */ + #findBucket(key) { + let index = this.#hashFunc(key); + let firstTombstone = -1; + // Linear probing, break when encountering an empty bucket + while (this.#buckets[index] !== null) { + // If key is encountered, return the corresponding bucket index + if (this.#buckets[index].key === key) { + // If a removal marker was encountered before, move the key-value pair to that index + if (firstTombstone !== -1) { + this.#buckets[firstTombstone] = this.#buckets[index]; + this.#buckets[index] = this.#TOMBSTONE; + return firstTombstone; // Return the moved bucket index + } + return index; // Return bucket index + } + // Record the first removal marker encountered + if ( + firstTombstone === -1 && + this.#buckets[index] === this.#TOMBSTONE + ) { + firstTombstone = index; + } + // Calculate bucket index, wrap around to the head if past the tail + index = (index + 1) % this.#capacity; + } + // If key does not exist, return the index for insertion + return firstTombstone === -1 ? index : firstTombstone; + } + + /* Query operation */ + get(key) { + // Search for bucket index corresponding to key + const index = this.#findBucket(key); + // If key-value pair is found, return corresponding val + if ( + this.#buckets[index] !== null && + this.#buckets[index] !== this.#TOMBSTONE + ) { + return this.#buckets[index].val; + } + // If key-value pair does not exist, return null + return null; + } + + /* Add operation */ + put(key, val) { + // When load factor exceeds threshold, perform expansion + if (this.#loadFactor() > this.#loadThres) { + this.#extend(); + } + // Search for bucket index corresponding to key + const index = this.#findBucket(key); + // If key-value pair is found, overwrite val and return + if ( + this.#buckets[index] !== null && + this.#buckets[index] !== this.#TOMBSTONE + ) { + this.#buckets[index].val = val; + return; + } + // If key-value pair does not exist, add the key-value pair + this.#buckets[index] = new Pair(key, val); + this.#size++; + } + + /* Remove operation */ + remove(key) { + // Search for bucket index corresponding to key + const index = this.#findBucket(key); + // If key-value pair is found, overwrite it with removal marker + if ( + this.#buckets[index] !== null && + this.#buckets[index] !== this.#TOMBSTONE + ) { + this.#buckets[index] = this.#TOMBSTONE; + this.#size--; + } + } + + /* Expand hash table */ + #extend() { + // Temporarily store the original hash table + const bucketsTmp = this.#buckets; + // Initialize expanded new hash table + this.#capacity *= this.#extendRatio; + this.#buckets = Array(this.#capacity).fill(null); + this.#size = 0; + // Move key-value pairs from original hash table to new hash table + for (const pair of bucketsTmp) { + if (pair !== null && pair !== this.#TOMBSTONE) { + this.put(pair.key, pair.val); + } + } + } + + /* Print hash table */ + print() { + for (const pair of this.#buckets) { + if (pair === null) { + console.log('null'); + } else if (pair === this.#TOMBSTONE) { + console.log('TOMBSTONE'); + } else { + console.log(pair.key + ' -> ' + pair.val); + } + } + } +} + +/* Driver Code */ +// Initialize hash table +const hashmap = new HashMapOpenAddressing(); + +// Add operation +// Add key-value pair (key, val) to the hash table +hashmap.put(12836, 'Xiao Ha'); +hashmap.put(15937, 'Xiao Luo'); +hashmap.put(16750, 'Xiao Suan'); +hashmap.put(13276, 'Xiao Fa'); +hashmap.put(10583, 'Xiao Ya'); +console.log('\nAfter adding is complete, hash table is\nKey -> Value'); +hashmap.print(); + +// Query operation +// Input key into hash table to get value val +const name = hashmap.get(13276); +console.log('\nInput student ID 13276, query name ' + name); + +// Remove operation +// Remove key-value pair (key, val) from hash table +hashmap.remove(16750); +console.log('\nAfter removing 16750, hash table is\nKey -> Value'); +hashmap.print(); diff --git a/en/codes/javascript/chapter_hashing/simple_hash.js b/en/codes/javascript/chapter_hashing/simple_hash.js new file mode 100644 index 000000000..9fc68b4fc --- /dev/null +++ b/en/codes/javascript/chapter_hashing/simple_hash.js @@ -0,0 +1,60 @@ +/** + * File: simple_hash.js + * Created Time: 2023-08-06 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Additive hash */ +function addHash(key) { + let hash = 0; + const MODULUS = 1000000007; + for (const c of key) { + hash = (hash + c.charCodeAt(0)) % MODULUS; + } + return hash; +} + +/* Multiplicative hash */ +function mulHash(key) { + let hash = 0; + const MODULUS = 1000000007; + for (const c of key) { + hash = (31 * hash + c.charCodeAt(0)) % MODULUS; + } + return hash; +} + +/* XOR hash */ +function xorHash(key) { + let hash = 0; + const MODULUS = 1000000007; + for (const c of key) { + hash ^= c.charCodeAt(0); + } + return hash % MODULUS; +} + +/* Rotational hash */ +function rotHash(key) { + let hash = 0; + const MODULUS = 1000000007; + for (const c of key) { + hash = ((hash << 4) ^ (hash >> 28) ^ c.charCodeAt(0)) % MODULUS; + } + return hash; +} + +/* Driver Code */ +const key = 'Hello Algo'; + +let hash = addHash(key); +console.log('Additive hash value is ' + hash); + +hash = mulHash(key); +console.log('Multiplicative hash value is ' + hash); + +hash = xorHash(key); +console.log('XOR hash value is ' + hash); + +hash = rotHash(key); +console.log('Rotational hash value is ' + hash); diff --git a/en/codes/javascript/chapter_heap/my_heap.js b/en/codes/javascript/chapter_heap/my_heap.js new file mode 100644 index 000000000..d5c6f0ea6 --- /dev/null +++ b/en/codes/javascript/chapter_heap/my_heap.js @@ -0,0 +1,158 @@ +/** + * File: my_heap.js + * Created Time: 2023-02-06 + * Author: what-is-me (whatisme@outlook.jp) + */ + +const { printHeap } = require('../modules/PrintUtil'); + +/* Max heap class */ +class MaxHeap { + #maxHeap; + + /* Constructor, build empty heap or build heap from input list */ + constructor(nums) { + // Add list elements to heap as is + this.#maxHeap = nums === undefined ? [] : [...nums]; + // Heapify all nodes except leaf nodes + for (let i = this.#parent(this.size() - 1); i >= 0; i--) { + this.#siftDown(i); + } + } + + /* Get index of left child node */ + #left(i) { + return 2 * i + 1; + } + + /* Get index of right child node */ + #right(i) { + return 2 * i + 2; + } + + /* Get index of parent node */ + #parent(i) { + return Math.floor((i - 1) / 2); // Floor division + } + + /* Swap elements */ + #swap(i, j) { + const tmp = this.#maxHeap[i]; + this.#maxHeap[i] = this.#maxHeap[j]; + this.#maxHeap[j] = tmp; + } + + /* Get heap size */ + size() { + return this.#maxHeap.length; + } + + /* Check if heap is empty */ + isEmpty() { + return this.size() === 0; + } + + /* Access top element */ + peek() { + return this.#maxHeap[0]; + } + + /* Element enters heap */ + push(val) { + // Add node + this.#maxHeap.push(val); + // Heapify from bottom to top + this.#siftUp(this.size() - 1); + } + + /* Starting from node i, heapify from bottom to top */ + #siftUp(i) { + while (true) { + // Get parent node of node i + const p = this.#parent(i); + // When "crossing root node" or "node needs no repair", end heapify + if (p < 0 || this.#maxHeap[i] <= this.#maxHeap[p]) break; + // Swap two nodes + this.#swap(i, p); + // Loop upward heapify + i = p; + } + } + + /* Element exits heap */ + pop() { + // Handle empty case + if (this.isEmpty()) throw new Error('Heap is empty'); + // Delete node + this.#swap(0, this.size() - 1); + // Remove node + const val = this.#maxHeap.pop(); + // Return top element + this.#siftDown(0); + // Return heap top element + return val; + } + + /* Starting from node i, heapify from top to bottom */ + #siftDown(i) { + while (true) { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + const l = this.#left(i), + r = this.#right(i); + let ma = i; + if (l < this.size() && this.#maxHeap[l] > this.#maxHeap[ma]) ma = l; + if (r < this.size() && this.#maxHeap[r] > this.#maxHeap[ma]) ma = r; + // Swap two nodes + if (ma === i) break; + // Swap two nodes + this.#swap(i, ma); + // Loop downwards heapification + i = ma; + } + } + + /* Driver Code */ + print() { + printHeap(this.#maxHeap); + } + + /* Extract elements from heap */ + getMaxHeap() { + return this.#maxHeap; + } +} + +/* Driver Code */ +if (require.main === module) { + /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + const maxHeap = new MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); + console.log('\nAfter inputting list and building heap'); + maxHeap.print(); + + /* Check if heap is empty */ + let peek = maxHeap.peek(); + console.log(`\nHeap top element is ${peek}`); + + /* Element enters heap */ + let val = 7; + maxHeap.push(val); + console.log(`\nAfter element ${val} pushes to heap`); + maxHeap.print(); + + /* Time complexity is O(n), not O(nlogn) */ + peek = maxHeap.pop(); + console.log(`\nAfter heap top element ${peek} pops from heap`); + maxHeap.print(); + + /* Get heap size */ + let size = maxHeap.size(); + console.log(`\nHeap size is ${size}`); + + /* Check if heap is empty */ + let isEmpty = maxHeap.isEmpty(); + console.log(`\nIs heap empty ${isEmpty}`); +} + +module.exports = { + MaxHeap, +}; diff --git a/en/codes/javascript/chapter_heap/top_k.js b/en/codes/javascript/chapter_heap/top_k.js new file mode 100644 index 000000000..f34e5c2ee --- /dev/null +++ b/en/codes/javascript/chapter_heap/top_k.js @@ -0,0 +1,58 @@ +/** + * File: top_k.js + * Created Time: 2023-08-13 + * Author: Justin (xiefahit@gmail.com) + */ + +const { MaxHeap } = require('./my_heap'); + +/* Element enters heap */ +function pushMinHeap(maxHeap, val) { + // Negate element + maxHeap.push(-val); +} + +/* Element exits heap */ +function popMinHeap(maxHeap) { + // Negate element + return -maxHeap.pop(); +} + +/* Access top element */ +function peekMinHeap(maxHeap) { + // Negate element + return -maxHeap.peek(); +} + +/* Extract elements from heap */ +function getMinHeap(maxHeap) { + // Negate element + return maxHeap.getMaxHeap().map((num) => -num); +} + +/* Find the largest k elements in array based on heap */ +function topKHeap(nums, k) { + // Python's heapq module implements min heap by default + // Note: We negate all heap elements to simulate min heap using max heap + const maxHeap = new MaxHeap([]); + // Enter the first k elements of array into heap + for (let i = 0; i < k; i++) { + pushMinHeap(maxHeap, nums[i]); + } + // Starting from the (k+1)th element, maintain heap length as k + for (let i = k; i < nums.length; i++) { + // If current element is greater than top element, top element exits heap, current element enters heap + if (nums[i] > peekMinHeap(maxHeap)) { + popMinHeap(maxHeap); + pushMinHeap(maxHeap, nums[i]); + } + } + // Return elements in heap + return getMinHeap(maxHeap); +} + +/* Driver Code */ +const nums = [1, 7, 6, 3, 2]; +const k = 3; +const res = topKHeap(nums, k); +console.log(`The largest ${k} elements are`, res); diff --git a/en/codes/javascript/chapter_searching/binary_search.js b/en/codes/javascript/chapter_searching/binary_search.js new file mode 100644 index 000000000..285245407 --- /dev/null +++ b/en/codes/javascript/chapter_searching/binary_search.js @@ -0,0 +1,60 @@ +/** + * File: binary_search.js + * Created Time: 2022-12-22 + * Author: JoseHung (szhong@link.cuhk.edu.hk) + */ + +/* Binary search (closed interval on both sides) */ +function binarySearch(nums, target) { + // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + let i = 0, + j = nums.length - 1; + // Loop, exit when the search interval is empty (empty when i > j) + while (i <= j) { + // Calculate midpoint index m, use parseInt() to round down + const m = parseInt(i + (j - i) / 2); + if (nums[m] < target) + // This means target is in the interval [m+1, j] + i = m + 1; + else if (nums[m] > target) + // This means target is in the interval [i, m-1] + j = m - 1; + else return m; // Found the target element, return its index + } + // Target element not found, return -1 + return -1; +} + +/* Binary search (left-closed right-open interval) */ +function binarySearchLCRO(nums, target) { + // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + let i = 0, + j = nums.length; + // Loop, exit when the search interval is empty (empty when i = j) + while (i < j) { + // Calculate midpoint index m, use parseInt() to round down + const m = parseInt(i + (j - i) / 2); + if (nums[m] < target) + // This means target is in the interval [m+1, j) + i = m + 1; + else if (nums[m] > target) + // This means target is in the interval [i, m) + j = m; + // Found the target element, return its index + else return m; + } + // Target element not found, return -1 + return -1; +} + +/* Driver Code */ +const target = 6; +const nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; + +/* Binary search (closed interval on both sides) */ +let index = binarySearch(nums, target); +console.log('Index of target element 6 = ' + index); + +/* Binary search (left-closed right-open interval) */ +index = binarySearchLCRO(nums, target); +console.log('Index of target element 6 = ' + index); diff --git a/en/codes/javascript/chapter_searching/binary_search_edge.js b/en/codes/javascript/chapter_searching/binary_search_edge.js new file mode 100644 index 000000000..2a9886c81 --- /dev/null +++ b/en/codes/javascript/chapter_searching/binary_search_edge.js @@ -0,0 +1,45 @@ +/** + * File: binary_search_edge.js + * Created Time: 2023-08-22 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +const { binarySearchInsertion } = require('./binary_search_insertion.js'); + +/* Binary search for the leftmost target */ +function binarySearchLeftEdge(nums, target) { + // Equivalent to finding the insertion point of target + const i = binarySearchInsertion(nums, target); + // Target not found, return -1 + if (i === nums.length || nums[i] !== target) { + return -1; + } + // Found target, return index i + return i; +} + +/* Binary search for the rightmost target */ +function binarySearchRightEdge(nums, target) { + // Convert to finding the leftmost target + 1 + const i = binarySearchInsertion(nums, target + 1); + // j points to the rightmost target, i points to the first element greater than target + const j = i - 1; + // Target not found, return -1 + if (j === -1 || nums[j] !== target) { + return -1; + } + // Found target, return index j + return j; +} + +/* Driver Code */ +// Array with duplicate elements +const nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; +console.log('\nArray nums = ' + nums); +// Binary search left and right boundaries +for (const target of [6, 7]) { + let index = binarySearchLeftEdge(nums, target); + console.log('Leftmost element ' + target + ' has index ' + index); + index = binarySearchRightEdge(nums, target); + console.log('Rightmost element ' + target + ' has index ' + index); +} diff --git a/en/codes/javascript/chapter_searching/binary_search_insertion.js b/en/codes/javascript/chapter_searching/binary_search_insertion.js new file mode 100644 index 000000000..a43d3a897 --- /dev/null +++ b/en/codes/javascript/chapter_searching/binary_search_insertion.js @@ -0,0 +1,64 @@ +/** + * File: binary_search_insertion.js + * Created Time: 2023-08-22 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Binary search for insertion point (no duplicate elements) */ +function binarySearchInsertionSimple(nums, target) { + let i = 0, + j = nums.length - 1; // Initialize closed interval [0, n-1] + while (i <= j) { + const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, use Math.floor() to round down + if (nums[m] < target) { + i = m + 1; // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // target is in the interval [i, m-1] + } else { + return m; // Found target, return insertion point m + } + } + // Target not found, return insertion point i + return i; +} + +/* Binary search for insertion point (with duplicate elements) */ +function binarySearchInsertion(nums, target) { + let i = 0, + j = nums.length - 1; // Initialize closed interval [0, n-1] + while (i <= j) { + const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, use Math.floor() to round down + if (nums[m] < target) { + i = m + 1; // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // target is in the interval [i, m-1] + } else { + j = m - 1; // The first element less than target is in the interval [i, m-1] + } + } + // Return insertion point i + return i; +} + +/* Driver Code */ +// Array without duplicate elements +let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; +console.log('\nArray nums = ' + nums); +// Binary search for insertion point +for (const target of [6, 9]) { + const index = binarySearchInsertionSimple(nums, target); + console.log('Element ' + target + ''s insertion point index is ' + index); +} + +// Array with duplicate elements +nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; +console.log('\nArray nums = ' + nums); +// Binary search for insertion point +for (const target of [2, 6, 20]) { + const index = binarySearchInsertion(nums, target); + console.log('Element ' + target + ''s insertion point index is ' + index); +} + +module.exports = { + binarySearchInsertion, +}; diff --git a/en/codes/javascript/chapter_searching/hashing_search.js b/en/codes/javascript/chapter_searching/hashing_search.js new file mode 100644 index 000000000..1d02aca98 --- /dev/null +++ b/en/codes/javascript/chapter_searching/hashing_search.js @@ -0,0 +1,45 @@ +/** + * File: hashing_search.js + * Created Time: 2022-12-29 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +const { arrToLinkedList } = require('../modules/ListNode'); + +/* Hash search (array) */ +function hashingSearchArray(map, target) { + // Hash table's key: target element, value: index + // If this key does not exist in the hash table, return -1 + return map.has(target) ? map.get(target) : -1; +} + +/* Hash search (linked list) */ +function hashingSearchLinkedList(map, target) { + // Hash table key: target node value, value: node object + // If key is not in hash table, return null + return map.has(target) ? map.get(target) : null; +} + +/* Driver Code */ +const target = 3; + +/* Hash search (array) */ +const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; +// Initialize hash table +const map = new Map(); +for (let i = 0; i < nums.length; i++) { + map.set(nums[i], i); // key: element, value: index +} +const index = hashingSearchArray(map, target); +console.log('Index of target element 3 = ' + index); + +/* Hash search (linked list) */ +let head = arrToLinkedList(nums); +// Initialize hash table +const map1 = new Map(); +while (head != null) { + map1.set(head.val, head); // key: node value, value: node + head = head.next; +} +const node = hashingSearchLinkedList(map1, target); +console.log('Node object with target value 3 is', node); diff --git a/en/codes/javascript/chapter_searching/linear_search.js b/en/codes/javascript/chapter_searching/linear_search.js new file mode 100644 index 000000000..e8b07f560 --- /dev/null +++ b/en/codes/javascript/chapter_searching/linear_search.js @@ -0,0 +1,47 @@ +/** + * File: linear_search.js + * Created Time: 2022-12-22 + * Author: JoseHung (szhong@link.cuhk.edu.hk) + */ + +const { ListNode, arrToLinkedList } = require('../modules/ListNode'); + +/* Linear search (array) */ +function linearSearchArray(nums, target) { + // Traverse array + for (let i = 0; i < nums.length; i++) { + // Found the target element, return its index + if (nums[i] === target) { + return i; + } + } + // Target element not found, return -1 + return -1; +} + +/* Linear search (linked list) */ +function linearSearchLinkedList(head, target) { + // Traverse the linked list + while (head) { + // Found the target node, return it + if (head.val === target) { + return head; + } + head = head.next; + } + // Target node not found, return null + return null; +} + +/* Driver Code */ +const target = 3; + +/* Perform linear search in array */ +const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; +const index = linearSearchArray(nums, target); +console.log('Index of target element 3 = ' + index); + +/* Perform linear search in linked list */ +const head = arrToLinkedList(nums); +const node = linearSearchLinkedList(head, target); +console.log('Node object corresponding to target node value 3 is ', node); diff --git a/en/codes/javascript/chapter_searching/two_sum.js b/en/codes/javascript/chapter_searching/two_sum.js new file mode 100644 index 000000000..5dc6fb40c --- /dev/null +++ b/en/codes/javascript/chapter_searching/two_sum.js @@ -0,0 +1,46 @@ +/** + * File: two_sum.js + * Created Time: 2022-12-15 + * Author: gyt95 (gytkwan@gmail.com) + */ + +/* Method 1: Brute force enumeration */ +function twoSumBruteForce(nums, target) { + const n = nums.length; + // Two nested loops, time complexity is O(n^2) + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + if (nums[i] + nums[j] === target) { + return [i, j]; + } + } + } + return []; +} + +/* Method 2: Auxiliary hash table */ +function twoSumHashTable(nums, target) { + // Auxiliary hash table, space complexity is O(n) + let m = {}; + // Single loop, time complexity is O(n) + for (let i = 0; i < nums.length; i++) { + if (m[target - nums[i]] !== undefined) { + return [m[target - nums[i]], i]; + } else { + m[nums[i]] = i; + } + } + return []; +} + +/* Driver Code */ +// Method 1 +const nums = [2, 7, 11, 15], + target = 13; + +let res = twoSumBruteForce(nums, target); +console.log('Method 1 res = ', res); + +// Method 2 +res = twoSumHashTable(nums, target); +console.log('Method 2 res = ', res); diff --git a/en/codes/javascript/chapter_sorting/bubble_sort.js b/en/codes/javascript/chapter_sorting/bubble_sort.js new file mode 100644 index 000000000..71fa9a806 --- /dev/null +++ b/en/codes/javascript/chapter_sorting/bubble_sort.js @@ -0,0 +1,49 @@ +/** + * File: bubble_sort.js + * Created Time: 2022-12-01 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +/* Bubble sort */ +function bubbleSort(nums) { + // Outer loop: unsorted range is [0, i] + for (let i = nums.length - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + } + } + } +} + +/* Bubble sort (flag optimization) */ +function bubbleSortWithFlag(nums) { + // Outer loop: unsorted range is [0, i] + for (let i = nums.length - 1; i > 0; i--) { + let flag = false; // Initialize flag + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + flag = true; // Record element swap + } + } + if (!flag) break; // No elements were swapped in this round of "bubbling", exit directly + } +} + +/* Driver Code */ +const nums = [4, 1, 3, 1, 5, 2]; +bubbleSort(nums); +console.log('After bubble sort, nums =', nums); + +const nums1 = [4, 1, 3, 1, 5, 2]; +bubbleSortWithFlag(nums1); +console.log('After bubble sort, nums =', nums1); diff --git a/en/codes/javascript/chapter_sorting/bucket_sort.js b/en/codes/javascript/chapter_sorting/bucket_sort.js new file mode 100644 index 000000000..dbbd9d04d --- /dev/null +++ b/en/codes/javascript/chapter_sorting/bucket_sort.js @@ -0,0 +1,39 @@ +/** + * File: bucket_sort.js + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Bucket sort */ +function bucketSort(nums) { + // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + const k = nums.length / 2; + const buckets = []; + for (let i = 0; i < k; i++) { + buckets.push([]); + } + // 1. Distribute array elements into various buckets + for (const num of nums) { + // Input data range is [0, 1), use num * k to map to index range [0, k-1] + const i = Math.floor(num * k); + // Add num to bucket i + buckets[i].push(num); + } + // 2. Sort each bucket + for (const bucket of buckets) { + // Use built-in sorting function, can also replace with other sorting algorithms + bucket.sort((a, b) => a - b); + } + // 3. Traverse buckets to merge results + let i = 0; + for (const bucket of buckets) { + for (const num of bucket) { + nums[i++] = num; + } + } +} + +/* Driver Code */ +const nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37]; +bucketSort(nums); +console.log('After bucket sort, nums =', nums); diff --git a/en/codes/javascript/chapter_sorting/counting_sort.js b/en/codes/javascript/chapter_sorting/counting_sort.js new file mode 100644 index 000000000..18c93641f --- /dev/null +++ b/en/codes/javascript/chapter_sorting/counting_sort.js @@ -0,0 +1,65 @@ +/** + * File: counting_sort.js + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Counting sort */ +// Simple implementation, cannot be used for sorting objects +function countingSortNaive(nums) { + // 1. Count the maximum element m in the array + let m = Math.max(...nums); + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + const counter = new Array(m + 1).fill(0); + for (const num of nums) { + counter[num]++; + } + // 3. Traverse counter, filling each element back into the original array nums + let i = 0; + for (let num = 0; num < m + 1; num++) { + for (let j = 0; j < counter[num]; j++, i++) { + nums[i] = num; + } + } +} + +/* Counting sort */ +// Complete implementation, can sort objects and is a stable sort +function countingSort(nums) { + // 1. Count the maximum element m in the array + let m = Math.max(...nums); + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + const counter = new Array(m + 1).fill(0); + for (const num of nums) { + counter[num]++; + } + // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + // counter[num]-1 is the last index where num appears in res + for (let i = 0; i < m; i++) { + counter[i + 1] += counter[i]; + } + // 4. Traverse nums in reverse order, placing each element into the result array res + // Initialize the array res to record results + const n = nums.length; + const res = new Array(n); + for (let i = n - 1; i >= 0; i--) { + const num = nums[i]; + res[counter[num] - 1] = num; // Place num at the corresponding index + counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num + } + // Use result array res to overwrite the original array nums + for (let i = 0; i < n; i++) { + nums[i] = res[i]; + } +} + +/* Driver Code */ +const nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; +countingSortNaive(nums); +console.log('After counting sort (cannot sort objects), nums =', nums); + +const nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; +countingSort(nums1); +console.log('After counting sort, nums1 =', nums1); diff --git a/en/codes/javascript/chapter_sorting/heap_sort.js b/en/codes/javascript/chapter_sorting/heap_sort.js new file mode 100644 index 000000000..3d9db9ab3 --- /dev/null +++ b/en/codes/javascript/chapter_sorting/heap_sort.js @@ -0,0 +1,49 @@ +/** + * File: heap_sort.js + * Created Time: 2023-06-04 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Heap length is n, start heapifying node i, from top to bottom */ +function siftDown(nums, n, i) { + while (true) { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + let l = 2 * i + 1; + let r = 2 * i + 2; + let ma = i; + if (l < n && nums[l] > nums[ma]) { + ma = l; + } + if (r < n && nums[r] > nums[ma]) { + ma = r; + } + // Swap two nodes + if (ma === i) { + break; + } + // Swap two nodes + [nums[i], nums[ma]] = [nums[ma], nums[i]]; + // Loop downwards heapification + i = ma; + } +} + +/* Heap sort */ +function heapSort(nums) { + // Build heap operation: heapify all nodes except leaves + for (let i = Math.floor(nums.length / 2) - 1; i >= 0; i--) { + siftDown(nums, nums.length, i); + } + // Extract the largest element from the heap and repeat for n-1 rounds + for (let i = nums.length - 1; i > 0; i--) { + // Delete node + [nums[0], nums[i]] = [nums[i], nums[0]]; + // Start heapifying the root node, from top to bottom + siftDown(nums, i, 0); + } +} + +/* Driver Code */ +const nums = [4, 1, 3, 1, 5, 2]; +heapSort(nums); +console.log('After heap sort, nums =', nums); diff --git a/en/codes/javascript/chapter_sorting/insertion_sort.js b/en/codes/javascript/chapter_sorting/insertion_sort.js new file mode 100644 index 000000000..40d3f494d --- /dev/null +++ b/en/codes/javascript/chapter_sorting/insertion_sort.js @@ -0,0 +1,25 @@ +/** + * File: insertion_sort.js + * Created Time: 2022-12-01 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +/* Insertion sort */ +function insertionSort(nums) { + // Outer loop: sorted interval is [0, i-1] + for (let i = 1; i < nums.length; i++) { + let base = nums[i], + j = i - 1; + // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // Move nums[j] to the right by one position + j--; + } + nums[j + 1] = base; // Assign base to the correct position + } +} + +/* Driver Code */ +const nums = [4, 1, 3, 1, 5, 2]; +insertionSort(nums); +console.log('After insertion sort, nums =', nums); diff --git a/en/codes/javascript/chapter_sorting/merge_sort.js b/en/codes/javascript/chapter_sorting/merge_sort.js new file mode 100644 index 000000000..b5df2ced3 --- /dev/null +++ b/en/codes/javascript/chapter_sorting/merge_sort.js @@ -0,0 +1,52 @@ +/** + * File: merge_sort.js + * Created Time: 2022-12-01 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +/* Merge left subarray and right subarray */ +function merge(nums, left, mid, right) { + // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + // Create a temporary array tmp to store the merged results + const tmp = new Array(right - left + 1); + // Initialize the start indices of the left and right subarrays + let i = left, + j = mid + 1, + k = 0; + // While both subarrays still have elements, compare and copy the smaller element into the temporary array + while (i <= mid && j <= right) { + if (nums[i] <= nums[j]) { + tmp[k++] = nums[i++]; + } else { + tmp[k++] = nums[j++]; + } + } + // Copy the remaining elements of the left and right subarrays into the temporary array + while (i <= mid) { + tmp[k++] = nums[i++]; + } + while (j <= right) { + tmp[k++] = nums[j++]; + } + // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + for (k = 0; k < tmp.length; k++) { + nums[left + k] = tmp[k]; + } +} + +/* Merge sort */ +function mergeSort(nums, left, right) { + // Termination condition + if (left >= right) return; // Terminate recursion when subarray length is 1 + // Divide and conquer stage + let mid = Math.floor(left + (right - left) / 2); // Calculate midpoint + mergeSort(nums, left, mid); // Recursively process the left subarray + mergeSort(nums, mid + 1, right); // Recursively process the right subarray + // Merge stage + merge(nums, left, mid, right); +} + +/* Driver Code */ +const nums = [7, 3, 2, 6, 0, 1, 5, 4]; +mergeSort(nums, 0, nums.length - 1); +console.log('After merge sort, nums =', nums); diff --git a/en/codes/javascript/chapter_sorting/quick_sort.js b/en/codes/javascript/chapter_sorting/quick_sort.js new file mode 100644 index 000000000..9d8e3b106 --- /dev/null +++ b/en/codes/javascript/chapter_sorting/quick_sort.js @@ -0,0 +1,161 @@ +/** + * File: quick_sort.js + * Created Time: 2022-12-01 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +/* Quick sort class */ +class QuickSort { + /* Swap elements */ + swap(nums, i, j) { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Sentinel partition */ + partition(nums, left, right) { + // Use nums[left] as the pivot + let i = left, + j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) { + j -= 1; // Search from right to left for the first element smaller than the pivot + } + while (i < j && nums[i] <= nums[left]) { + i += 1; // Search from left to right for the first element greater than the pivot + } + // Swap elements + this.swap(nums, i, j); // Swap these two elements + } + this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort */ + quickSort(nums, left, right) { + // Terminate recursion when subarray length is 1 + if (left >= right) return; + // Sentinel partition + const pivot = this.partition(nums, left, right); + // Recursively process the left subarray and right subarray + this.quickSort(nums, left, pivot - 1); + this.quickSort(nums, pivot + 1, right); + } +} + +/* Quick sort class (median pivot optimization) */ +class QuickSortMedian { + /* Swap elements */ + swap(nums, i, j) { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Select the median of three candidate elements */ + medianThree(nums, left, mid, right) { + let l = nums[left], + m = nums[mid], + r = nums[right]; + // m is between l and r + if ((l <= m && m <= r) || (r <= m && m <= l)) return mid; + // l is between m and r + if ((m <= l && l <= r) || (r <= l && l <= m)) return left; + return right; + } + + /* Sentinel partition (median of three) */ + partition(nums, left, right) { + // Select the median of three candidate elements + let med = this.medianThree( + nums, + left, + Math.floor((left + right) / 2), + right + ); + // Swap the median to the array's leftmost position + this.swap(nums, left, med); + // Use nums[left] as the pivot + let i = left, + j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) j--; // Search from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) i++; // Search from left to right for the first element greater than the pivot + this.swap(nums, i, j); // Swap these two elements + } + this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort */ + quickSort(nums, left, right) { + // Terminate recursion when subarray length is 1 + if (left >= right) return; + // Sentinel partition + const pivot = this.partition(nums, left, right); + // Recursively process the left subarray and right subarray + this.quickSort(nums, left, pivot - 1); + this.quickSort(nums, pivot + 1, right); + } +} + +/* Quick sort class (recursion depth optimization) */ +class QuickSortTailCall { + /* Swap elements */ + swap(nums, i, j) { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Sentinel partition */ + partition(nums, left, right) { + // Use nums[left] as the pivot + let i = left, + j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) j--; // Search from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) i++; // Search from left to right for the first element greater than the pivot + this.swap(nums, i, j); // Swap these two elements + } + this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort (recursion depth optimization) */ + quickSort(nums, left, right) { + // Terminate when subarray length is 1 + while (left < right) { + // Sentinel partition operation + let pivot = this.partition(nums, left, right); + // Perform quick sort on the shorter of the two subarrays + if (pivot - left < right - pivot) { + this.quickSort(nums, left, pivot - 1); // Recursively sort the left subarray + left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + } else { + this.quickSort(nums, pivot + 1, right); // Recursively sort the right subarray + right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + } + } + } +} + +/* Driver Code */ +/* Quick sort */ +const nums = [2, 4, 1, 0, 3, 5]; +const quickSort = new QuickSort(); +quickSort.quickSort(nums, 0, nums.length - 1); +console.log('After quick sort, nums =', nums); + +/* Quick sort (recursion depth optimization) */ +const nums1 = [2, 4, 1, 0, 3, 5]; +const quickSortMedian = new QuickSortMedian(); +quickSortMedian.quickSort(nums1, 0, nums1.length - 1); +console.log('After quick sort (median pivot optimization), nums =', nums1); + +/* Quick sort (recursion depth optimization) */ +const nums2 = [2, 4, 1, 0, 3, 5]; +const quickSortTailCall = new QuickSortTailCall(); +quickSortTailCall.quickSort(nums2, 0, nums2.length - 1); +console.log('After quick sort (recursion depth optimization), nums =', nums2); diff --git a/en/codes/javascript/chapter_sorting/radix_sort.js b/en/codes/javascript/chapter_sorting/radix_sort.js new file mode 100644 index 000000000..573228f7b --- /dev/null +++ b/en/codes/javascript/chapter_sorting/radix_sort.js @@ -0,0 +1,61 @@ +/** + * File: radix_sort.js + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Get the k-th digit of element num, where exp = 10^(k-1) */ +function digit(num, exp) { + // Passing exp instead of k can avoid repeated expensive exponentiation here + return Math.floor(num / exp) % 10; +} + +/* Counting sort (based on nums k-th digit) */ +function countingSortDigit(nums, exp) { + // Decimal digit range is 0~9, therefore need a bucket array of length 10 + const counter = new Array(10).fill(0); + const n = nums.length; + // Count the occurrence of digits 0~9 + for (let i = 0; i < n; i++) { + const d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d + counter[d]++; // Count the occurrence of digit d + } + // Calculate prefix sum, converting "occurrence count" into "array index" + for (let i = 1; i < 10; i++) { + counter[i] += counter[i - 1]; + } + // Traverse in reverse, based on bucket statistics, place each element into res + const res = new Array(n).fill(0); + for (let i = n - 1; i >= 0; i--) { + const d = digit(nums[i], exp); + const j = counter[d] - 1; // Get the index j for d in the array + res[j] = nums[i]; // Place the current element at index j + counter[d]--; // Decrease the count of d by 1 + } + // Use result to overwrite the original array nums + for (let i = 0; i < n; i++) { + nums[i] = res[i]; + } +} + +/* Radix sort */ +function radixSort(nums) { + // Get the maximum element of the array, used to determine the maximum number of digits + let m = Math.max(... nums); + // Traverse from the lowest to the highest digit + for (let exp = 1; exp <= m; exp *= 10) { + // Perform counting sort on the k-th digit of array elements + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // i.e., exp = 10^(k-1) + countingSortDigit(nums, exp); + } +} + +/* Driver Code */ +const nums = [ + 10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, + 30524779, 82060337, 63832996, +]; +radixSort(nums); +console.log('After radix sort, nums =', nums); diff --git a/en/codes/javascript/chapter_sorting/selection_sort.js b/en/codes/javascript/chapter_sorting/selection_sort.js new file mode 100644 index 000000000..162986711 --- /dev/null +++ b/en/codes/javascript/chapter_sorting/selection_sort.js @@ -0,0 +1,27 @@ +/** + * File: selection_sort.js + * Created Time: 2023-06-04 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Selection sort */ +function selectionSort(nums) { + let n = nums.length; + // Outer loop: unsorted interval is [i, n-1] + for (let i = 0; i < n - 1; i++) { + // Inner loop: find the smallest element within the unsorted interval + let k = i; + for (let j = i + 1; j < n; j++) { + if (nums[j] < nums[k]) { + k = j; // Record the index of the smallest element + } + } + // Swap the smallest element with the first element of the unsorted interval + [nums[i], nums[k]] = [nums[k], nums[i]]; + } +} + +/* Driver Code */ +const nums = [4, 1, 3, 1, 5, 2]; +selectionSort(nums); +console.log('After selection sort, nums =', nums); diff --git a/en/codes/javascript/chapter_stack_and_queue/array_deque.js b/en/codes/javascript/chapter_stack_and_queue/array_deque.js new file mode 100644 index 000000000..c7545b978 --- /dev/null +++ b/en/codes/javascript/chapter_stack_and_queue/array_deque.js @@ -0,0 +1,156 @@ +/** + * File: array_deque.js + * Created Time: 2023-02-28 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +/* Double-ended queue based on circular array implementation */ +class ArrayDeque { + #nums; // Array for storing double-ended queue elements + #front; // Front pointer, points to the front of the queue element + #queSize; // Double-ended queue length + + /* Constructor */ + constructor(capacity) { + this.#nums = new Array(capacity); + this.#front = 0; + this.#queSize = 0; + } + + /* Get the capacity of the double-ended queue */ + capacity() { + return this.#nums.length; + } + + /* Get the length of the double-ended queue */ + size() { + return this.#queSize; + } + + /* Check if the double-ended queue is empty */ + isEmpty() { + return this.#queSize === 0; + } + + /* Calculate circular array index */ + index(i) { + // Use modulo operation to wrap the array head and tail together + // When i passes the tail of the array, return to the head + // When i passes the head of the array, return to the tail + return (i + this.capacity()) % this.capacity(); + } + + /* Front of the queue enqueue */ + pushFirst(num) { + if (this.#queSize === this.capacity()) { + console.log('Double-ended queue is full'); + return; + } + // Use modulo operation to wrap front around to the tail after passing the head of the array + // Add num to the front of the queue + this.#front = this.index(this.#front - 1); + // Add num to front of queue + this.#nums[this.#front] = num; + this.#queSize++; + } + + /* Rear of the queue enqueue */ + pushLast(num) { + if (this.#queSize === this.capacity()) { + console.log('Double-ended queue is full'); + return; + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + const rear = this.index(this.#front + this.#queSize); + // Front pointer moves one position backward + this.#nums[rear] = num; + this.#queSize++; + } + + /* Rear of the queue dequeue */ + popFirst() { + const num = this.peekFirst(); + // Move front pointer backward by one position + this.#front = this.index(this.#front + 1); + this.#queSize--; + return num; + } + + /* Access rear of the queue element */ + popLast() { + const num = this.peekLast(); + this.#queSize--; + return num; + } + + /* Return list for printing */ + peekFirst() { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + return this.#nums[this.#front]; + } + + /* Driver Code */ + peekLast() { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + // Initialize double-ended queue + const last = this.index(this.#front + this.#queSize - 1); + return this.#nums[last]; + } + + /* Return array for printing */ + toArray() { + // Elements enqueue + const res = []; + for (let i = 0, j = this.#front; i < this.#queSize; i++, j++) { + res[i] = this.#nums[this.index(j)]; + } + return res; + } +} + +/* Driver Code */ +/* Get the length of the double-ended queue */ +const capacity = 5; +const deque = new ArrayDeque(capacity); +deque.pushLast(3); +deque.pushLast(2); +deque.pushLast(5); +console.log('Deque deque = [' + deque.toArray() + ']'); + +/* Update element */ +const peekFirst = deque.peekFirst(); +console.log('Front element peekFirst = ' + peekFirst); +const peekLast = deque.peekLast(); +console.log('Rear element peekLast = ' + peekLast); + +/* Elements enqueue */ +deque.pushLast(4); +console.log('After element 4 enqueues at rear, deque = [' + deque.toArray() + ']'); +deque.pushFirst(1); +console.log('After element 1 enqueues at front, deque = [' + deque.toArray() + ']'); + +/* Element dequeue */ +const popLast = deque.popLast(); +console.log( + 'Rear dequeue element = ' + + popLast + + ', after rear dequeue deque = [' + + deque.toArray() + + ']' +); +const popFirst = deque.popFirst(); +console.log( + 'Front dequeue element = ' + + popFirst + + ', after front dequeue deque = [' + + deque.toArray() + + ']' +); + +/* Get the length of the double-ended queue */ +const size = deque.size(); +console.log('Double-ended queue length size = ' + size); + +/* Check if the double-ended queue is empty */ +const isEmpty = deque.isEmpty(); +console.log('Double-ended queue is empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/array_queue.js b/en/codes/javascript/chapter_stack_and_queue/array_queue.js new file mode 100644 index 000000000..6893d2904 --- /dev/null +++ b/en/codes/javascript/chapter_stack_and_queue/array_queue.js @@ -0,0 +1,106 @@ +/** + * File: array_queue.js + * Created Time: 2022-12-13 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +/* Queue based on circular array implementation */ +class ArrayQueue { + #nums; // Array for storing queue elements + #front = 0; // Front pointer, points to the front of the queue element + #queSize = 0; // Queue length + + constructor(capacity) { + this.#nums = new Array(capacity); + } + + /* Get the capacity of the queue */ + get capacity() { + return this.#nums.length; + } + + /* Get the length of the queue */ + get size() { + return this.#queSize; + } + + /* Check if the queue is empty */ + isEmpty() { + return this.#queSize === 0; + } + + /* Enqueue */ + push(num) { + if (this.size === this.capacity) { + console.log('Queue is full'); + return; + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Add num to the rear of the queue + const rear = (this.#front + this.size) % this.capacity; + // Front pointer moves one position backward + this.#nums[rear] = num; + this.#queSize++; + } + + /* Dequeue */ + pop() { + const num = this.peek(); + // Move front pointer backward by one position, if it passes the tail, return to array head + this.#front = (this.#front + 1) % this.capacity; + this.#queSize--; + return num; + } + + /* Return list for printing */ + peek() { + if (this.isEmpty()) throw new Error('Queue is empty'); + return this.#nums[this.#front]; + } + + /* Return Array */ + toArray() { + // Elements enqueue + const arr = new Array(this.size); + for (let i = 0, j = this.#front; i < this.size; i++, j++) { + arr[i] = this.#nums[j % this.capacity]; + } + return arr; + } +} + +/* Driver Code */ +/* Access front of the queue element */ +const capacity = 10; +const queue = new ArrayQueue(capacity); + +/* Elements enqueue */ +queue.push(1); +queue.push(3); +queue.push(2); +queue.push(5); +queue.push(4); +console.log('Queue queue =', queue.toArray()); + +/* Return list for printing */ +const peek = queue.peek(); +console.log('Front element peek = ' + peek); + +/* Element dequeue */ +const pop = queue.pop(); +console.log('Dequeue element pop = ' + pop + ', after dequeue queue =', queue.toArray()); + +/* Get the length of the queue */ +const size = queue.size; +console.log('Queue length size = ' + size); + +/* Check if the queue is empty */ +const isEmpty = queue.isEmpty(); +console.log('Queue is empty = ' + isEmpty); + +/* Test circular array */ +for (let i = 0; i < 10; i++) { + queue.push(i); + queue.pop(); + console.log('Round ' + i + ' rounds of enqueue + dequeue, queue =', queue.toArray()); +} diff --git a/en/codes/javascript/chapter_stack_and_queue/array_stack.js b/en/codes/javascript/chapter_stack_and_queue/array_stack.js new file mode 100644 index 000000000..4be71e2ec --- /dev/null +++ b/en/codes/javascript/chapter_stack_and_queue/array_stack.js @@ -0,0 +1,75 @@ +/** + * File: array_stack.js + * Created Time: 2022-12-09 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +/* Stack based on array implementation */ +class ArrayStack { + #stack; + constructor() { + this.#stack = []; + } + + /* Get the length of the stack */ + get size() { + return this.#stack.length; + } + + /* Check if the stack is empty */ + isEmpty() { + return this.#stack.length === 0; + } + + /* Push */ + push(num) { + this.#stack.push(num); + } + + /* Pop */ + pop() { + if (this.isEmpty()) throw new Error('Stack is empty'); + return this.#stack.pop(); + } + + /* Return list for printing */ + top() { + if (this.isEmpty()) throw new Error('Stack is empty'); + return this.#stack[this.#stack.length - 1]; + } + + /* Return Array */ + toArray() { + return this.#stack; + } +} + +/* Driver Code */ +/* Access top of the stack element */ +const stack = new ArrayStack(); + +/* Elements push onto stack */ +stack.push(1); +stack.push(3); +stack.push(2); +stack.push(5); +stack.push(4); +console.log('Stack stack = '); +console.log(stack.toArray()); + +/* Return list for printing */ +const top = stack.top(); +console.log('Stack top element top = ' + top); + +/* Element pop from stack */ +const pop = stack.pop(); +console.log('Pop element pop = ' + pop + ', after pop, stack = '); +console.log(stack.toArray()); + +/* Get the length of the stack */ +const size = stack.size; +console.log('Stack length size = ' + size); + +/* Check if empty */ +const isEmpty = stack.isEmpty(); +console.log('Stack is empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/deque.js b/en/codes/javascript/chapter_stack_and_queue/deque.js new file mode 100644 index 000000000..6a92239c7 --- /dev/null +++ b/en/codes/javascript/chapter_stack_and_queue/deque.js @@ -0,0 +1,44 @@ +/** + * File: deque.js + * Created Time: 2023-01-17 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +/* Driver Code */ +/* Get the length of the double-ended queue */ +// JavaScript has no built-in deque, can only use Array as deque +const deque = []; + +/* Elements enqueue */ +deque.push(2); +deque.push(5); +deque.push(4); +// Note: due to array, unshift() method has O(n) time complexity +deque.unshift(3); +deque.unshift(1); +console.log('Double-ended queue deque = ', deque); + +/* Update element */ +const peekFirst = deque[0]; +console.log('Front element peekFirst = ' + peekFirst); +const peekLast = deque[deque.length - 1]; +console.log('Rear element peekLast = ' + peekLast); + +/* Element dequeue */ +// Note: due to array, shift() method has O(n) time complexity +const popFront = deque.shift(); +console.log( + 'Front dequeue element popFront = ' + popFront + ', after front dequeue, deque = ' + deque +); +const popBack = deque.pop(); +console.log( + 'Dequeue rear element popBack = ' + popBack + ', after rear dequeue, deque = ' + deque +); + +/* Get the length of the double-ended queue */ +const size = deque.length; +console.log('Double-ended queue length size = ' + size); + +/* Check if the double-ended queue is empty */ +const isEmpty = size === 0; +console.log('Double-ended queue is empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/linkedlist_deque.js b/en/codes/javascript/chapter_stack_and_queue/linkedlist_deque.js new file mode 100644 index 000000000..717b3fa32 --- /dev/null +++ b/en/codes/javascript/chapter_stack_and_queue/linkedlist_deque.js @@ -0,0 +1,167 @@ +/** + * File: linkedlist_deque.js + * Created Time: 2023-02-04 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +/* Doubly linked list node */ +class ListNode { + prev; // Predecessor node reference (pointer) + next; // Successor node reference (pointer) + val; // Node value + + constructor(val) { + this.val = val; + this.next = null; + this.prev = null; + } +} + +/* Double-ended queue based on doubly linked list implementation */ +class LinkedListDeque { + #front; // Head node front + #rear; // Tail node rear + #queSize; // Length of the double-ended queue + + constructor() { + this.#front = null; + this.#rear = null; + this.#queSize = 0; + } + + /* Rear of the queue enqueue operation */ + pushLast(val) { + const node = new ListNode(val); + // If the linked list is empty, make both front and rear point to node + if (this.#queSize === 0) { + this.#front = node; + this.#rear = node; + } else { + // Add node to the tail of the linked list + this.#rear.next = node; + node.prev = this.#rear; + this.#rear = node; // Update tail node + } + this.#queSize++; + } + + /* Front of the queue enqueue operation */ + pushFirst(val) { + const node = new ListNode(val); + // If the linked list is empty, make both front and rear point to node + if (this.#queSize === 0) { + this.#front = node; + this.#rear = node; + } else { + // Add node to the head of the linked list + this.#front.prev = node; + node.next = this.#front; + this.#front = node; // Update head node + } + this.#queSize++; + } + + /* Temporarily store tail node value */ + popLast() { + if (this.#queSize === 0) { + return null; + } + const value = this.#rear.val; // Store tail node value + // Update tail node + let temp = this.#rear.prev; + if (temp !== null) { + temp.next = null; + this.#rear.prev = null; + } + this.#rear = temp; // Update tail node + this.#queSize--; + return value; + } + + /* Temporarily store head node value */ + popFirst() { + if (this.#queSize === 0) { + return null; + } + const value = this.#front.val; // Store tail node value + // Delete head node + let temp = this.#front.next; + if (temp !== null) { + temp.prev = null; + this.#front.next = null; + } + this.#front = temp; // Update head node + this.#queSize--; + return value; + } + + /* Driver Code */ + peekLast() { + return this.#queSize === 0 ? null : this.#rear.val; + } + + /* Return list for printing */ + peekFirst() { + return this.#queSize === 0 ? null : this.#front.val; + } + + /* Get the length of the double-ended queue */ + size() { + return this.#queSize; + } + + /* Check if the double-ended queue is empty */ + isEmpty() { + return this.#queSize === 0; + } + + /* Print deque */ + print() { + const arr = []; + let temp = this.#front; + while (temp !== null) { + arr.push(temp.val); + temp = temp.next; + } + console.log('[' + arr.join(', ') + ']'); + } +} + +/* Driver Code */ +/* Get the length of the double-ended queue */ +const linkedListDeque = new LinkedListDeque(); +linkedListDeque.pushLast(3); +linkedListDeque.pushLast(2); +linkedListDeque.pushLast(5); +console.log('Deque linkedListDeque = '); +linkedListDeque.print(); + +/* Update element */ +const peekFirst = linkedListDeque.peekFirst(); +console.log('Front element peekFirst = ' + peekFirst); +const peekLast = linkedListDeque.peekLast(); +console.log('Rear element peekLast = ' + peekLast); + +/* Elements enqueue */ +linkedListDeque.pushLast(4); +console.log('After element 4 enqueues at rear, linkedListDeque = '); +linkedListDeque.print(); +linkedListDeque.pushFirst(1); +console.log('After element 1 enqueues at front, linkedListDeque = '); +linkedListDeque.print(); + +/* Element dequeue */ +const popLast = linkedListDeque.popLast(); +console.log('Rear dequeue element = ' + popLast + ', after rear dequeue linkedListDeque = '); +linkedListDeque.print(); +const popFirst = linkedListDeque.popFirst(); +console.log('Front dequeue element = ' + popFirst + ', after front dequeue linkedListDeque = '); +linkedListDeque.print(); + +/* Get the length of the double-ended queue */ +const size = linkedListDeque.size(); +console.log('Double-ended queue length size = ' + size); + +/* Check if the double-ended queue is empty */ +const isEmpty = linkedListDeque.isEmpty(); +console.log('Double-ended queue is empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/linkedlist_queue.js b/en/codes/javascript/chapter_stack_and_queue/linkedlist_queue.js new file mode 100644 index 000000000..31093bd99 --- /dev/null +++ b/en/codes/javascript/chapter_stack_and_queue/linkedlist_queue.js @@ -0,0 +1,99 @@ +/** + * File: linkedlist_queue.js + * Created Time: 2022-12-20 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +const { ListNode } = require('../modules/ListNode'); + +/* Queue based on linked list implementation */ +class LinkedListQueue { + #front; // Front node #front + #rear; // Rear node #rear + #queSize = 0; + + constructor() { + this.#front = null; + this.#rear = null; + } + + /* Get the length of the queue */ + get size() { + return this.#queSize; + } + + /* Check if the queue is empty */ + isEmpty() { + return this.size === 0; + } + + /* Enqueue */ + push(num) { + // Add num after the tail node + const node = new ListNode(num); + // If the queue is empty, make both front and rear point to the node + if (!this.#front) { + this.#front = node; + this.#rear = node; + // If the queue is not empty, add the node after the tail node + } else { + this.#rear.next = node; + this.#rear = node; + } + this.#queSize++; + } + + /* Dequeue */ + pop() { + const num = this.peek(); + // Delete head node + this.#front = this.#front.next; + this.#queSize--; + return num; + } + + /* Return list for printing */ + peek() { + if (this.size === 0) throw new Error('Queue is empty'); + return this.#front.val; + } + + /* Convert linked list to Array and return */ + toArray() { + let node = this.#front; + const res = new Array(this.size); + for (let i = 0; i < res.length; i++) { + res[i] = node.val; + node = node.next; + } + return res; + } +} + +/* Driver Code */ +/* Access front of the queue element */ +const queue = new LinkedListQueue(); + +/* Elements enqueue */ +queue.push(1); +queue.push(3); +queue.push(2); +queue.push(5); +queue.push(4); +console.log('Queue queue = ' + queue.toArray()); + +/* Return list for printing */ +const peek = queue.peek(); +console.log('Front element peek = ' + peek); + +/* Element dequeue */ +const pop = queue.pop(); +console.log('Dequeue element pop = ' + pop + ', after dequeue, queue = ' + queue.toArray()); + +/* Get the length of the queue */ +const size = queue.size; +console.log('Queue length size = ' + size); + +/* Check if the queue is empty */ +const isEmpty = queue.isEmpty(); +console.log('Queue is empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/linkedlist_stack.js b/en/codes/javascript/chapter_stack_and_queue/linkedlist_stack.js new file mode 100644 index 000000000..7e7bb6a66 --- /dev/null +++ b/en/codes/javascript/chapter_stack_and_queue/linkedlist_stack.js @@ -0,0 +1,88 @@ +/** + * File: linkedlist_stack.js + * Created Time: 2022-12-22 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +const { ListNode } = require('../modules/ListNode'); + +/* Stack based on linked list implementation */ +class LinkedListStack { + #stackPeek; // Use head node as stack top + #stkSize = 0; // Stack length + + constructor() { + this.#stackPeek = null; + } + + /* Get the length of the stack */ + get size() { + return this.#stkSize; + } + + /* Check if the stack is empty */ + isEmpty() { + return this.size === 0; + } + + /* Push */ + push(num) { + const node = new ListNode(num); + node.next = this.#stackPeek; + this.#stackPeek = node; + this.#stkSize++; + } + + /* Pop */ + pop() { + const num = this.peek(); + this.#stackPeek = this.#stackPeek.next; + this.#stkSize--; + return num; + } + + /* Return list for printing */ + peek() { + if (!this.#stackPeek) throw new Error('Stack is empty'); + return this.#stackPeek.val; + } + + /* Convert linked list to Array and return */ + toArray() { + let node = this.#stackPeek; + const res = new Array(this.size); + for (let i = res.length - 1; i >= 0; i--) { + res[i] = node.val; + node = node.next; + } + return res; + } +} + +/* Driver Code */ +/* Access top of the stack element */ +const stack = new LinkedListStack(); + +/* Elements push onto stack */ +stack.push(1); +stack.push(3); +stack.push(2); +stack.push(5); +stack.push(4); +console.log('Stack stack = ' + stack.toArray()); + +/* Return list for printing */ +const peek = stack.peek(); +console.log('Stack top element peek = ' + peek); + +/* Element pop from stack */ +const pop = stack.pop(); +console.log('Pop element pop = ' + pop + ', after pop, stack = ' + stack.toArray()); + +/* Get the length of the stack */ +const size = stack.size; +console.log('Stack length size = ' + size); + +/* Check if empty */ +const isEmpty = stack.isEmpty(); +console.log('Stack is empty = ' + isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/queue.js b/en/codes/javascript/chapter_stack_and_queue/queue.js new file mode 100644 index 000000000..15db49b28 --- /dev/null +++ b/en/codes/javascript/chapter_stack_and_queue/queue.js @@ -0,0 +1,35 @@ +/** + * File: queue.js + * Created Time: 2022-12-05 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +/* Driver Code */ +/* Access front of the queue element */ +// JavaScript has no built-in queue, can use Array as queue +const queue = []; + +/* Elements enqueue */ +queue.push(1); +queue.push(3); +queue.push(2); +queue.push(5); +queue.push(4); +console.log('Queue queue =', queue); + +/* Return list for printing */ +const peek = queue[0]; +console.log('Front element peek =', peek); + +/* Element dequeue */ +// Underlying is array, so shift() method has O(n) time complexity +const pop = queue.shift(); +console.log('Dequeue element pop =', pop, ', after dequeue, queue = ', queue); + +/* Get the length of the queue */ +const size = queue.length; +console.log('Queue length size =', size); + +/* Check if the queue is empty */ +const isEmpty = queue.length === 0; +console.log('Queue is empty = ', isEmpty); diff --git a/en/codes/javascript/chapter_stack_and_queue/stack.js b/en/codes/javascript/chapter_stack_and_queue/stack.js new file mode 100644 index 000000000..98a675eb8 --- /dev/null +++ b/en/codes/javascript/chapter_stack_and_queue/stack.js @@ -0,0 +1,35 @@ +/** + * File: stack.js + * Created Time: 2022-12-04 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +/* Driver Code */ +/* Access top of the stack element */ +// JavaScript has no built-in stack class, can use Array as stack +const stack = []; + +/* Elements push onto stack */ +stack.push(1); +stack.push(3); +stack.push(2); +stack.push(5); +stack.push(4); +console.log('Stack stack =', stack); + +/* Return list for printing */ +const peek = stack[stack.length - 1]; +console.log('Stack top element peek =', peek); + +/* Element pop from stack */ +const pop = stack.pop(); +console.log('Pop element pop =', pop); +console.log('After pop, stack =', stack); + +/* Get the length of the stack */ +const size = stack.length; +console.log('Stack length size =', size); + +/* Check if empty */ +const isEmpty = stack.length === 0; +console.log('Is stack empty =', isEmpty); diff --git a/en/codes/javascript/chapter_tree/array_binary_tree.js b/en/codes/javascript/chapter_tree/array_binary_tree.js new file mode 100644 index 000000000..1d9643c0d --- /dev/null +++ b/en/codes/javascript/chapter_tree/array_binary_tree.js @@ -0,0 +1,147 @@ +/** + * File: array_binary_tree.js + * Created Time: 2023-08-06 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +const { arrToTree } = require('../modules/TreeNode'); +const { printTree } = require('../modules/PrintUtil'); + +/* Binary tree class represented by array */ +class ArrayBinaryTree { + #tree; + + /* Constructor */ + constructor(arr) { + this.#tree = arr; + } + + /* List capacity */ + size() { + return this.#tree.length; + } + + /* Get value of node at index i */ + val(i) { + // If index out of bounds, return null to represent empty position + if (i < 0 || i >= this.size()) return null; + return this.#tree[i]; + } + + /* Get index of left child node of node at index i */ + left(i) { + return 2 * i + 1; + } + + /* Get index of right child node of node at index i */ + right(i) { + return 2 * i + 2; + } + + /* Get index of parent node of node at index i */ + parent(i) { + return Math.floor((i - 1) / 2); // Floor division + } + + /* Level-order traversal */ + levelOrder() { + let res = []; + // Traverse array directly + for (let i = 0; i < this.size(); i++) { + if (this.val(i) !== null) res.push(this.val(i)); + } + return res; + } + + /* Depth-first traversal */ + #dfs(i, order, res) { + // If empty position, return + if (this.val(i) === null) return; + // Preorder traversal + if (order === 'pre') res.push(this.val(i)); + this.#dfs(this.left(i), order, res); + // Inorder traversal + if (order === 'in') res.push(this.val(i)); + this.#dfs(this.right(i), order, res); + // Postorder traversal + if (order === 'post') res.push(this.val(i)); + } + + /* Preorder traversal */ + preOrder() { + const res = []; + this.#dfs(0, 'pre', res); + return res; + } + + /* Inorder traversal */ + inOrder() { + const res = []; + this.#dfs(0, 'in', res); + return res; + } + + /* Postorder traversal */ + postOrder() { + const res = []; + this.#dfs(0, 'post', res); + return res; + } +} + +/* Driver Code */ +// Initialize binary tree +// Here we use a function to generate a binary tree directly from an array +const arr = Array.of( + 1, + 2, + 3, + 4, + null, + 6, + 7, + 8, + 9, + null, + null, + 12, + null, + null, + 15 +); + +const root = arrToTree(arr); +console.log('\nInitialize binary tree\n'); +console.log('Array representation of binary tree:'); +console.log(arr); +console.log('Linked list representation of binary tree:'); +printTree(root); + +// Binary tree class represented by array +const abt = new ArrayBinaryTree(arr); + +// Access node +const i = 1; +const l = abt.left(i); +const r = abt.right(i); +const p = abt.parent(i); +console.log('\nCurrent node index is ' + i + ', value is ' + abt.val(i)); +console.log( + 'Its left child node index is ' + l + ', value is ' + (l === null ? 'null' : abt.val(l)) +); +console.log( + 'Its right child node index is ' + r + ', value is ' + (r === null ? 'null' : abt.val(r)) +); +console.log( + 'Its parent node index is ' + p + ', value is ' + (p === null ? 'null' : abt.val(p)) +); + +// Traverse tree +let res = abt.levelOrder(); +console.log('\nLevel-order traversal is:' + res); +res = abt.preOrder(); +console.log('Preorder traversal is:' + res); +res = abt.inOrder(); +console.log('Inorder traversal is:' + res); +res = abt.postOrder(); +console.log('Postorder traversal is:' + res); diff --git a/en/codes/javascript/chapter_tree/avl_tree.js b/en/codes/javascript/chapter_tree/avl_tree.js new file mode 100644 index 000000000..e2a276b25 --- /dev/null +++ b/en/codes/javascript/chapter_tree/avl_tree.js @@ -0,0 +1,208 @@ +/** + * File: avl_tree.js + * Created Time: 2023-02-05 + * Author: what-is-me (whatisme@outlook.jp) + */ + +const { TreeNode } = require('../modules/TreeNode'); +const { printTree } = require('../modules/PrintUtil'); + +/* AVL tree */ +class AVLTree { + /* Constructor */ + constructor() { + this.root = null; // Root node + } + + /* Get node height */ + height(node) { + // Empty node height is -1, leaf node height is 0 + return node === null ? -1 : node.height; + } + + /* Update node height */ + #updateHeight(node) { + // Node height equals the height of the tallest subtree + 1 + node.height = + Math.max(this.height(node.left), this.height(node.right)) + 1; + } + + /* Get balance factor */ + balanceFactor(node) { + // Empty node balance factor is 0 + if (node === null) return 0; + // Node balance factor = left subtree height - right subtree height + return this.height(node.left) - this.height(node.right); + } + + /* Right rotation operation */ + #rightRotate(node) { + const child = node.left; + const grandChild = child.right; + // Using child as pivot, rotate node to the right + child.right = node; + node.left = grandChild; + // Update node height + this.#updateHeight(node); + this.#updateHeight(child); + // Return root node of subtree after rotation + return child; + } + + /* Left rotation operation */ + #leftRotate(node) { + const child = node.right; + const grandChild = child.left; + // Using child as pivot, rotate node to the left + child.left = node; + node.right = grandChild; + // Update node height + this.#updateHeight(node); + this.#updateHeight(child); + // Return root node of subtree after rotation + return child; + } + + /* Perform rotation operation to restore balance to this subtree */ + #rotate(node) { + // Get balance factor of node + const balanceFactor = this.balanceFactor(node); + // Left-leaning tree + if (balanceFactor > 1) { + if (this.balanceFactor(node.left) >= 0) { + // Right rotation + return this.#rightRotate(node); + } else { + // First left rotation then right rotation + node.left = this.#leftRotate(node.left); + return this.#rightRotate(node); + } + } + // Right-leaning tree + if (balanceFactor < -1) { + if (this.balanceFactor(node.right) <= 0) { + // Left rotation + return this.#leftRotate(node); + } else { + // First right rotation then left rotation + node.right = this.#rightRotate(node.right); + return this.#leftRotate(node); + } + } + // Balanced tree, no rotation needed, return directly + return node; + } + + /* Insert node */ + insert(val) { + this.root = this.#insertHelper(this.root, val); + } + + /* Recursively insert node (helper method) */ + #insertHelper(node, val) { + if (node === null) return new TreeNode(val); + /* 1. Find insertion position and insert node */ + if (val < node.val) node.left = this.#insertHelper(node.left, val); + else if (val > node.val) + node.right = this.#insertHelper(node.right, val); + else return node; // Duplicate node not inserted, return directly + this.#updateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = this.#rotate(node); + // Return root node of subtree + return node; + } + + /* Remove node */ + remove(val) { + this.root = this.#removeHelper(this.root, val); + } + + /* Recursively delete node (helper method) */ + #removeHelper(node, val) { + if (node === null) return null; + /* 1. Find node and delete */ + if (val < node.val) node.left = this.#removeHelper(node.left, val); + else if (val > node.val) + node.right = this.#removeHelper(node.right, val); + else { + if (node.left === null || node.right === null) { + const child = node.left !== null ? node.left : node.right; + // Number of child nodes = 0, delete node directly and return + if (child === null) return null; + // Number of child nodes = 1, delete node directly + else node = child; + } else { + // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + let temp = node.right; + while (temp.left !== null) { + temp = temp.left; + } + node.right = this.#removeHelper(node.right, temp.val); + node.val = temp.val; + } + } + this.#updateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = this.#rotate(node); + // Return root node of subtree + return node; + } + + /* Search node */ + search(val) { + let cur = this.root; + // Loop search, exit after passing leaf node + while (cur !== null) { + // Target node is in cur's right subtree + if (cur.val < val) cur = cur.right; + // Target node is in cur's left subtree + else if (cur.val > val) cur = cur.left; + // Found target node, exit loop + else break; + } + // Return target node + return cur; + } +} + +function testInsert(tree, val) { + tree.insert(val); + console.log('\nInsert node ' + val + ', AVL tree is'); + printTree(tree.root); +} + +function testRemove(tree, val) { + tree.remove(val); + console.log('\nRemove node ' + val + ', AVL tree is'); + printTree(tree.root); +} + +/* Driver Code */ +/* Please pay attention to how the AVL tree maintains balance after inserting nodes */ +const avlTree = new AVLTree(); +/* Insert node */ +// Delete nodes +testInsert(avlTree, 1); +testInsert(avlTree, 2); +testInsert(avlTree, 3); +testInsert(avlTree, 4); +testInsert(avlTree, 5); +testInsert(avlTree, 8); +testInsert(avlTree, 7); +testInsert(avlTree, 9); +testInsert(avlTree, 10); +testInsert(avlTree, 6); + +/* Please pay attention to how the AVL tree maintains balance after deleting nodes */ +testInsert(avlTree, 7); + +/* Remove node */ +// Delete node with degree 1 +testRemove(avlTree, 8); // Delete node with degree 2 +testRemove(avlTree, 5); // Remove node with degree 1 +testRemove(avlTree, 4); // Remove node with degree 2 + +/* Search node */ +const node = avlTree.search(7); +console.log('\nFound node object is', node, ', node value = ' + node.val); diff --git a/en/codes/javascript/chapter_tree/binary_search_tree.js b/en/codes/javascript/chapter_tree/binary_search_tree.js new file mode 100644 index 000000000..a262a6185 --- /dev/null +++ b/en/codes/javascript/chapter_tree/binary_search_tree.js @@ -0,0 +1,139 @@ +/** + * File: binary_search_tree.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +const { TreeNode } = require('../modules/TreeNode'); +const { printTree } = require('../modules/PrintUtil'); + +/* Binary search tree */ +class BinarySearchTree { + /* Constructor */ + constructor() { + // Initialize empty tree + this.root = null; + } + + /* Get binary tree root node */ + getRoot() { + return this.root; + } + + /* Search node */ + search(num) { + let cur = this.root; + // Loop search, exit after passing leaf node + while (cur !== null) { + // Target node is in cur's right subtree + if (cur.val < num) cur = cur.right; + // Target node is in cur's left subtree + else if (cur.val > num) cur = cur.left; + // Found target node, exit loop + else break; + } + // Return target node + return cur; + } + + /* Insert node */ + insert(num) { + // If tree is empty, initialize root node + if (this.root === null) { + this.root = new TreeNode(num); + return; + } + let cur = this.root, + pre = null; + // Loop search, exit after passing leaf node + while (cur !== null) { + // Found duplicate node, return directly + if (cur.val === num) return; + pre = cur; + // Insertion position is in cur's right subtree + if (cur.val < num) cur = cur.right; + // Insertion position is in cur's left subtree + else cur = cur.left; + } + // Insert node + const node = new TreeNode(num); + if (pre.val < num) pre.right = node; + else pre.left = node; + } + + /* Remove node */ + remove(num) { + // If tree is empty, return directly + if (this.root === null) return; + let cur = this.root, + pre = null; + // Loop search, exit after passing leaf node + while (cur !== null) { + // Found node to delete, exit loop + if (cur.val === num) break; + pre = cur; + // Node to delete is in cur's right subtree + if (cur.val < num) cur = cur.right; + // Node to delete is in cur's left subtree + else cur = cur.left; + } + // If no node to delete, return directly + if (cur === null) return; + // Number of child nodes = 0 or 1 + if (cur.left === null || cur.right === null) { + // When number of child nodes = 0 / 1, child = null / that child node + const child = cur.left !== null ? cur.left : cur.right; + // Delete node cur + if (cur !== this.root) { + if (pre.left === cur) pre.left = child; + else pre.right = child; + } else { + // If deleted node is root node, reassign root node + this.root = child; + } + } + // Number of child nodes = 2 + else { + // Get next node of cur in inorder traversal + let tmp = cur.right; + while (tmp.left !== null) { + tmp = tmp.left; + } + // Recursively delete node tmp + this.remove(tmp.val); + // Replace cur with tmp + cur.val = tmp.val; + } + } +} + +/* Driver Code */ +/* Initialize binary search tree */ +const bst = new BinarySearchTree(); +// Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree +const nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]; +for (const num of nums) { + bst.insert(num); +} +console.log('\nInitialized binary tree is\n'); +printTree(bst.getRoot()); + +/* Search node */ +const node = bst.search(7); +console.log('\nFound node object is ' + node + ', node value = ' + node.val); + +/* Insert node */ +bst.insert(16); +console.log('\nAfter inserting node 16, binary tree is\n'); +printTree(bst.getRoot()); + +/* Remove node */ +bst.remove(1); +console.log('\nAfter removing node 1, binary tree is\n'); +printTree(bst.getRoot()); +bst.remove(2); +console.log('\nAfter removing node 2, binary tree is\n'); +printTree(bst.getRoot()); +bst.remove(4); +console.log('\nAfter removing node 4, binary tree is\n'); +printTree(bst.getRoot()); diff --git a/en/codes/javascript/chapter_tree/binary_tree.js b/en/codes/javascript/chapter_tree/binary_tree.js new file mode 100644 index 000000000..136904084 --- /dev/null +++ b/en/codes/javascript/chapter_tree/binary_tree.js @@ -0,0 +1,35 @@ +/** + * File: binary_tree.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +const { TreeNode } = require('../modules/TreeNode'); +const { printTree } = require('../modules/PrintUtil'); + +/* Initialize binary tree */ +// Initialize nodes +let n1 = new TreeNode(1), + n2 = new TreeNode(2), + n3 = new TreeNode(3), + n4 = new TreeNode(4), + n5 = new TreeNode(5); +// Build references (pointers) between nodes +n1.left = n2; +n1.right = n3; +n2.left = n4; +n2.right = n5; +console.log('\nInitialize binary tree\n'); +printTree(n1); + +/* Insert node P between n1 -> n2 */ +const P = new TreeNode(0); +// Delete node +n1.left = P; +P.left = n2; +console.log('\nAfter inserting node P\n'); +printTree(n1); +// Remove node P +n1.left = n2; +console.log('\nAfter removing node P\n'); +printTree(n1); diff --git a/en/codes/javascript/chapter_tree/binary_tree_bfs.js b/en/codes/javascript/chapter_tree/binary_tree_bfs.js new file mode 100644 index 000000000..9751558bb --- /dev/null +++ b/en/codes/javascript/chapter_tree/binary_tree_bfs.js @@ -0,0 +1,34 @@ +/** + * File: binary_tree_bfs.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +const { arrToTree } = require('../modules/TreeNode'); +const { printTree } = require('../modules/PrintUtil'); + +/* Level-order traversal */ +function levelOrder(root) { + // Initialize queue, add root node + const queue = [root]; + // Initialize a list to save the traversal sequence + const list = []; + while (queue.length) { + let node = queue.shift(); // Dequeue + list.push(node.val); // Save node value + if (node.left) queue.push(node.left); // Left child node enqueue + if (node.right) queue.push(node.right); // Right child node enqueue + } + return list; +} + +/* Driver Code */ +/* Initialize binary tree */ +// Here we use a function to generate a binary tree directly from an array +const root = arrToTree([1, 2, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree\n'); +printTree(root); + +/* Level-order traversal */ +const list = levelOrder(root); +console.log('\nLevel-order traversal node print sequence = ' + list); diff --git a/en/codes/javascript/chapter_tree/binary_tree_dfs.js b/en/codes/javascript/chapter_tree/binary_tree_dfs.js new file mode 100644 index 000000000..df1e46a93 --- /dev/null +++ b/en/codes/javascript/chapter_tree/binary_tree_dfs.js @@ -0,0 +1,60 @@ +/** + * File: binary_tree_dfs.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +const { arrToTree } = require('../modules/TreeNode'); +const { printTree } = require('../modules/PrintUtil'); + +// Initialize list for storing traversal sequence +const list = []; + +/* Preorder traversal */ +function preOrder(root) { + if (root === null) return; + // Visit priority: root node -> left subtree -> right subtree + list.push(root.val); + preOrder(root.left); + preOrder(root.right); +} + +/* Inorder traversal */ +function inOrder(root) { + if (root === null) return; + // Visit priority: left subtree -> root node -> right subtree + inOrder(root.left); + list.push(root.val); + inOrder(root.right); +} + +/* Postorder traversal */ +function postOrder(root) { + if (root === null) return; + // Visit priority: left subtree -> right subtree -> root node + postOrder(root.left); + postOrder(root.right); + list.push(root.val); +} + +/* Driver Code */ +/* Initialize binary tree */ +// Here we use a function to generate a binary tree directly from an array +const root = arrToTree([1, 2, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree\n'); +printTree(root); + +/* Preorder traversal */ +list.length = 0; +preOrder(root); +console.log('\nPreorder traversal node print sequence = ' + list); + +/* Inorder traversal */ +list.length = 0; +inOrder(root); +console.log('\nInorder traversal node print sequence = ' + list); + +/* Postorder traversal */ +list.length = 0; +postOrder(root); +console.log('\nPostorder traversal node print sequence = ' + list); diff --git a/en/codes/javascript/modules/ListNode.js b/en/codes/javascript/modules/ListNode.js new file mode 100644 index 000000000..632ae8acf --- /dev/null +++ b/en/codes/javascript/modules/ListNode.js @@ -0,0 +1,31 @@ +/** + * File: ListNode.js + * Created Time: 2022-12-12 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +/* Linked list node */ +class ListNode { + val; // Node value + next; // Reference (pointer) to next node + constructor(val, next) { + this.val = val === undefined ? 0 : val; + this.next = next === undefined ? null : next; + } +} + +/* Deserialize a list into a linked list */ +function arrToLinkedList(arr) { + const dum = new ListNode(0); + let head = dum; + for (const val of arr) { + head.next = new ListNode(val); + head = head.next; + } + return dum.next; +} + +module.exports = { + ListNode, + arrToLinkedList, +}; diff --git a/en/codes/javascript/modules/PrintUtil.js b/en/codes/javascript/modules/PrintUtil.js new file mode 100644 index 000000000..f37c7ae4d --- /dev/null +++ b/en/codes/javascript/modules/PrintUtil.js @@ -0,0 +1,86 @@ +/** + * File: PrintUtil.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +const { arrToTree } = require('./TreeNode'); + +/* Print linked list */ +function printLinkedList(head) { + let list = []; + while (head !== null) { + list.push(head.val.toString()); + head = head.next; + } + console.log(list.join(' -> ')); +} + +function Trunk(prev, str) { + this.prev = prev; + this.str = str; +} + +/** + * Print binary tree + * This tree printer is borrowed from TECHIE DELIGHT + * https://www.techiedelight.com/c-program-print-binary-tree/ + */ +function printTree(root) { + printTree(root, null, false); +} + +/* Print binary tree */ +function printTree(root, prev, isRight) { + if (root === null) { + return; + } + + let prev_str = ' '; + let trunk = new Trunk(prev, prev_str); + + printTree(root.right, trunk, true); + + if (!prev) { + trunk.str = '———'; + } else if (isRight) { + trunk.str = '/———'; + prev_str = ' |'; + } else { + trunk.str = '\\———'; + prev.str = prev_str; + } + + showTrunks(trunk); + console.log(' ' + root.val); + + if (prev) { + prev.str = prev_str; + } + trunk.str = ' |'; + + printTree(root.left, trunk, false); +} + +function showTrunks(p) { + if (!p) { + return; + } + + showTrunks(p.prev); + process.stdout.write(p.str); +} + +/* Print heap */ +function printHeap(arr) { + console.log('Heap array representation:'); + console.log(arr); + console.log('Heap tree representation:'); + printTree(arrToTree(arr)); +} + +module.exports = { + printLinkedList, + printTree, + printHeap, +}; diff --git a/en/codes/javascript/modules/TreeNode.js b/en/codes/javascript/modules/TreeNode.js new file mode 100644 index 000000000..b1a35e86c --- /dev/null +++ b/en/codes/javascript/modules/TreeNode.js @@ -0,0 +1,35 @@ +/** + * File: TreeNode.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +/* Binary tree node */ +class TreeNode { + val; // Node value + left; // Left child pointer + right; // Right child pointer + height; // Node height + constructor(val, left, right, height) { + this.val = val === undefined ? 0 : val; + this.left = left === undefined ? null : left; + this.right = right === undefined ? null : right; + this.height = height === undefined ? 0 : height; + } +} + +/* Deserialize array to binary tree */ +function arrToTree(arr, i = 0) { + if (i < 0 || i >= arr.length || arr[i] === null) { + return null; + } + let root = new TreeNode(arr[i]); + root.left = arrToTree(arr, 2 * i + 1); + root.right = arrToTree(arr, 2 * i + 2); + return root; +} + +module.exports = { + TreeNode, + arrToTree, +}; diff --git a/en/codes/javascript/modules/Vertex.js b/en/codes/javascript/modules/Vertex.js new file mode 100644 index 000000000..59c285853 --- /dev/null +++ b/en/codes/javascript/modules/Vertex.js @@ -0,0 +1,35 @@ +/** + * File: Vertex.js + * Created Time: 2023-02-15 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +/* Vertex class */ +class Vertex { + val; + constructor(val) { + this.val = val; + } + + /* Input value list vals, return vertex list vets */ + static valsToVets(vals) { + const vets = []; + for (let i = 0; i < vals.length; i++) { + vets[i] = new Vertex(vals[i]); + } + return vets; + } + + /* Input vertex list vets, return value list vals */ + static vetsToVals(vets) { + const vals = []; + for (const vet of vets) { + vals.push(vet.val); + } + return vals; + } +} + +module.exports = { + Vertex, +}; diff --git a/en/codes/javascript/test_all.js b/en/codes/javascript/test_all.js new file mode 100644 index 000000000..b43ef49f5 --- /dev/null +++ b/en/codes/javascript/test_all.js @@ -0,0 +1,63 @@ +import { bold, brightRed } from 'jsr:@std/fmt/colors'; +import { expandGlob } from 'jsr:@std/fs'; +import { relative, resolve } from 'jsr:@std/path'; + +/** + * @typedef {import('jsr:@std/fs').WalkEntry} WalkEntry + * @type {WalkEntry[]} + */ +const entries = []; + +for await (const entry of expandGlob( + resolve(import.meta.dirname, './chapter_*/*.js') +)) { + entries.push(entry); +} + +/** @type {{ status: Promise; stderr: ReadableStream; }[]} */ +const processes = []; + +for (const file of entries) { + const execute = new Deno.Command('node', { + args: [relative(import.meta.dirname, file.path)], + cwd: import.meta.dirname, + stdin: 'piped', + stdout: 'piped', + stderr: 'piped', + }); + + const process = execute.spawn(); + processes.push({ status: process.status, stderr: process.stderr }); +} + +const results = await Promise.all( + processes.map(async (item) => { + const status = await item.status; + return { status, stderr: item.stderr }; + }) +); + +/** @type {ReadableStream[]} */ +const errors = []; + +for (const result of results) { + if (!result.status.success) { + errors.push(result.stderr); + } +} + +console.log(`Tested ${entries.length} files`); +console.log(`Found exception in ${errors.length} files`); + +if (errors.length) { + console.log(); + + for (const error of errors) { + const reader = error.getReader(); + const { value } = await reader.read(); + const decoder = new TextDecoder(); + console.log(`${bold(brightRed('error'))}: ${decoder.decode(value)}`); + } + + throw new Error('Test failed'); +} diff --git a/en/codes/kotlin/chapter_array_and_linkedlist/array.kt b/en/codes/kotlin/chapter_array_and_linkedlist/array.kt new file mode 100644 index 000000000..fdbcf7be4 --- /dev/null +++ b/en/codes/kotlin/chapter_array_and_linkedlist/array.kt @@ -0,0 +1,102 @@ +/** + * File: array.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_array_and_linkedlist + +import java.util.concurrent.ThreadLocalRandom + +/* Random access to element */ +fun randomAccess(nums: IntArray): Int { + // Randomly select a number in interval [0, nums.size) + val randomIndex = ThreadLocalRandom.current().nextInt(0, nums.size) + // Retrieve and return the random element + val randomNum = nums[randomIndex] + return randomNum +} + +/* Extend array length */ +fun extend(nums: IntArray, enlarge: Int): IntArray { + // Initialize an array with extended length + val res = IntArray(nums.size + enlarge) + // Copy all elements from the original array to the new array + for (i in nums.indices) { + res[i] = nums[i] + } + // Return the extended new array + return res +} + +/* Insert element num at index index in the array */ +fun insert(nums: IntArray, num: Int, index: Int) { + // Move all elements at and after index index backward by one position + for (i in nums.size - 1 downTo index + 1) { + nums[i] = nums[i - 1] + } + // Assign num to the element at index index + nums[index] = num +} + +/* Remove the element at index index */ +fun remove(nums: IntArray, index: Int) { + // Move all elements after index index forward by one position + for (i in index.. P -> n1 + val p = n0.next + val n1 = p?.next + n0.next = n1 +} + +/* Access the node at index index in the linked list */ +fun access(head: ListNode?, index: Int): ListNode? { + var h = head + for (i in 0..= size) + throw IndexOutOfBoundsException("Index out of bounds") + return arr[index] + } + + /* Add elements at the end */ + fun set(index: Int, num: Int) { + if (index < 0 || index >= size) + throw IndexOutOfBoundsException("Index out of bounds") + arr[index] = num + } + + /* Direct traversal of list elements */ + fun add(num: Int) { + // When the number of elements exceeds capacity, trigger the extension mechanism + if (size == capacity()) + extendCapacity() + arr[size] = num + // Update the number of elements + size++ + } + + /* Sort list */ + fun insert(index: Int, num: Int) { + if (index < 0 || index >= size) + throw IndexOutOfBoundsException("Index out of bounds") + // When the number of elements exceeds capacity, trigger the extension mechanism + if (size == capacity()) + extendCapacity() + // Move all elements after index index forward by one position + for (j in size - 1 downTo index) + arr[j + 1] = arr[j] + arr[index] = num + // Update the number of elements + size++ + } + + /* Remove element */ + fun remove(index: Int): Int { + if (index < 0 || index >= size) + throw IndexOutOfBoundsException("Index out of bounds") + val num = arr[index] + // Move all elements after index forward by one position + for (j in index..>, + res: MutableList>?>, + cols: BooleanArray, + diags1: BooleanArray, + diags2: BooleanArray +) { + // When all rows are placed, record the solution + if (row == n) { + val copyState = mutableListOf>() + for (sRow in state) { + copyState.add(sRow.toMutableList()) + } + res.add(copyState) + return + } + // Traverse all columns + for (col in 0..>?> { + // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + val state = mutableListOf>() + for (i in 0..() + for (j in 0..>?>() + + backtrack(0, n, state, res, cols, diags1, diags2) + + return res +} + +/* Driver Code */ +fun main() { + val n = 4 + val res = nQueens(n) + + println("Input board size is $n") + println("Total queen placement solutions: ${res.size}") + for (state in res) { + println("--------------------") + for (row in state!!) { + println(row) + } + } +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_backtracking/permutations_i.kt b/en/codes/kotlin/chapter_backtracking/permutations_i.kt new file mode 100644 index 000000000..263229f82 --- /dev/null +++ b/en/codes/kotlin/chapter_backtracking/permutations_i.kt @@ -0,0 +1,53 @@ +/** + * File: permutations_i.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_backtracking.permutations_i + +/* Backtracking algorithm: Permutations I */ +fun backtrack( + state: MutableList, + choices: IntArray, + selected: BooleanArray, + res: MutableList?> +) { + // When the state length equals the number of elements, record the solution + if (state.size == choices.size) { + res.add(state.toMutableList()) + return + } + // Traverse all choices + for (i in choices.indices) { + val choice = choices[i] + // Pruning: do not allow repeated selection of elements + if (!selected[i]) { + // Attempt: make choice, update state + selected[i] = true + state.add(choice) + // Proceed to the next round of selection + backtrack(state, choices, selected, res) + // Backtrack: undo choice, restore to previous state + selected[i] = false + state.removeAt(state.size - 1) + } + } +} + +/* Permutations I */ +fun permutationsI(nums: IntArray): MutableList?> { + val res = mutableListOf?>() + backtrack(mutableListOf(), nums, BooleanArray(nums.size), res) + return res +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(1, 2, 3) + + val res = permutationsI(nums) + + println("Input array nums = ${nums.contentToString()}") + println("All permutations res = $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_backtracking/permutations_ii.kt b/en/codes/kotlin/chapter_backtracking/permutations_ii.kt new file mode 100644 index 000000000..26c6e3a85 --- /dev/null +++ b/en/codes/kotlin/chapter_backtracking/permutations_ii.kt @@ -0,0 +1,54 @@ +/** + * File: permutations_ii.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_backtracking.permutations_ii + +/* Backtracking algorithm: Permutations II */ +fun backtrack( + state: MutableList, + choices: IntArray, + selected: BooleanArray, + res: MutableList?> +) { + // When the state length equals the number of elements, record the solution + if (state.size == choices.size) { + res.add(state.toMutableList()) + return + } + // Traverse all choices + val duplicated = HashSet() + for (i in choices.indices) { + val choice = choices[i] + // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if (!selected[i] && !duplicated.contains(choice)) { + // Attempt: make choice, update state + duplicated.add(choice) // Record the selected element value + selected[i] = true + state.add(choice) + // Proceed to the next round of selection + backtrack(state, choices, selected, res) + // Backtrack: undo choice, restore to previous state + selected[i] = false + state.removeAt(state.size - 1) + } + } +} + +/* Permutations II */ +fun permutationsII(nums: IntArray): MutableList?> { + val res = mutableListOf?>() + backtrack(mutableListOf(), nums, BooleanArray(nums.size), res) + return res +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(1, 2, 2) + val res = permutationsII(nums) + + println("Input array nums = ${nums.contentToString()}") + println("All permutations res = $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_backtracking/preorder_traversal_i_compact.kt b/en/codes/kotlin/chapter_backtracking/preorder_traversal_i_compact.kt new file mode 100644 index 000000000..cf3ff83dc --- /dev/null +++ b/en/codes/kotlin/chapter_backtracking/preorder_traversal_i_compact.kt @@ -0,0 +1,43 @@ +/** + * File: preorder_traversal_i_compact.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_backtracking.preorder_traversal_i_compact + +import utils.TreeNode +import utils.printTree + +var res: MutableList? = null + +/* Preorder traversal: Example 1 */ +fun preOrder(root: TreeNode?) { + if (root == null) { + return + } + if (root._val == 7) { + // Record solution + res!!.add(root) + } + preOrder(root.left) + preOrder(root.right) +} + +/* Driver Code */ +fun main() { + val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7)) + println("\nInitialize binary tree") + printTree(root) + + // Preorder traversal + res = mutableListOf() + preOrder(root) + + println("\nOutput all nodes with value 7") + val vals = mutableListOf() + for (node in res!!) { + vals.add(node._val) + } + println(vals) +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_backtracking/preorder_traversal_ii_compact.kt b/en/codes/kotlin/chapter_backtracking/preorder_traversal_ii_compact.kt new file mode 100644 index 000000000..ef1beb258 --- /dev/null +++ b/en/codes/kotlin/chapter_backtracking/preorder_traversal_ii_compact.kt @@ -0,0 +1,51 @@ +/** + * File: preorder_traversal_ii_compact.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_backtracking.preorder_traversal_ii_compact + +import utils.TreeNode +import utils.printTree + +var path: MutableList? = null +var res: MutableList>? = null + +/* Preorder traversal: Example 2 */ +fun preOrder(root: TreeNode?) { + if (root == null) { + return + } + // Attempt + path!!.add(root) + if (root._val == 7) { + // Record solution + res!!.add(path!!.toMutableList()) + } + preOrder(root.left) + preOrder(root.right) + // Backtrack + path!!.removeAt(path!!.size - 1) +} + +/* Driver Code */ +fun main() { + val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7)) + println("\nInitialize binary tree") + printTree(root) + + // Preorder traversal + path = mutableListOf() + res = mutableListOf() + preOrder(root) + + println("\nOutput all paths from root node to node 7") + for (path in res!!) { + val _vals = mutableListOf() + for (node in path) { + _vals.add(node._val) + } + println(_vals) + } +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_compact.kt b/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_compact.kt new file mode 100644 index 000000000..8be3543e9 --- /dev/null +++ b/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_compact.kt @@ -0,0 +1,52 @@ +/** + * File: preorder_traversal_iii_compact.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_backtracking.preorder_traversal_iii_compact + +import utils.TreeNode +import utils.printTree + +var path: MutableList? = null +var res: MutableList>? = null + +/* Preorder traversal: Example 3 */ +fun preOrder(root: TreeNode?) { + // Pruning + if (root == null || root._val == 3) { + return + } + // Attempt + path!!.add(root) + if (root._val == 7) { + // Record solution + res!!.add(path!!.toMutableList()) + } + preOrder(root.left) + preOrder(root.right) + // Backtrack + path!!.removeAt(path!!.size - 1) +} + +/* Driver Code */ +fun main() { + val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7)) + println("\nInitialize binary tree") + printTree(root) + + // Preorder traversal + path = mutableListOf() + res = mutableListOf() + preOrder(root) + + println("\nOutput all paths from root node to node 7, paths do not include nodes with value 3") + for (path in res!!) { + val _vals = mutableListOf() + for (node in path) { + _vals.add(node._val) + } + println(_vals) + } +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_template.kt b/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_template.kt new file mode 100644 index 000000000..70e241677 --- /dev/null +++ b/en/codes/kotlin/chapter_backtracking/preorder_traversal_iii_template.kt @@ -0,0 +1,82 @@ +/** + * File: preorder_traversal_iii_template.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_backtracking.preorder_traversal_iii_template + +import utils.TreeNode +import utils.printTree + +/* Check if the current state is a solution */ +fun isSolution(state: MutableList): Boolean { + return state.isNotEmpty() && state[state.size - 1]?._val == 7 +} + +/* Record solution */ +fun recordSolution(state: MutableList?, res: MutableList?>) { + res.add(state!!.toMutableList()) +} + +/* Check if the choice is valid under the current state */ +fun isValid(state: MutableList?, choice: TreeNode?): Boolean { + return choice != null && choice._val != 3 +} + +/* Update state */ +fun makeChoice(state: MutableList, choice: TreeNode?) { + state.add(choice) +} + +/* Restore state */ +fun undoChoice(state: MutableList, choice: TreeNode?) { + state.removeLast() +} + +/* Backtracking algorithm: Example 3 */ +fun backtrack( + state: MutableList, + choices: MutableList, + res: MutableList?> +) { + // Check if it is a solution + if (isSolution(state)) { + // Record solution + recordSolution(state, res) + } + // Traverse all choices + for (choice in choices) { + // Pruning: check if the choice is valid + if (isValid(state, choice)) { + // Attempt: make choice, update state + makeChoice(state, choice) + // Proceed to the next round of selection + backtrack(state, mutableListOf(choice!!.left, choice.right), res) + // Backtrack: undo choice, restore to previous state + undoChoice(state, choice) + } + } +} + +/* Driver Code */ +fun main() { + val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7)) + println("\nInitialize binary tree") + printTree(root) + + // Backtracking algorithm + val res = mutableListOf?>() + backtrack(mutableListOf(), mutableListOf(root), res) + + println("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3") + for (path in res) { + val vals = mutableListOf() + for (node in path!!) { + if (node != null) { + vals.add(node._val) + } + } + println(vals) + } +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_backtracking/subset_sum_i.kt b/en/codes/kotlin/chapter_backtracking/subset_sum_i.kt new file mode 100644 index 000000000..fcb7f2c94 --- /dev/null +++ b/en/codes/kotlin/chapter_backtracking/subset_sum_i.kt @@ -0,0 +1,58 @@ +/** + * File: subset_sum_i.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_backtracking.subset_sum_i + +/* Backtracking algorithm: Subset sum I */ +fun backtrack( + state: MutableList, + target: Int, + choices: IntArray, + start: Int, + res: MutableList?> +) { + // When the subset sum equals target, record the solution + if (target == 0) { + res.add(state.toMutableList()) + return + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + for (i in start..?> { + val state = mutableListOf() // State (subset) + nums.sort() // Sort nums + val start = 0 // Start point for traversal + val res = mutableListOf?>() // Result list (subset list) + backtrack(state, target, nums, start, res) + return res +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(3, 4, 5) + val target = 9 + + val res = subsetSumI(nums, target) + + println("Input array nums = ${nums.contentToString()}, target = $target") + println("All subsets with sum equal to $target res = $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_backtracking/subset_sum_i_naive.kt b/en/codes/kotlin/chapter_backtracking/subset_sum_i_naive.kt new file mode 100644 index 000000000..c44a37a4b --- /dev/null +++ b/en/codes/kotlin/chapter_backtracking/subset_sum_i_naive.kt @@ -0,0 +1,55 @@ +/** + * File: subset_sum_i_native.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_backtracking.subset_sum_i_naive + +/* Backtracking algorithm: Subset sum I */ +fun backtrack( + state: MutableList, + target: Int, + total: Int, + choices: IntArray, + res: MutableList?> +) { + // When the subset sum equals target, record the solution + if (total == target) { + res.add(state.toMutableList()) + return + } + // Traverse all choices + for (i in choices.indices) { + // Pruning: if the subset sum exceeds target, skip this choice + if (total + choices[i] > target) { + continue + } + // Attempt: make choice, update element sum total + state.add(choices[i]) + // Proceed to the next round of selection + backtrack(state, target, total + choices[i], choices, res) + // Backtrack: undo choice, restore to previous state + state.removeAt(state.size - 1) + } +} + +/* Solve subset sum I (including duplicate subsets) */ +fun subsetSumINaive(nums: IntArray, target: Int): MutableList?> { + val state = mutableListOf() // State (subset) + val total = 0 // Subset sum + val res = mutableListOf?>() // Result list (subset list) + backtrack(state, target, total, nums, res) + return res +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(3, 4, 5) + val target = 9 + val res = subsetSumINaive(nums, target) + + println("Input array nums = ${nums.contentToString()}, target = $target") + println("All subsets with sum equal to $target res = $res") + println("Please note that this method outputs results containing duplicate sets") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_backtracking/subset_sum_ii.kt b/en/codes/kotlin/chapter_backtracking/subset_sum_ii.kt new file mode 100644 index 000000000..77bb910da --- /dev/null +++ b/en/codes/kotlin/chapter_backtracking/subset_sum_ii.kt @@ -0,0 +1,62 @@ +/** + * File: subset_sum_ii.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_backtracking.subset_sum_ii + +/* Backtracking algorithm: Subset sum II */ +fun backtrack( + state: MutableList, + target: Int, + choices: IntArray, + start: Int, + res: MutableList?> +) { + // When the subset sum equals target, record the solution + if (target == 0) { + res.add(state.toMutableList()) + return + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + for (i in start.. start && choices[i] == choices[i - 1]) { + continue + } + // Attempt: make choice, update target, start + state.add(choices[i]) + // Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i + 1, res) + // Backtrack: undo choice, restore to previous state + state.removeAt(state.size - 1) + } +} + +/* Solve subset sum II */ +fun subsetSumII(nums: IntArray, target: Int): MutableList?> { + val state = mutableListOf() // State (subset) + nums.sort() // Sort nums + val start = 0 // Start point for traversal + val res = mutableListOf?>() // Result list (subset list) + backtrack(state, target, nums, start, res) + return res +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(4, 4, 5) + val target = 9 + val res = subsetSumII(nums, target) + + println("Input array nums = ${nums.contentToString()}, target = $target") + println("All subsets with sum equal to $target res = $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_computational_complexity/iteration.kt b/en/codes/kotlin/chapter_computational_complexity/iteration.kt new file mode 100644 index 000000000..b8a9143c4 --- /dev/null +++ b/en/codes/kotlin/chapter_computational_complexity/iteration.kt @@ -0,0 +1,74 @@ +/** + * File: iteration.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_computational_complexity.iteration + +/* for loop */ +fun forLoop(n: Int): Int { + var res = 0 + // Sum 1, 2, ..., n-1, n + for (i in 1..n) { + res += i + } + return res +} + +/* while loop */ +fun whileLoop(n: Int): Int { + var res = 0 + var i = 1 // Initialize condition variable + // Sum 1, 2, ..., n-1, n + while (i <= n) { + res += i + i++ // Update condition variable + } + return res +} + +/* while loop (two updates) */ +fun whileLoopII(n: Int): Int { + var res = 0 + var i = 1 // Initialize condition variable + // Sum 1, 4, 10, ... + while (i <= n) { + res += i + // Update condition variable + i++ + i *= 2 + } + return res +} + +/* Nested for loop */ +fun nestedForLoop(n: Int): String { + val res = StringBuilder() + // Loop i = 1, 2, ..., n-1, n + for (i in 1..n) { + // Loop j = 1, 2, ..., n-1, n + for (j in 1..n) { + res.append(" ($i, $j), ") + } + } + return res.toString() +} + +/* Driver Code */ +fun main() { + val n = 5 + var res: Int + + res = forLoop(n) + println("\nFor loop sum result res = $res") + + res = whileLoop(n) + println("\nWhile loop sum result res = $res") + + res = whileLoopII(n) + println("\nWhile loop (two updates) sum result res = $res") + + val resStr = nestedForLoop(n) + println("\nNested for loop traversal result $resStr") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_computational_complexity/recursion.kt b/en/codes/kotlin/chapter_computational_complexity/recursion.kt new file mode 100644 index 000000000..ac7ac707f --- /dev/null +++ b/en/codes/kotlin/chapter_computational_complexity/recursion.kt @@ -0,0 +1,78 @@ +/** + * File: recursion.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_computational_complexity.recursion + +import java.util.* + +/* Recursion */ +fun recur(n: Int): Int { + // Termination condition + if (n == 1) + return 1 + // Descend: recursive call + val res = recur(n - 1) + // Return: return result + return n + res +} + +/* Simulate recursion using iteration */ +fun forLoopRecur(n: Int): Int { + // Use an explicit stack to simulate the system call stack + val stack = Stack() + var res = 0 + // Descend: recursive call + for (i in n downTo 0) { + // Simulate "recurse" with "push" + stack.push(i) + } + // Return: return result + while (stack.isNotEmpty()) { + // Simulate "return" with "pop" + res += stack.pop() + } + // res = 1+2+3+...+n + return res +} + +/* Tail recursion */ +tailrec fun tailRecur(n: Int, res: Int): Int { + // Add tailrec keyword to enable tail recursion optimization + // Termination condition + if (n == 0) + return res + // Tail recursive call + return tailRecur(n - 1, res + n) +} + +/* Fibonacci sequence: recursion */ +fun fib(n: Int): Int { + // Termination condition f(1) = 0, f(2) = 1 + if (n == 1 || n == 2) + return n - 1 + // Recursive call f(n) = f(n-1) + f(n-2) + val res = fib(n - 1) + fib(n - 2) + // Return result f(n) + return res +} + +/* Driver Code */ +fun main() { + val n = 5 + var res: Int + + res = recur(n) + println("\nRecursion sum result res = $res") + + res = forLoopRecur(n) + println("\nUsing iteration to simulate recursion sum result res = $res") + + res = tailRecur(n, 0) + println("\nTail recursion sum result res = $res") + + res = fib(n) + println("\nThe ${n}th Fibonacci number is $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_computational_complexity/space_complexity.kt b/en/codes/kotlin/chapter_computational_complexity/space_complexity.kt new file mode 100644 index 000000000..849dcb6a0 --- /dev/null +++ b/en/codes/kotlin/chapter_computational_complexity/space_complexity.kt @@ -0,0 +1,109 @@ +/** + * File: space_complexity.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_computational_complexity.space_complexity + +import utils.ListNode +import utils.TreeNode +import utils.printTree + +/* Function */ +fun function(): Int { + // Perform some operations + return 0 +} + +/* Constant order */ +fun constant(n: Int) { + // Constants, variables, objects occupy O(1) space + val a = 0 + var b = 0 + val nums = Array(10000) { 0 } + val node = ListNode(0) + // Variables in the loop occupy O(1) space + for (i in 0..() + for (i in 0..() + for (i in 0..?>(n) + // 2D list uses O(n^2) space + val numList = mutableListOf>() + for (i in 0..() + for (j in 0.. nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + val temp = nums[j] + nums[j] = nums[j + 1] + nums[j + 1] = temp + count += 3 // Element swap includes 3 unit operations + } + } + } + return count +} + +/* Exponential order (loop implementation) */ +fun exponential(n: Int): Int { + var count = 0 + var base = 1 + // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + for (i in 0.. 1) { + n1 /= 2 + count++ + } + return count +} + +/* Logarithmic order (recursive implementation) */ +fun logRecur(n: Int): Int { + if (n <= 1) + return 0 + return logRecur(n / 2) + 1 +} + +/* Linearithmic order */ +fun linearLogRecur(n: Int): Int { + if (n <= 1) + return 1 + var count = linearLogRecur(n / 2) + linearLogRecur(n / 2) + for (i in 0.. { + val nums = IntArray(n) + // Generate array nums = { 1, 2, 3, ..., n } + for (i in 0..(n) + for (i in 0..): Int { + for (i in nums.indices) { + // When element 1 is at the head of the array, best time complexity O(1) is achieved + // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + if (nums[i] == 1) + return i + } + return -1 +} + +/* Driver Code */ +fun main() { + for (i in 0..9) { + val n = 100 + val nums = randomNumbers(n) + val index = findOne(nums) + println("\nArray [ 1, 2, ..., n ] after shuffling = ${nums.contentToString()}") + println("Index of number 1 is $index") + } +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_divide_and_conquer/binary_search_recur.kt b/en/codes/kotlin/chapter_divide_and_conquer/binary_search_recur.kt new file mode 100644 index 000000000..2e8ec02df --- /dev/null +++ b/en/codes/kotlin/chapter_divide_and_conquer/binary_search_recur.kt @@ -0,0 +1,49 @@ +/** + * File: binary_search_recur.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_divide_and_conquer.binary_search_recur + +/* Binary search: problem f(i, j) */ +fun dfs( + nums: IntArray, + target: Int, + i: Int, + j: Int +): Int { + // If the interval is empty, it means there is no target element, return -1 + if (i > j) { + return -1 + } + // Calculate the midpoint index m + val m = (i + j) / 2 + return if (nums[m] < target) { + // Recursion subproblem f(m+1, j) + dfs(nums, target, m + 1, j) + } else if (nums[m] > target) { + // Recursion subproblem f(i, m-1) + dfs(nums, target, i, m - 1) + } else { + // Found the target element, return its index + m + } +} + +/* Binary search */ +fun binarySearch(nums: IntArray, target: Int): Int { + val n = nums.size + // Solve the problem f(0, n-1) + return dfs(nums, target, 0, n - 1) +} + +/* Driver Code */ +fun main() { + val target = 6 + val nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35) + + // Binary search (closed interval on both sides) + val index = binarySearch(nums, target) + println("Index of target element 6 = $index") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_divide_and_conquer/build_tree.kt b/en/codes/kotlin/chapter_divide_and_conquer/build_tree.kt new file mode 100644 index 000000000..b07142f18 --- /dev/null +++ b/en/codes/kotlin/chapter_divide_and_conquer/build_tree.kt @@ -0,0 +1,55 @@ +/** + * File: build_tree.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_divide_and_conquer.build_tree + +import utils.TreeNode +import utils.printTree + +/* Build binary tree: divide and conquer */ +fun dfs( + preorder: IntArray, + inorderMap: Map, + i: Int, + l: Int, + r: Int +): TreeNode? { + // Terminate when the subtree interval is empty + if (r - l < 0) return null + // Initialize the root node + val root = TreeNode(preorder[i]) + // Query m to divide the left and right subtrees + val m = inorderMap[preorder[i]]!! + // Subproblem: build the left subtree + root.left = dfs(preorder, inorderMap, i + 1, l, m - 1) + // Subproblem: build the right subtree + root.right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r) + // Return the root node + return root +} + +/* Build binary tree */ +fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { + // Initialize hash map, storing the mapping from inorder elements to indices + val inorderMap = HashMap() + for (i in inorder.indices) { + inorderMap[inorder[i]] = i + } + val root = dfs(preorder, inorderMap, 0, 0, inorder.size - 1) + return root +} + +/* Driver Code */ +fun main() { + val preorder = intArrayOf(3, 9, 2, 1, 7) + val inorder = intArrayOf(9, 3, 1, 2, 7) + println("Pre-order traversal = ${preorder.contentToString()}") + println("In-order traversal = ${inorder.contentToString()}") + + val root = buildTree(preorder, inorder) + println("The constructed binary tree is:") + printTree(root) +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_divide_and_conquer/hanota.kt b/en/codes/kotlin/chapter_divide_and_conquer/hanota.kt new file mode 100644 index 000000000..81ee8db2b --- /dev/null +++ b/en/codes/kotlin/chapter_divide_and_conquer/hanota.kt @@ -0,0 +1,56 @@ +/** + * File: hanota.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_divide_and_conquer.hanota + +/* Move a disk */ +fun move(src: MutableList, tar: MutableList) { + // Take out a disk from the top of src + val pan = src.removeAt(src.size - 1) + // Place the disk on top of tar + tar.add(pan) +} + +/* Solve the Tower of Hanoi problem f(i) */ +fun dfs(i: Int, src: MutableList, buf: MutableList, tar: MutableList) { + // If there is only one disk left in src, move it directly to tar + if (i == 1) { + move(src, tar) + return + } + // Subproblem f(i-1): move the top i-1 disks from src to buf using tar + dfs(i - 1, src, tar, buf) + // Subproblem f(1): move the remaining disk from src to tar + move(src, tar) + // Subproblem f(i-1): move the top i-1 disks from buf to tar using src + dfs(i - 1, buf, src, tar) +} + +/* Solve the Tower of Hanoi problem */ +fun solveHanota(A: MutableList, B: MutableList, C: MutableList) { + val n = A.size + // Move the top n disks from A to C using B + dfs(n, A, B, C) +} + +/* Driver Code */ +fun main() { + // The tail of the list is the top of the rod + val A = mutableListOf(5, 4, 3, 2, 1) + val B = mutableListOf() + val C = mutableListOf() + println("In initial state:") + println("A = $A") + println("B = $B") + println("C = $C") + + solveHanota(A, B, C) + + println("After disk movement is complete:") + println("A = $A") + println("B = $B") + println("C = $C") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_backtrack.kt b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_backtrack.kt new file mode 100644 index 000000000..8fee13f88 --- /dev/null +++ b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_backtrack.kt @@ -0,0 +1,45 @@ +/** + * File: climbing_stairs_backtrack.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +/* Backtracking */ +fun backtrack( + choices: MutableList, + state: Int, + n: Int, + res: MutableList +) { + // When climbing to the n-th stair, add 1 to the solution count + if (state == n) + res[0] = res[0] + 1 + // Traverse all choices + for (choice in choices) { + // Pruning: not allowed to go beyond the n-th stair + if (state + choice > n) continue + // Attempt: make choice, update state + backtrack(choices, state + choice, n, res) + // Backtrack + } +} + +/* Climbing stairs: Backtracking */ +fun climbingStairsBacktrack(n: Int): Int { + val choices = mutableListOf(1, 2) // Can choose to climb up 1 or 2 stairs + val state = 0 // Start climbing from the 0-th stair + val res = mutableListOf() + res.add(0) // Use res[0] to record the solution count + backtrack(choices, state, n, res) + return res[0] +} + +/* Driver Code */ +fun main() { + val n = 9 + + val res = climbingStairsBacktrack(n) + println("Climbing $n stairs has $res solutions") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_constraint_dp.kt b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_constraint_dp.kt new file mode 100644 index 000000000..a1e686e9d --- /dev/null +++ b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_constraint_dp.kt @@ -0,0 +1,35 @@ +/** + * File: climbing_stairs_constraint_dp.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +/* Climbing stairs with constraint: Dynamic programming */ +fun climbingStairsConstraintDP(n: Int): Int { + if (n == 1 || n == 2) { + return 1 + } + // Initialize dp table, used to store solutions to subproblems + val dp = Array(n + 1) { IntArray(3) } + // Initial state: preset the solution to the smallest subproblem + dp[1][1] = 1 + dp[1][2] = 0 + dp[2][1] = 0 + dp[2][2] = 1 + // State transition: gradually solve larger subproblems from smaller ones + for (i in 3..n) { + dp[i][1] = dp[i - 1][2] + dp[i][2] = dp[i - 2][1] + dp[i - 2][2] + } + return dp[n][1] + dp[n][2] +} + +/* Driver Code */ +fun main() { + val n = 9 + + val res = climbingStairsConstraintDP(n) + println("Climbing $n stairs has $res solutions") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs.kt b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs.kt new file mode 100644 index 000000000..c3a50470b --- /dev/null +++ b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs.kt @@ -0,0 +1,29 @@ +/** + * File: climbing_stairs_dfs.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +/* Search */ +fun dfs(i: Int): Int { + // Known dp[1] and dp[2], return them + if (i == 1 || i == 2) return i + // dp[i] = dp[i-1] + dp[i-2] + val count = dfs(i - 1) + dfs(i - 2) + return count +} + +/* Climbing stairs: Search */ +fun climbingStairsDFS(n: Int): Int { + return dfs(n) +} + +/* Driver Code */ +fun main() { + val n = 9 + + val res = climbingStairsDFS(n) + println("Climbing $n stairs has $res solutions") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs_mem.kt b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs_mem.kt new file mode 100644 index 000000000..0ba68dbc2 --- /dev/null +++ b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs_mem.kt @@ -0,0 +1,36 @@ +/** + * File: climbing_stairs_dfs_mem.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +/* Memoization search */ +fun dfs(i: Int, mem: IntArray): Int { + // Known dp[1] and dp[2], return them + if (i == 1 || i == 2) return i + // If record dp[i] exists, return it directly + if (mem[i] != -1) return mem[i] + // dp[i] = dp[i-1] + dp[i-2] + val count = dfs(i - 1, mem) + dfs(i - 2, mem) + // Record dp[i] + mem[i] = count + return count +} + +/* Climbing stairs: Memoization search */ +fun climbingStairsDFSMem(n: Int): Int { + // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + val mem = IntArray(n + 1) + mem.fill(-1) + return dfs(n, mem) +} + +/* Driver Code */ +fun main() { + val n = 9 + + val res = climbingStairsDFSMem(n) + println("Climbing $n stairs has $res solutions") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dp.kt b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dp.kt new file mode 100644 index 000000000..f6a4faf77 --- /dev/null +++ b/en/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dp.kt @@ -0,0 +1,46 @@ +/** + * File: climbing_stairs_dp.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +/* Climbing stairs: Dynamic programming */ +fun climbingStairsDP(n: Int): Int { + if (n == 1 || n == 2) return n + // Initialize dp table, used to store solutions to subproblems + val dp = IntArray(n + 1) + // Initial state: preset the solution to the smallest subproblem + dp[1] = 1 + dp[2] = 2 + // State transition: gradually solve larger subproblems from smaller ones + for (i in 3..n) { + dp[i] = dp[i - 1] + dp[i - 2] + } + return dp[n] +} + +/* Climbing stairs: Space-optimized dynamic programming */ +fun climbingStairsDPComp(n: Int): Int { + if (n == 1 || n == 2) return n + var a = 1 + var b = 2 + for (i in 3..n) { + val temp = b + b += a + a = temp + } + return b +} + +/* Driver Code */ +fun main() { + val n = 9 + + var res = climbingStairsDP(n) + println("Climbing $n stairs has $res solutions") + + res = climbingStairsDPComp(n) + println("Climbing $n stairs has $res solutions") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/coin_change.kt b/en/codes/kotlin/chapter_dynamic_programming/coin_change.kt new file mode 100644 index 000000000..05e9e3180 --- /dev/null +++ b/en/codes/kotlin/chapter_dynamic_programming/coin_change.kt @@ -0,0 +1,71 @@ +/** + * File: coin_change.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +import kotlin.math.min + +/* Coin change: Dynamic programming */ +fun coinChangeDP(coins: IntArray, amt: Int): Int { + val n = coins.size + val MAX = amt + 1 + // Initialize dp table + val dp = Array(n + 1) { IntArray(amt + 1) } + // State transition: first row and first column + for (a in 1..amt) { + dp[0][a] = MAX + } + // State transition: rest of the rows and columns + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a] + } else { + // The smaller value between not selecting and selecting coin i + dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1) + } + } + } + return if (dp[n][amt] != MAX) dp[n][amt] else -1 +} + +/* Coin change: Space-optimized dynamic programming */ +fun coinChangeDPComp(coins: IntArray, amt: Int): Int { + val n = coins.size + val MAX = amt + 1 + // Initialize dp table + val dp = IntArray(amt + 1) + dp.fill(MAX) + dp[0] = 0 + // State transition + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a] + } else { + // The smaller value between not selecting and selecting coin i + dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1) + } + } + } + return if (dp[amt] != MAX) dp[amt] else -1 +} + +/* Driver Code */ +fun main() { + val coins = intArrayOf(1, 2, 5) + val amt = 4 + + // Dynamic programming + var res = coinChangeDP(coins, amt) + println("Minimum coins needed to make target amount is $res") + + // Space-optimized dynamic programming + res = coinChangeDPComp(coins, amt) + println("Minimum coins needed to make target amount is $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/coin_change_ii.kt b/en/codes/kotlin/chapter_dynamic_programming/coin_change_ii.kt new file mode 100644 index 000000000..65fccef40 --- /dev/null +++ b/en/codes/kotlin/chapter_dynamic_programming/coin_change_ii.kt @@ -0,0 +1,66 @@ +/** + * File: coin_change_ii.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +/* Coin change II: Dynamic programming */ +fun coinChangeIIDP(coins: IntArray, amt: Int): Int { + val n = coins.size + // Initialize dp table + val dp = Array(n + 1) { IntArray(amt + 1) } + // Initialize first column + for (i in 0..n) { + dp[i][0] = 1 + } + // State transition + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a] + } else { + // Sum of the two options: not selecting and selecting coin i + dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]] + } + } + } + return dp[n][amt] +} + +/* Coin change II: Space-optimized dynamic programming */ +fun coinChangeIIDPComp(coins: IntArray, amt: Int): Int { + val n = coins.size + // Initialize dp table + val dp = IntArray(amt + 1) + dp[0] = 1 + // State transition + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a] + } else { + // Sum of the two options: not selecting and selecting coin i + dp[a] = dp[a] + dp[a - coins[i - 1]] + } + } + } + return dp[amt] +} + +/* Driver Code */ +fun main() { + val coins = intArrayOf(1, 2, 5) + val amt = 5 + + // Dynamic programming + var res = coinChangeIIDP(coins, amt) + println("Number of coin combinations to make target amount is $res") + + // Space-optimized dynamic programming + res = coinChangeIIDPComp(coins, amt) + println("Number of coin combinations to make target amount is $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/edit_distance.kt b/en/codes/kotlin/chapter_dynamic_programming/edit_distance.kt new file mode 100644 index 000000000..8dc96fe6d --- /dev/null +++ b/en/codes/kotlin/chapter_dynamic_programming/edit_distance.kt @@ -0,0 +1,143 @@ +/** + * File: edit_distance.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +import kotlin.math.min + +/* Edit distance: Brute-force search */ +fun editDistanceDFS( + s: String, + t: String, + i: Int, + j: Int +): Int { + // If both s and t are empty, return 0 + if (i == 0 && j == 0) return 0 + // If s is empty, return length of t + if (i == 0) return j + // If t is empty, return length of s + if (j == 0) return i + // If two characters are equal, skip both characters + if (s[i - 1] == t[j - 1]) return editDistanceDFS(s, t, i - 1, j - 1) + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + val insert = editDistanceDFS(s, t, i, j - 1) + val delete = editDistanceDFS(s, t, i - 1, j) + val replace = editDistanceDFS(s, t, i - 1, j - 1) + // Return minimum edit steps + return min(min(insert, delete), replace) + 1 +} + +/* Edit distance: Memoization search */ +fun editDistanceDFSMem( + s: String, + t: String, + mem: Array, + i: Int, + j: Int +): Int { + // If both s and t are empty, return 0 + if (i == 0 && j == 0) return 0 + // If s is empty, return length of t + if (i == 0) return j + // If t is empty, return length of s + if (j == 0) return i + // If there's a record, return it directly + if (mem[i][j] != -1) return mem[i][j] + // If two characters are equal, skip both characters + if (s[i - 1] == t[j - 1]) return editDistanceDFSMem(s, t, mem, i - 1, j - 1) + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + val insert = editDistanceDFSMem(s, t, mem, i, j - 1) + val delete = editDistanceDFSMem(s, t, mem, i - 1, j) + val replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1) + // Record and return minimum edit steps + mem[i][j] = min(min(insert, delete), replace) + 1 + return mem[i][j] +} + +/* Edit distance: Dynamic programming */ +fun editDistanceDP(s: String, t: String): Int { + val n = s.length + val m = t.length + val dp = Array(n + 1) { IntArray(m + 1) } + // State transition: first row and first column + for (i in 1..n) { + dp[i][0] = i + } + for (j in 1..m) { + dp[0][j] = j + } + // State transition: rest of the rows and columns + for (i in 1..n) { + for (j in 1..m) { + if (s[i - 1] == t[j - 1]) { + // If two characters are equal, skip both characters + dp[i][j] = dp[i - 1][j - 1] + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1 + } + } + } + return dp[n][m] +} + +/* Edit distance: Space-optimized dynamic programming */ +fun editDistanceDPComp(s: String, t: String): Int { + val n = s.length + val m = t.length + val dp = IntArray(m + 1) + // State transition: first row + for (j in 1..m) { + dp[j] = j + } + // State transition: rest of the rows + for (i in 1..n) { + // State transition: first column + var leftup = dp[0] // Temporarily store dp[i-1, j-1] + dp[0] = i + // State transition: rest of the columns + for (j in 1..m) { + val temp = dp[j] + if (s[i - 1] == t[j - 1]) { + // If two characters are equal, skip both characters + dp[j] = leftup + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1 + } + leftup = temp // Update for next round's dp[i-1, j-1] + } + } + return dp[m] +} + +/* Driver Code */ +fun main() { + val s = "bag" + val t = "pack" + val n = s.length + val m = t.length + + // Brute-force search + var res = editDistanceDFS(s, t, n, m) + println("Changing $s to $t requires minimum $res edits") + + // Memoization search + val mem = Array(n + 1) { IntArray(m + 1) } + for (row in mem) + row.fill(-1) + res = editDistanceDFSMem(s, t, mem, n, m) + println("Changing $s to $t requires minimum $res edits") + + // Dynamic programming + res = editDistanceDP(s, t) + println("Changing $s to $t requires minimum $res edits") + + // Space-optimized dynamic programming + res = editDistanceDPComp(s, t) + println("Changing $s to $t requires minimum $res edits") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/knapsack.kt b/en/codes/kotlin/chapter_dynamic_programming/knapsack.kt new file mode 100644 index 000000000..455ddad0a --- /dev/null +++ b/en/codes/kotlin/chapter_dynamic_programming/knapsack.kt @@ -0,0 +1,125 @@ +/** + * File: knapsack.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +import kotlin.math.max + +/* 0-1 knapsack: Brute-force search */ +fun knapsackDFS( + wgt: IntArray, + _val: IntArray, + i: Int, + c: Int +): Int { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i == 0 || c == 0) { + return 0 + } + // If exceeds knapsack capacity, can only choose not to put it in + if (wgt[i - 1] > c) { + return knapsackDFS(wgt, _val, i - 1, c) + } + // Calculate the maximum value of not putting in and putting in item i + val no = knapsackDFS(wgt, _val, i - 1, c) + val yes = knapsackDFS(wgt, _val, i - 1, c - wgt[i - 1]) + _val[i - 1] + // Return the larger value of the two options + return max(no, yes) +} + +/* 0-1 knapsack: Memoization search */ +fun knapsackDFSMem( + wgt: IntArray, + _val: IntArray, + mem: Array, + i: Int, + c: Int +): Int { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i == 0 || c == 0) { + return 0 + } + // If there's a record, return it directly + if (mem[i][c] != -1) { + return mem[i][c] + } + // If exceeds knapsack capacity, can only choose not to put it in + if (wgt[i - 1] > c) { + return knapsackDFSMem(wgt, _val, mem, i - 1, c) + } + // Calculate the maximum value of not putting in and putting in item i + val no = knapsackDFSMem(wgt, _val, mem, i - 1, c) + val yes = knapsackDFSMem(wgt, _val, mem, i - 1, c - wgt[i - 1]) + _val[i - 1] + // Record and return the larger value of the two options + mem[i][c] = max(no, yes) + return mem[i][c] +} + +/* 0-1 knapsack: Dynamic programming */ +fun knapsackDP(wgt: IntArray, _val: IntArray, cap: Int): Int { + val n = wgt.size + // Initialize dp table + val dp = Array(n + 1) { IntArray(cap + 1) } + // State transition + for (i in 1..n) { + for (c in 1..cap) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c] + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + _val[i - 1]) + } + } + } + return dp[n][cap] +} + +/* 0-1 knapsack: Space-optimized dynamic programming */ +fun knapsackDPComp(wgt: IntArray, _val: IntArray, cap: Int): Int { + val n = wgt.size + // Initialize dp table + val dp = IntArray(cap + 1) + // State transition + for (i in 1..n) { + // Traverse in reverse order + for (c in cap downTo 1) { + if (wgt[i - 1] <= c) { + // The larger value between not selecting and selecting item i + dp[c] = max(dp[c], dp[c - wgt[i - 1]] + _val[i - 1]) + } + } + } + return dp[cap] +} + +/* Driver Code */ +fun main() { + val wgt = intArrayOf(10, 20, 30, 40, 50) + val _val = intArrayOf(50, 120, 150, 210, 240) + val cap = 50 + val n = wgt.size + + // Brute-force search + var res = knapsackDFS(wgt, _val, n, cap) + println("Maximum item value not exceeding knapsack capacity is $res") + + // Memoization search + val mem = Array(n + 1) { IntArray(cap + 1) } + for (row in mem) { + row.fill(-1) + } + res = knapsackDFSMem(wgt, _val, mem, n, cap) + println("Maximum item value not exceeding knapsack capacity is $res") + + // Dynamic programming + res = knapsackDP(wgt, _val, cap) + println("Maximum item value not exceeding knapsack capacity is $res") + + // Space-optimized dynamic programming + res = knapsackDPComp(wgt, _val, cap) + println("Maximum item value not exceeding knapsack capacity is $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/min_cost_climbing_stairs_dp.kt b/en/codes/kotlin/chapter_dynamic_programming/min_cost_climbing_stairs_dp.kt new file mode 100644 index 000000000..c4d70b927 --- /dev/null +++ b/en/codes/kotlin/chapter_dynamic_programming/min_cost_climbing_stairs_dp.kt @@ -0,0 +1,51 @@ +/** + * File: min_cost_climbing_stairs_dp.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +import kotlin.math.min + +/* Minimum cost climbing stairs: Dynamic programming */ +fun minCostClimbingStairsDP(cost: IntArray): Int { + val n = cost.size - 1 + if (n == 1 || n == 2) return cost[n] + // Initialize dp table, used to store solutions to subproblems + val dp = IntArray(n + 1) + // Initial state: preset the solution to the smallest subproblem + dp[1] = cost[1] + dp[2] = cost[2] + // State transition: gradually solve larger subproblems from smaller ones + for (i in 3..n) { + dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] + } + return dp[n] +} + +/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +fun minCostClimbingStairsDPComp(cost: IntArray): Int { + val n = cost.size - 1 + if (n == 1 || n == 2) return cost[n] + var a = cost[1] + var b = cost[2] + for (i in 3..n) { + val tmp = b + b = min(a, tmp) + cost[i] + a = tmp + } + return b +} + +/* Driver Code */ +fun main() { + val cost = intArrayOf(0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1) + println("Input stair cost list is ${cost.contentToString()}") + + var res = minCostClimbingStairsDP(cost) + println("Minimum cost to climb stairs is $res") + + res = minCostClimbingStairsDPComp(cost) + println("Minimum cost to climb stairs is $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_dynamic_programming/min_path_sum.kt b/en/codes/kotlin/chapter_dynamic_programming/min_path_sum.kt new file mode 100644 index 000000000..3d9375d1b --- /dev/null +++ b/en/codes/kotlin/chapter_dynamic_programming/min_path_sum.kt @@ -0,0 +1,132 @@ +/** + * File: min_path_sum.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +import kotlin.math.min + +/* Minimum path sum: Brute-force search */ +fun minPathSumDFS(grid: Array, i: Int, j: Int): Int { + // If it's the top-left cell, terminate the search + if (i == 0 && j == 0) { + return grid[0][0] + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + return Int.MAX_VALUE + } + // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + val up = minPathSumDFS(grid, i - 1, j) + val left = minPathSumDFS(grid, i, j - 1) + // Return the minimum path cost from top-left to (i, j) + return min(left, up) + grid[i][j] +} + +/* Minimum path sum: Memoization search */ +fun minPathSumDFSMem( + grid: Array, + mem: Array, + i: Int, + j: Int +): Int { + // If it's the top-left cell, terminate the search + if (i == 0 && j == 0) { + return grid[0][0] + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + return Int.MAX_VALUE + } + // If there's a record, return it directly + if (mem[i][j] != -1) { + return mem[i][j] + } + // Minimum path cost for left and upper cells + val up = minPathSumDFSMem(grid, mem, i - 1, j) + val left = minPathSumDFSMem(grid, mem, i, j - 1) + // Record and return the minimum path cost from top-left to (i, j) + mem[i][j] = min(left, up) + grid[i][j] + return mem[i][j] +} + +/* Minimum path sum: Dynamic programming */ +fun minPathSumDP(grid: Array): Int { + val n = grid.size + val m = grid[0].size + // Initialize dp table + val dp = Array(n) { IntArray(m) } + dp[0][0] = grid[0][0] + // State transition: first row + for (j in 1..): Int { + val n = grid.size + val m = grid[0].size + // Initialize dp table + val dp = IntArray(m) + // State transition: first row + dp[0] = grid[0][0] + for (j in 1.. c) { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c] + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + _val[i - 1]) + } + } + } + return dp[n][cap] +} + +/* Unbounded knapsack: Space-optimized dynamic programming */ +fun unboundedKnapsackDPComp( + wgt: IntArray, + _val: IntArray, + cap: Int +): Int { + val n = wgt.size + // Initialize dp table + val dp = IntArray(cap + 1) + // State transition + for (i in 1..n) { + for (c in 1..cap) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[c] = dp[c] + } else { + // The larger value between not selecting and selecting item i + dp[c] = max(dp[c], dp[c - wgt[i - 1]] + _val[i - 1]) + } + } + } + return dp[cap] +} + +/* Driver Code */ +fun main() { + val wgt = intArrayOf(1, 2, 3) + val _val = intArrayOf(5, 11, 15) + val cap = 4 + + // Dynamic programming + var res = unboundedKnapsackDP(wgt, _val, cap) + println("Maximum item value not exceeding knapsack capacity is $res") + + // Space-optimized dynamic programming + res = unboundedKnapsackDPComp(wgt, _val, cap) + println("Maximum item value not exceeding knapsack capacity is $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_graph/graph_adjacency_list.kt b/en/codes/kotlin/chapter_graph/graph_adjacency_list.kt new file mode 100644 index 000000000..2fb2eff76 --- /dev/null +++ b/en/codes/kotlin/chapter_graph/graph_adjacency_list.kt @@ -0,0 +1,121 @@ +/** + * File: graph_adjacency_list.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_graph + +import utils.Vertex + +/* Undirected graph class based on adjacency list */ +class GraphAdjList(edges: Array>) { + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + val adjList = HashMap>() + + /* Constructor */ + init { + // Add all vertices and edges + for (edge in edges) { + addVertex(edge[0]!!) + addVertex(edge[1]!!) + addEdge(edge[0]!!, edge[1]!!) + } + } + + /* Get the number of vertices */ + fun size(): Int { + return adjList.size + } + + /* Add edge */ + fun addEdge(vet1: Vertex, vet2: Vertex) { + if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) + throw IllegalArgumentException() + // Add edge vet1 - vet2 + adjList[vet1]?.add(vet2) + adjList[vet2]?.add(vet1) + } + + /* Remove edge */ + fun removeEdge(vet1: Vertex, vet2: Vertex) { + if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) + throw IllegalArgumentException() + // Remove edge vet1 - vet2 + adjList[vet1]?.remove(vet2) + adjList[vet2]?.remove(vet1) + } + + /* Add vertex */ + fun addVertex(vet: Vertex) { + if (adjList.containsKey(vet)) + return + // Add a new linked list in the adjacency list + adjList[vet] = mutableListOf() + } + + /* Remove vertex */ + fun removeVertex(vet: Vertex) { + if (!adjList.containsKey(vet)) + throw IllegalArgumentException() + // Remove the linked list corresponding to vertex vet in the adjacency list + adjList.remove(vet) + // Traverse the linked lists of other vertices and remove all edges containing vet + for (list in adjList.values) { + list.remove(vet) + } + } + + /* Print adjacency list */ + fun print() { + println("Adjacency list =") + for (pair in adjList.entries) { + val tmp = mutableListOf() + for (vertex in pair.value) { + tmp.add(vertex._val) + } + println("${pair.key._val}: $tmp,") + } + } +} + +/* Driver Code */ +fun main() { + /* Add edge */ + val v = Vertex.valsToVets(intArrayOf(1, 3, 2, 5, 4)) + val edges = arrayOf( + arrayOf(v[0], v[1]), + arrayOf(v[0], v[3]), + arrayOf(v[1], v[2]), + arrayOf(v[2], v[3]), + arrayOf(v[2], v[4]), + arrayOf(v[3], v[4]) + ) + val graph = GraphAdjList(edges) + println("\nAfter initialization, graph is") + graph.print() + + /* Add edge */ + // Vertices 1, 3 are v[0], v[1] + graph.addEdge(v[0]!!, v[2]!!) + println("\nAfter adding edge 1-2, graph is") + graph.print() + + /* Remove edge */ + // Vertex 3 is v[1] + graph.removeEdge(v[0]!!, v[1]!!) + println("\nAfter removing edge 1-3, graph is") + graph.print() + + /* Add vertex */ + val v5 = Vertex(6) + graph.addVertex(v5) + println("\nAfter adding vertex 6, graph is") + graph.print() + + /* Remove vertex */ + // Vertex 3 is v[1] + graph.removeVertex(v[1]!!) + println("\nAfter removing vertex 3, graph is") + graph.print() +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_graph/graph_adjacency_matrix.kt b/en/codes/kotlin/chapter_graph/graph_adjacency_matrix.kt new file mode 100644 index 000000000..4b5b7a920 --- /dev/null +++ b/en/codes/kotlin/chapter_graph/graph_adjacency_matrix.kt @@ -0,0 +1,134 @@ +/** + * File: graph_adjacency_matrix.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_graph + +import utils.printMatrix + +/* Undirected graph class based on adjacency matrix */ +class GraphAdjMat(vertices: IntArray, edges: Array) { + val vertices = mutableListOf() // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + val adjMat = mutableListOf>() // Adjacency matrix, where the row and column indices correspond to the "vertex index" + + /* Constructor */ + init { + // Add vertex + for (vertex in vertices) { + addVertex(vertex) + } + // Add edge + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + for (edge in edges) { + addEdge(edge[0], edge[1]) + } + } + + /* Get the number of vertices */ + fun size(): Int { + return vertices.size + } + + /* Add vertex */ + fun addVertex(_val: Int) { + val n = size() + // Add the value of the new vertex to the vertex list + vertices.add(_val) + // Add a row to the adjacency matrix + val newRow = mutableListOf() + for (j in 0..= size()) + throw IndexOutOfBoundsException() + // Remove the vertex at index from the vertex list + vertices.removeAt(index) + // Remove the row at index from the adjacency matrix + adjMat.removeAt(index) + // Remove the column at index from the adjacency matrix + for (row in adjMat) { + row.removeAt(index) + } + } + + /* Add edge */ + // Parameters i, j correspond to the vertices element indices + fun addEdge(i: Int, j: Int) { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) + throw IndexOutOfBoundsException() + // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + adjMat[i][j] = 1 + adjMat[j][i] = 1 + } + + /* Remove edge */ + // Parameters i, j correspond to the vertices element indices + fun removeEdge(i: Int, j: Int) { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) + throw IndexOutOfBoundsException() + adjMat[i][j] = 0 + adjMat[j][i] = 0 + } + + /* Print adjacency matrix */ + fun print() { + print("Vertex list = ") + println(vertices) + println("Adjacency matrix =") + printMatrix(adjMat) + } +} + +/* Driver Code */ +fun main() { + /* Add edge */ + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + val vertices = intArrayOf(1, 3, 2, 5, 4) + val edges = arrayOf( + intArrayOf(0, 1), + intArrayOf(0, 3), + intArrayOf(1, 2), + intArrayOf(2, 3), + intArrayOf(2, 4), + intArrayOf(3, 4) + ) + val graph = GraphAdjMat(vertices, edges) + println("\nAfter initialization, graph is") + graph.print() + + /* Add edge */ + // Add vertex + graph.addEdge(0, 2) + println("\nAfter adding edge 1-2, graph is") + graph.print() + + /* Remove edge */ + // Vertices 1, 3 have indices 0, 1 respectively + graph.removeEdge(0, 1) + println("\nAfter removing edge 1-3, graph is") + graph.print() + + /* Add vertex */ + graph.addVertex(6) + println("\nAfter adding vertex 6, graph is") + graph.print() + + /* Remove vertex */ + // Vertex 3 has index 1 + graph.removeVertex(1) + println("\nAfter removing vertex 3, graph is") + graph.print() +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_graph/graph_bfs.kt b/en/codes/kotlin/chapter_graph/graph_bfs.kt new file mode 100644 index 000000000..60086f04e --- /dev/null +++ b/en/codes/kotlin/chapter_graph/graph_bfs.kt @@ -0,0 +1,65 @@ +/** + * File: graph_bfs.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_graph + +import utils.Vertex +import java.util.* + +/* Breadth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList { + // Vertex traversal sequence + val res = mutableListOf() + // Hash set for recording vertices that have been visited + val visited = HashSet() + visited.add(startVet) + // Queue used to implement BFS + val que = LinkedList() + que.offer(startVet) + // Starting from vertex vet, loop until all vertices are visited + while (!que.isEmpty()) { + val vet = que.poll() // Dequeue the front vertex + res.add(vet) // Record visited vertex + // Traverse all adjacent vertices of this vertex + for (adjVet in graph.adjList[vet]!!) { + if (visited.contains(adjVet)) + continue // Skip vertices that have been visited + que.offer(adjVet) // Only enqueue unvisited vertices + visited.add(adjVet) // Mark this vertex as visited + } + } + // Return vertex traversal sequence + return res +} + +/* Driver Code */ +fun main() { + /* Add edge */ + val v = Vertex.valsToVets(intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)) + val edges = arrayOf( + arrayOf(v[0], v[1]), + arrayOf(v[0], v[3]), + arrayOf(v[1], v[2]), + arrayOf(v[1], v[4]), + arrayOf(v[2], v[5]), + arrayOf(v[3], v[4]), + arrayOf(v[3], v[6]), + arrayOf(v[4], v[5]), + arrayOf(v[4], v[7]), + arrayOf(v[5], v[8]), + arrayOf(v[6], v[7]), + arrayOf(v[7], v[8]) + ) + val graph = GraphAdjList(edges) + println("\nAfter initialization, graph is") + graph.print() + + /* Breadth-first traversal */ + val res = graphBFS(graph, v[0]!!) + println("\nBreadth-first traversal (BFS) vertex sequence is") + println(Vertex.vetsToVals(res)) +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_graph/graph_dfs.kt b/en/codes/kotlin/chapter_graph/graph_dfs.kt new file mode 100644 index 000000000..64f2879b2 --- /dev/null +++ b/en/codes/kotlin/chapter_graph/graph_dfs.kt @@ -0,0 +1,60 @@ +/** + * File: graph_dfs.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_graph + +import utils.Vertex + +/* Depth-first traversal helper function */ +fun dfs( + graph: GraphAdjList, + visited: MutableSet, + res: MutableList, + vet: Vertex? +) { + res.add(vet) // Record visited vertex + visited.add(vet) // Mark this vertex as visited + // Traverse all adjacent vertices of this vertex + for (adjVet in graph.adjList[vet]!!) { + if (visited.contains(adjVet)) + continue // Skip vertices that have been visited + // Recursively visit adjacent vertices + dfs(graph, visited, res, adjVet) + } +} + +/* Depth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList { + // Vertex traversal sequence + val res = mutableListOf() + // Hash set for recording vertices that have been visited + val visited = HashSet() + dfs(graph, visited, res, startVet) + return res +} + +/* Driver Code */ +fun main() { + /* Add edge */ + val v = Vertex.valsToVets(intArrayOf(0, 1, 2, 3, 4, 5, 6)) + val edges = arrayOf( + arrayOf(v[0], v[1]), + arrayOf(v[0], v[3]), + arrayOf(v[1], v[2]), + arrayOf(v[2], v[5]), + arrayOf(v[4], v[5]), + arrayOf(v[5], v[6]) + ) + val graph = GraphAdjList(edges) + println("\nAfter initialization, graph is") + graph.print() + + /* Depth-first traversal */ + val res = graphDFS(graph, v[0]) + println("\nDepth-first traversal (DFS) vertex sequence is") + println(Vertex.vetsToVals(res)) +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_greedy/coin_change_greedy.kt b/en/codes/kotlin/chapter_greedy/coin_change_greedy.kt new file mode 100644 index 000000000..962337f56 --- /dev/null +++ b/en/codes/kotlin/chapter_greedy/coin_change_greedy.kt @@ -0,0 +1,53 @@ +/** + * File: coin_change_greedy.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_greedy + +/* Coin change: Greedy algorithm */ +fun coinChangeGreedy(coins: IntArray, amt: Int): Int { + // Assume coins list is sorted + var am = amt + var i = coins.size - 1 + var count = 0 + // Loop to make greedy choices until no remaining amount + while (am > 0) { + // Find the coin that is less than and closest to the remaining amount + while (i > 0 && coins[i] > am) { + i-- + } + // Choose coins[i] + am -= coins[i] + count++ + } + // If no feasible solution is found, return -1 + return if (am == 0) count else -1 +} + +/* Driver Code */ +fun main() { + // Greedy algorithm: Can guarantee finding the global optimal solution + var coins = intArrayOf(1, 5, 10, 20, 50, 100) + var amt = 186 + var res = coinChangeGreedy(coins, amt) + println("\ncoins = ${coins.contentToString()}, amt = $amt") + println("Minimum coins needed to make $amt is $res") + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = intArrayOf(1, 20, 50) + amt = 60 + res = coinChangeGreedy(coins, amt) + println("\ncoins = ${coins.contentToString()}, amt = $amt") + println("Minimum coins needed to make $amt is $res") + println("Actually the minimum number needed is 3, i.e., 20 + 20 + 20") + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = intArrayOf(1, 49, 50) + amt = 98 + res = coinChangeGreedy(coins, amt) + println("\ncoins = ${coins.contentToString()}, amt = $amt") + println("Minimum coins needed to make $amt is $res") + println("Actually the minimum number needed is 2, i.e., 49 + 49") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_greedy/fractional_knapsack.kt b/en/codes/kotlin/chapter_greedy/fractional_knapsack.kt new file mode 100644 index 000000000..d8f26cf09 --- /dev/null +++ b/en/codes/kotlin/chapter_greedy/fractional_knapsack.kt @@ -0,0 +1,51 @@ +/** + * File: fractional_knapsack.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_greedy + +/* Item */ +class Item( + val w: Int, // Item + val v: Int // Item value +) + +/* Fractional knapsack: Greedy algorithm */ +fun fractionalKnapsack(wgt: IntArray, _val: IntArray, c: Int): Double { + // Create item list with two attributes: weight, value + var cap = c + val items = arrayOfNulls(wgt.size) + for (i in wgt.indices) { + items[i] = Item(wgt[i], _val[i]) + } + // Sort by unit value item.v / item.w from high to low + items.sortBy { item: Item? -> -(item!!.v.toDouble() / item.w) } + // Loop for greedy selection + var res = 0.0 + for (item in items) { + if (item!!.w <= cap) { + // If remaining capacity is sufficient, put the entire current item into the knapsack + res += item.v + cap -= item.w + } else { + // If remaining capacity is insufficient, put part of the current item into the knapsack + res += item.v.toDouble() / item.w * cap + // No remaining capacity, so break out of the loop + break + } + } + return res +} + +/* Driver Code */ +fun main() { + val wgt = intArrayOf(10, 20, 30, 40, 50) + val _val = intArrayOf(50, 120, 150, 210, 240) + val cap = 50 + + // Greedy algorithm + val res = fractionalKnapsack(wgt, _val, cap) + println("Maximum item value not exceeding knapsack capacity is $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_greedy/max_capacity.kt b/en/codes/kotlin/chapter_greedy/max_capacity.kt new file mode 100644 index 000000000..a8d6015bc --- /dev/null +++ b/en/codes/kotlin/chapter_greedy/max_capacity.kt @@ -0,0 +1,41 @@ +/** + * File: max_capacity.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_greedy + +import kotlin.math.max +import kotlin.math.min + +/* Max capacity: Greedy algorithm */ +fun maxCapacity(ht: IntArray): Int { + // Initialize i, j to be at both ends of the array + var i = 0 + var j = ht.size - 1 + // Initial max capacity is 0 + var res = 0 + // Loop for greedy selection until the two boards meet + while (i < j) { + // Update max capacity + val cap = min(ht[i], ht[j]) * (j - i) + res = max(res, cap) + // Move the shorter board inward + if (ht[i] < ht[j]) { + i++ + } else { + j-- + } + } + return res +} + +/* Driver Code */ +fun main() { + val ht = intArrayOf(3, 8, 5, 2, 7, 7, 3, 4) + + // Greedy algorithm + val res = maxCapacity(ht) + println("Maximum capacity is $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_greedy/max_product_cutting.kt b/en/codes/kotlin/chapter_greedy/max_product_cutting.kt new file mode 100644 index 000000000..cd4b43678 --- /dev/null +++ b/en/codes/kotlin/chapter_greedy/max_product_cutting.kt @@ -0,0 +1,39 @@ +/** + * File: max_product_cutting.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_greedy + +import kotlin.math.pow + +/* Max product cutting: Greedy algorithm */ +fun maxProductCutting(n: Int): Int { + // When n <= 3, must cut out a 1 + if (n <= 3) { + return 1 * (n - 1) + } + // Greedily cut out 3, a is the number of 3s, b is the remainder + val a = n / 3 + val b = n % 3 + if (b == 1) { + // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + return 3.0.pow((a - 1)).toInt() * 2 * 2 + } + if (b == 2) { + // When the remainder is 2, do nothing + return 3.0.pow(a).toInt() * 2 * 2 + } + // When the remainder is 0, do nothing + return 3.0.pow(a).toInt() +} + +/* Driver Code */ +fun main() { + val n = 58 + + // Greedy algorithm + val res = maxProductCutting(n) + println("Maximum cutting product is $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_hashing/array_hash_map.kt b/en/codes/kotlin/chapter_hashing/array_hash_map.kt new file mode 100644 index 000000000..6ec179847 --- /dev/null +++ b/en/codes/kotlin/chapter_hashing/array_hash_map.kt @@ -0,0 +1,126 @@ +/** + * File: array_hash_map.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_hashing + +/* Key-value pair */ +class Pair( + var key: Int, + var _val: String +) + +/* Hash table based on array implementation */ +class ArrayHashMap { + // Initialize array with 100 buckets + private val buckets = arrayOfNulls(100) + + /* Hash function */ + fun hashFunc(key: Int): Int { + val index = key % 100 + return index + } + + /* Query operation */ + fun get(key: Int): String? { + val index = hashFunc(key) + val pair = buckets[index] ?: return null + return pair._val + } + + /* Add operation */ + fun put(key: Int, _val: String) { + val pair = Pair(key, _val) + val index = hashFunc(key) + buckets[index] = pair + } + + /* Remove operation */ + fun remove(key: Int) { + val index = hashFunc(key) + // Set to null to represent deletion + buckets[index] = null + } + + /* Get all key-value pairs */ + fun pairSet(): MutableList { + val pairSet = mutableListOf() + for (pair in buckets) { + if (pair != null) + pairSet.add(pair) + } + return pairSet + } + + /* Get all keys */ + fun keySet(): MutableList { + val keySet = mutableListOf() + for (pair in buckets) { + if (pair != null) + keySet.add(pair.key) + } + return keySet + } + + /* Get all values */ + fun valueSet(): MutableList { + val valueSet = mutableListOf() + for (pair in buckets) { + if (pair != null) + valueSet.add(pair._val) + } + return valueSet + } + + /* Print hash table */ + fun print() { + for (kv in pairSet()) { + val key = kv.key + val _val = kv._val + println("$key -> $_val") + } + } +} + +/* Driver Code */ +fun main() { + /* Initialize hash table */ + val map = ArrayHashMap() + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.put(12836, "Xiao Ha") + map.put(15937, "Xiao Luo") + map.put(16750, "Xiao Suan") + map.put(13276, "Xiao Fa") + map.put(10583, "Xiao Ya") + println("\nAfter adding is complete, hash table is\nKey -> Value") + map.print() + + /* Query operation */ + // Input key into hash table to get value + val name = map.get(15937) + println("\nInput student ID 15937, found name $name") + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(10583) + println("\nAfter removing 10583, hash table is\nKey -> Value") + map.print() + + /* Traverse hash table */ + println("\nTraverse key-value pairs Key -> Value") + for (kv in map.pairSet()) { + println("${kv.key} -> ${kv._val}") + } + println("\nTraverse keys only Key") + for (key in map.keySet()) { + println(key) + } + println("\nTraverse values only Value") + for (_val in map.valueSet()) { + println(_val) + } +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_hashing/built_in_hash.kt b/en/codes/kotlin/chapter_hashing/built_in_hash.kt new file mode 100644 index 000000000..2023f775b --- /dev/null +++ b/en/codes/kotlin/chapter_hashing/built_in_hash.kt @@ -0,0 +1,36 @@ +/** + * File: built_in_hash.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_hashing + +import utils.ListNode + +/* Driver Code */ +fun main() { + val num = 3 + val hashNum = num.hashCode() + println("Hash value of integer $num is $hashNum") + + val bol = true + val hashBol = bol.hashCode() + println("Hash value of boolean $bol is $hashBol") + + val dec = 3.14159 + val hashDec = dec.hashCode() + println("Hash value of decimal $dec is $hashDec") + + val str = "Hello Algo" + val hashStr = str.hashCode() + println("Hash value of string $str is $hashStr") + + val arr = arrayOf(12836, "Xiao Ha") + val hashTup = arr.contentHashCode() + println("Hash value of array ${arr.contentToString()} is $hashTup") + + val obj = ListNode(0) + val hashObj = obj.hashCode() + println("Hash value of node object $obj is $hashObj") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_hashing/hash_map.kt b/en/codes/kotlin/chapter_hashing/hash_map.kt new file mode 100644 index 000000000..95f20152d --- /dev/null +++ b/en/codes/kotlin/chapter_hashing/hash_map.kt @@ -0,0 +1,50 @@ +/** + * File: hash_map.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_hashing + +import utils.printHashMap + +/* Driver Code */ +fun main() { + /* Initialize hash table */ + val map = HashMap() + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map[12836] = "Xiao Ha" + map[15937] = "Xiao Luo" + map[16750] = "Xiao Suan" + map[13276] = "Xiao Fa" + map[10583] = "Xiao Ya" + println("\nAfter adding is complete, hash table is\nKey -> Value") + printHashMap(map) + + /* Query operation */ + // Input key into hash table to get value + val name = map[15937] + println("\nInput student ID 15937, found name $name") + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(10583) + println("\nAfter removing 10583, hash table is\nKey -> Value") + printHashMap(map) + + /* Traverse hash table */ + println("\nTraverse key-value pairs Key->Value") + for ((key, value) in map) { + println("$key -> $value") + } + println("\nTraverse keys only Key") + for (key in map.keys) { + println(key) + } + println("\nTraverse values only Value") + for (_val in map.values) { + println(_val) + } +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_hashing/hash_map_chaining.kt b/en/codes/kotlin/chapter_hashing/hash_map_chaining.kt new file mode 100644 index 000000000..0af4877f0 --- /dev/null +++ b/en/codes/kotlin/chapter_hashing/hash_map_chaining.kt @@ -0,0 +1,145 @@ +/** + * File: hash_map_chaining.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_hashing + +/* Hash table with separate chaining */ +class HashMapChaining { + var size: Int // Number of key-value pairs + var capacity: Int // Hash table capacity + val loadThres: Double // Load factor threshold for triggering expansion + val extendRatio: Int // Expansion multiplier + var buckets: MutableList> // Bucket array + + /* Constructor */ + init { + size = 0 + capacity = 4 + loadThres = 2.0 / 3.0 + extendRatio = 2 + buckets = mutableListOf() + for (i in 0.. loadThres) { + extend() + } + val index = hashFunc(key) + val bucket = buckets[index] + // Traverse bucket, if specified key is encountered, update corresponding val and return + for (pair in bucket) { + if (pair.key == key) { + pair._val = _val + return + } + } + // If key does not exist, append key-value pair to the end + val pair = Pair(key, _val) + bucket.add(pair) + size++ + } + + /* Remove operation */ + fun remove(key: Int) { + val index = hashFunc(key) + val bucket = buckets[index] + // Traverse bucket and remove key-value pair from it + for (pair in bucket) { + if (pair.key == key) { + bucket.remove(pair) + size-- + break + } + } + } + + /* Expand hash table */ + fun extend() { + // Temporarily store the original hash table + val bucketsTmp = buckets + // Initialize expanded new hash table + capacity *= extendRatio + // mutablelist has no fixed size + buckets = mutableListOf() + for (i in 0..() + for (pair in bucket) { + val k = pair.key + val v = pair._val + res.add("$k -> $v") + } + println(res) + } + } +} + +/* Driver Code */ +fun main() { + /* Initialize hash table */ + val map = HashMapChaining() + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.put(12836, "Xiao Ha") + map.put(15937, "Xiao Luo") + map.put(16750, "Xiao Suan") + map.put(13276, "Xiao Fa") + map.put(10583, "Xiao Ya") + println("\nAfter adding is complete, hash table is\nKey -> Value") + map.print() + + /* Query operation */ + // Input key into hash table to get value + val name = map.get(13276) + println("\nInput student ID 13276, found name $name") + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(12836) + println("\nAfter removing 12836, hash table is\nKey -> Value") + map.print() +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_hashing/hash_map_open_addressing.kt b/en/codes/kotlin/chapter_hashing/hash_map_open_addressing.kt new file mode 100644 index 000000000..c89ee9458 --- /dev/null +++ b/en/codes/kotlin/chapter_hashing/hash_map_open_addressing.kt @@ -0,0 +1,161 @@ +/** + * File: hash_map_open_addressing.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_hashing + +/* Hash table with open addressing */ +class HashMapOpenAddressing { + private var size: Int // Number of key-value pairs + private var capacity: Int // Hash table capacity + private val loadThres: Double // Load factor threshold for triggering expansion + private val extendRatio: Int // Expansion multiplier + private var buckets: Array // Bucket array + private val TOMBSTONE: Pair // Removal marker + + /* Constructor */ + init { + size = 0 + capacity = 4 + loadThres = 2.0 / 3.0 + extendRatio = 2 + buckets = arrayOfNulls(capacity) + TOMBSTONE = Pair(-1, "-1") + } + + /* Hash function */ + fun hashFunc(key: Int): Int { + return key % capacity + } + + /* Load factor */ + fun loadFactor(): Double { + return (size / capacity).toDouble() + } + + /* Search for bucket index corresponding to key */ + fun findBucket(key: Int): Int { + var index = hashFunc(key) + var firstTombstone = -1 + // Linear probing, break when encountering an empty bucket + while (buckets[index] != null) { + // If key is encountered, return the corresponding bucket index + if (buckets[index]?.key == key) { + // If a removal marker was encountered before, move the key-value pair to that index + if (firstTombstone != -1) { + buckets[firstTombstone] = buckets[index] + buckets[index] = TOMBSTONE + return firstTombstone // Return the moved bucket index + } + return index // Return bucket index + } + // Record the first removal marker encountered + if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { + firstTombstone = index + } + // Calculate bucket index, wrap around to the head if past the tail + index = (index + 1) % capacity + } + // If key does not exist, return the index for insertion + return if (firstTombstone == -1) index else firstTombstone + } + + /* Query operation */ + fun get(key: Int): String? { + // Search for bucket index corresponding to key + val index = findBucket(key) + // If key-value pair is found, return corresponding val + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + return buckets[index]?._val + } + // If key-value pair does not exist, return null + return null + } + + /* Add operation */ + fun put(key: Int, _val: String) { + // When load factor exceeds threshold, perform expansion + if (loadFactor() > loadThres) { + extend() + } + // Search for bucket index corresponding to key + val index = findBucket(key) + // If key-value pair is found, overwrite val and return + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + buckets[index]!!._val = _val + return + } + // If key-value pair does not exist, add the key-value pair + buckets[index] = Pair(key, _val) + size++ + } + + /* Remove operation */ + fun remove(key: Int) { + // Search for bucket index corresponding to key + val index = findBucket(key) + // If key-value pair is found, overwrite it with removal marker + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + buckets[index] = TOMBSTONE + size-- + } + } + + /* Expand hash table */ + fun extend() { + // Temporarily store the original hash table + val bucketsTmp = buckets + // Initialize expanded new hash table + capacity *= extendRatio + buckets = arrayOfNulls(capacity) + size = 0 + // Move key-value pairs from original hash table to new hash table + for (pair in bucketsTmp) { + if (pair != null && pair != TOMBSTONE) { + put(pair.key, pair._val) + } + } + } + + /* Print hash table */ + fun print() { + for (pair in buckets) { + if (pair == null) { + println("null") + } else if (pair == TOMBSTONE) { + println("TOMESTOME") + } else { + println("${pair.key} -> ${pair._val}") + } + } + } +} + +/* Driver Code */ +fun main() { + // Initialize hash table + val hashmap = HashMapOpenAddressing() + + // Add operation + // Add key-value pair (key, val) to the hash table + hashmap.put(12836, "Xiao Ha") + hashmap.put(15937, "Xiao Luo") + hashmap.put(16750, "Xiao Suan") + hashmap.put(13276, "Xiao Fa") + hashmap.put(10583, "Xiao Ya") + println("\nAfter adding is complete, hash table is\nKey -> Value") + hashmap.print() + + // Query operation + // Input key into hash table to get value val + val name = hashmap.get(13276) + println("\nInput student ID 13276, found name $name") + + // Remove operation + // Remove key-value pair (key, val) from hash table + hashmap.remove(16750) + println("\nAfter removing 16750, hash table is\nKey -> Value") + hashmap.print() +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_hashing/simple_hash.kt b/en/codes/kotlin/chapter_hashing/simple_hash.kt new file mode 100644 index 000000000..d4ba2fb52 --- /dev/null +++ b/en/codes/kotlin/chapter_hashing/simple_hash.kt @@ -0,0 +1,64 @@ +/** + * File: simple_hash.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_hashing + +/* Additive hash */ +fun addHash(key: String): Int { + var hash = 0L + val MODULUS = 1000000007 + for (c in key.toCharArray()) { + hash = (hash + c.code) % MODULUS + } + return hash.toInt() +} + +/* Multiplicative hash */ +fun mulHash(key: String): Int { + var hash = 0L + val MODULUS = 1000000007 + for (c in key.toCharArray()) { + hash = (31 * hash + c.code) % MODULUS + } + return hash.toInt() +} + +/* XOR hash */ +fun xorHash(key: String): Int { + var hash = 0 + val MODULUS = 1000000007 + for (c in key.toCharArray()) { + hash = hash xor c.code + } + return hash and MODULUS +} + +/* Rotational hash */ +fun rotHash(key: String): Int { + var hash = 0L + val MODULUS = 1000000007 + for (c in key.toCharArray()) { + hash = ((hash shl 4) xor (hash shr 28) xor c.code.toLong()) % MODULUS + } + return hash.toInt() +} + +/* Driver Code */ +fun main() { + val key = "Hello Algo" + + var hash = addHash(key) + println("Additive hash value is $hash") + + hash = mulHash(key) + println("Multiplicative hash value is $hash") + + hash = xorHash(key) + println("XOR hash value is $hash") + + hash = rotHash(key) + println("Rotational hash value is $hash") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_heap/heap.kt b/en/codes/kotlin/chapter_heap/heap.kt new file mode 100644 index 000000000..962a44bb8 --- /dev/null +++ b/en/codes/kotlin/chapter_heap/heap.kt @@ -0,0 +1,66 @@ +/** + * File: heap.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_heap + +import utils.printHeap +import java.util.* + +fun testPush(heap: Queue, _val: Int) { + heap.offer(_val) // Element enters heap + print("\nAfter element $_val pushes to heap\n") + printHeap(heap) +} + +fun testPop(heap: Queue) { + val _val = heap.poll() // Time complexity is O(n), not O(nlogn) + print("\nAfter heap top element $_val pops from heap\n") + printHeap(heap) +} + +/* Driver Code */ +fun main() { + /* Initialize heap */ + // Python's heapq module implements min heap by default + var minHeap = PriorityQueue() + + // Initialize max heap (modify Comparator using lambda expression) + val maxHeap = PriorityQueue { a: Int, b: Int -> b - a } + + println("\nThe following test cases are for max heap") + + /* Element enters heap */ + testPush(maxHeap, 1) + testPush(maxHeap, 3) + testPush(maxHeap, 2) + testPush(maxHeap, 5) + testPush(maxHeap, 4) + + /* Check if heap is empty */ + val peek = maxHeap.peek() + print("\nHeap top element is $peek\n") + + /* Time complexity is O(n), not O(nlogn) */ + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + + /* Get heap size */ + val size = maxHeap.size + print("\nHeap size is $size\n") + + /* Check if heap is empty */ + val isEmpty = maxHeap.isEmpty() + print("\nIs heap empty $isEmpty\n") + + /* Input list and build heap */ + // Time complexity is O(n), not O(nlogn) + minHeap = PriorityQueue(mutableListOf(1, 3, 2, 5, 4)) + println("\nAfter inputting list and building min heap") + printHeap(minHeap) +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_heap/my_heap.kt b/en/codes/kotlin/chapter_heap/my_heap.kt new file mode 100644 index 000000000..034aebff0 --- /dev/null +++ b/en/codes/kotlin/chapter_heap/my_heap.kt @@ -0,0 +1,160 @@ +/** + * File: my_heap.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_heap + +import utils.printHeap +import java.util.* + +/* Max heap */ +class MaxHeap(nums: MutableList?) { + // Use list instead of array, no need to consider capacity expansion + private val maxHeap = mutableListOf() + + /* Constructor, build heap based on input list */ + init { + // Add list elements to heap as is + maxHeap.addAll(nums!!) + // Heapify all nodes except leaf nodes + for (i in parent(size() - 1) downTo 0) { + siftDown(i) + } + } + + /* Get index of left child node */ + private fun left(i: Int): Int { + return 2 * i + 1 + } + + /* Get index of right child node */ + private fun right(i: Int): Int { + return 2 * i + 2 + } + + /* Get index of parent node */ + private fun parent(i: Int): Int { + return (i - 1) / 2 // Floor division + } + + /* Swap elements */ + private fun swap(i: Int, j: Int) { + val temp = maxHeap[i] + maxHeap[i] = maxHeap[j] + maxHeap[j] = temp + } + + /* Get heap size */ + fun size(): Int { + return maxHeap.size + } + + /* Check if heap is empty */ + fun isEmpty(): Boolean { + /* Check if heap is empty */ + return size() == 0 + } + + /* Access top element */ + fun peek(): Int { + return maxHeap[0] + } + + /* Element enters heap */ + fun push(_val: Int) { + // Add node + maxHeap.add(_val) + // Heapify from bottom to top + siftUp(size() - 1) + } + + /* Starting from node i, heapify from bottom to top */ + private fun siftUp(it: Int) { + // Kotlin function parameters are immutable, so create temporary variable + var i = it + while (true) { + // Get parent node of node i + val p = parent(i) + // When "crossing root node" or "node needs no repair", end heapify + if (p < 0 || maxHeap[i] <= maxHeap[p]) break + // Swap two nodes + swap(i, p) + // Loop upward heapify + i = p + } + } + + /* Element exits heap */ + fun pop(): Int { + // Handle empty case + if (isEmpty()) throw IndexOutOfBoundsException() + // Delete node + swap(0, size() - 1) + // Remove node + val _val = maxHeap.removeAt(size() - 1) + // Return top element + siftDown(0) + // Return heap top element + return _val + } + + /* Starting from node i, heapify from top to bottom */ + private fun siftDown(it: Int) { + // Kotlin function parameters are immutable, so create temporary variable + var i = it + while (true) { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + val l = left(i) + val r = right(i) + var ma = i + if (l < size() && maxHeap[l] > maxHeap[ma]) ma = l + if (r < size() && maxHeap[r] > maxHeap[ma]) ma = r + // Swap two nodes + if (ma == i) break + // Swap two nodes + swap(i, ma) + // Loop downwards heapification + i = ma + } + } + + /* Driver Code */ + fun print() { + val queue = PriorityQueue { a: Int, b: Int -> b - a } + queue.addAll(maxHeap) + printHeap(queue) + } +} + +/* Driver Code */ +fun main() { + /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + val maxHeap = MaxHeap(mutableListOf(9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2)) + println("\nAfter inputting list and building heap") + maxHeap.print() + + /* Check if heap is empty */ + var peek = maxHeap.peek() + print("\nHeap top element is $peek\n") + + /* Element enters heap */ + val _val = 7 + maxHeap.push(_val) + print("\nAfter element $_val pushes to heap\n") + maxHeap.print() + + /* Time complexity is O(n), not O(nlogn) */ + peek = maxHeap.pop() + print("\nAfter heap top element $peek pops from heap\n") + maxHeap.print() + + /* Get heap size */ + val size = maxHeap.size() + print("\nHeap size is $size\n") + + /* Check if heap is empty */ + val isEmpty = maxHeap.isEmpty() + print("\nIs heap empty $isEmpty\n") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_heap/top_k.kt b/en/codes/kotlin/chapter_heap/top_k.kt new file mode 100644 index 000000000..80109baaf --- /dev/null +++ b/en/codes/kotlin/chapter_heap/top_k.kt @@ -0,0 +1,38 @@ +/** + * File: top_k.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_heap + +import utils.printHeap +import java.util.* + +/* Find the largest k elements in array based on heap */ +fun topKHeap(nums: IntArray, k: Int): Queue { + // Python's heapq module implements min heap by default + val heap = PriorityQueue() + // Enter the first k elements of array into heap + for (i in 0.. heap.peek()) { + heap.poll() + heap.offer(nums[i]) + } + } + return heap +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(1, 7, 6, 3, 2) + val k = 3 + val res = topKHeap(nums, k) + println("The largest $k elements are") + printHeap(res) +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_searching/binary_search.kt b/en/codes/kotlin/chapter_searching/binary_search.kt new file mode 100644 index 000000000..3052e3ad7 --- /dev/null +++ b/en/codes/kotlin/chapter_searching/binary_search.kt @@ -0,0 +1,59 @@ +/** + * File: binary_search.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_searching + +/* Binary search (closed interval on both sides) */ +fun binarySearch(nums: IntArray, target: Int): Int { + // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + var i = 0 + var j = nums.size - 1 + // Loop, exit when the search interval is empty (empty when i > j) + while (i <= j) { + val m = i + (j - i) / 2 // Calculate the midpoint index m + if (nums[m] < target) // This means target is in the interval [m+1, j] + i = m + 1 + else if (nums[m] > target) // This means target is in the interval [i, m-1] + j = m - 1 + else // Found the target element, return its index + return m + } + // Target element not found, return -1 + return -1 +} + +/* Binary search (left-closed right-open interval) */ +fun binarySearchLCRO(nums: IntArray, target: Int): Int { + // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + var i = 0 + var j = nums.size + // Loop, exit when the search interval is empty (empty when i = j) + while (i < j) { + val m = i + (j - i) / 2 // Calculate the midpoint index m + if (nums[m] < target) // This means target is in the interval [m+1, j) + i = m + 1 + else if (nums[m] > target) // This means target is in the interval [i, m) + j = m + else // Found the target element, return its index + return m + } + // Target element not found, return -1 + return -1 +} + +/* Driver Code */ +fun main() { + val target = 6 + val nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35) + + /* Binary search (closed interval on both sides) */ + var index = binarySearch(nums, target) + println("Index of target element 6 = $index") + + /* Binary search (left-closed right-open interval) */ + index = binarySearchLCRO(nums, target) + println("Index of target element 6 = $index") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_searching/binary_search_edge.kt b/en/codes/kotlin/chapter_searching/binary_search_edge.kt new file mode 100644 index 000000000..0b1b54a7f --- /dev/null +++ b/en/codes/kotlin/chapter_searching/binary_search_edge.kt @@ -0,0 +1,48 @@ +/** + * File: binary_search_edge.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_searching + +/* Binary search for the leftmost target */ +fun binarySearchLeftEdge(nums: IntArray, target: Int): Int { + // Equivalent to finding the insertion point of target + val i = binarySearchInsertion(nums, target) + // Target not found, return -1 + if (i == nums.size || nums[i] != target) { + return -1 + } + // Found target, return index i + return i +} + +/* Binary search for the rightmost target */ +fun binarySearchRightEdge(nums: IntArray, target: Int): Int { + // Convert to finding the leftmost target + 1 + val i = binarySearchInsertion(nums, target + 1) + // j points to the rightmost target, i points to the first element greater than target + val j = i - 1 + // Target not found, return -1 + if (j == -1 || nums[j] != target) { + return -1 + } + // Found target, return index j + return j +} + +/* Driver Code */ +fun main() { + // Array with duplicate elements + val nums = intArrayOf(1, 3, 6, 6, 6, 6, 6, 10, 12, 15) + println("\nArray nums = ${nums.contentToString()}") + + // Binary search left and right boundaries + for (target in intArrayOf(6, 7)) { + var index = binarySearchLeftEdge(nums, target) + println("Leftmost element $target index is $index") + index = binarySearchRightEdge(nums, target) + println("Rightmost element $target index is $index") + } +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_searching/binary_search_insertion.kt b/en/codes/kotlin/chapter_searching/binary_search_insertion.kt new file mode 100644 index 000000000..5093a80e7 --- /dev/null +++ b/en/codes/kotlin/chapter_searching/binary_search_insertion.kt @@ -0,0 +1,65 @@ +/** + * File: binary_search_insertion.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_searching + +/* Binary search for insertion point (no duplicate elements) */ +fun binarySearchInsertionSimple(nums: IntArray, target: Int): Int { + var i = 0 + var j = nums.size - 1 // Initialize closed interval [0, n-1] + while (i <= j) { + val m = i + (j - i) / 2 // Calculate the midpoint index m + if (nums[m] < target) { + i = m + 1 // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1 // target is in the interval [i, m-1] + } else { + return m // Found target, return insertion point m + } + } + // Target not found, return insertion point i + return i +} + +/* Binary search for insertion point (with duplicate elements) */ +fun binarySearchInsertion(nums: IntArray, target: Int): Int { + var i = 0 + var j = nums.size - 1 // Initialize closed interval [0, n-1] + while (i <= j) { + val m = i + (j - i) / 2 // Calculate the midpoint index m + if (nums[m] < target) { + i = m + 1 // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1 // target is in the interval [i, m-1] + } else { + j = m - 1 // The first element less than target is in the interval [i, m-1] + } + } + // Return insertion point i + return i +} + +/* Driver Code */ +fun main() { + // Array without duplicate elements + var nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35) + println("\nArray nums = ${nums.contentToString()}") + // Binary search for insertion point + for (target in intArrayOf(6, 9)) { + val index = binarySearchInsertionSimple(nums, target) + println("Insertion point index for element $target is $index") + } + + // Array with duplicate elements + nums = intArrayOf(1, 3, 6, 6, 6, 6, 6, 10, 12, 15) + println("\nArray nums = ${nums.contentToString()}") + + // Binary search for insertion point + for (target in intArrayOf(2, 6, 20)) { + val index = binarySearchInsertion(nums, target) + println("Insertion point index for element $target is $index") + } +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_searching/hashing_search.kt b/en/codes/kotlin/chapter_searching/hashing_search.kt new file mode 100644 index 000000000..74c4abf65 --- /dev/null +++ b/en/codes/kotlin/chapter_searching/hashing_search.kt @@ -0,0 +1,49 @@ +/** + * File: hashing_search.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_searching + +import utils.ListNode + +/* Hash search (array) */ +fun hashingSearchArray(map: Map, target: Int): Int { + // Hash table key: target element, _val: index + // If this key does not exist in the hash table, return -1 + return map.getOrDefault(target, -1) +} + +/* Hash search (linked list) */ +fun hashingSearchLinkedList(map: Map, target: Int): ListNode? { + // Hash table key: target node value, _val: node object + // If key is not in hash table, return null + return map.getOrDefault(target, null) +} + +/* Driver Code */ +fun main() { + val target = 3 + + /* Hash search (array) */ + val nums = intArrayOf(1, 5, 3, 2, 4, 7, 5, 9, 10, 8) + // Initialize hash table + val map = HashMap() + for (i in nums.indices) { + map[nums[i]] = i // key: element, _val: index + } + val index = hashingSearchArray(map, target) + println("Index of target element 3 = $index") + + /* Hash search (linked list) */ + var head = ListNode.arrToLinkedList(nums) + // Initialize hash table + val map1 = HashMap() + while (head != null) { + map1[head._val] = head // key: node value, _val: node + head = head.next + } + val node = hashingSearchLinkedList(map1, target) + println("Node object corresponding to target node value 3 is $node") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_searching/linear_search.kt b/en/codes/kotlin/chapter_searching/linear_search.kt new file mode 100644 index 000000000..b1c5e6587 --- /dev/null +++ b/en/codes/kotlin/chapter_searching/linear_search.kt @@ -0,0 +1,50 @@ +/** + * File: linear_search.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_searching + +import utils.ListNode + +/* Linear search (array) */ +fun linearSearchArray(nums: IntArray, target: Int): Int { + // Traverse array + for (i in nums.indices) { + // Found the target element, return its index + if (nums[i] == target) + return i + } + // Target element not found, return -1 + return -1 +} + +/* Linear search (linked list) */ +fun linearSearchLinkedList(h: ListNode?, target: Int): ListNode? { + // Traverse the linked list + var head = h + while (head != null) { + // Found the target node, return it + if (head._val == target) + return head + head = head.next + } + // Target node not found, return null + return null +} + +/* Driver Code */ +fun main() { + val target = 3 + + /* Perform linear search in array */ + val nums = intArrayOf(1, 5, 3, 2, 4, 7, 5, 9, 10, 8) + val index = linearSearchArray(nums, target) + println("Index of target element 3 = $index") + + /* Perform linear search in linked list */ + val head = ListNode.arrToLinkedList(nums) + val node = linearSearchLinkedList(head, target) + println("Node object corresponding to target node value 3 is $node") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_searching/two_sum.kt b/en/codes/kotlin/chapter_searching/two_sum.kt new file mode 100644 index 000000000..a93d6e033 --- /dev/null +++ b/en/codes/kotlin/chapter_searching/two_sum.kt @@ -0,0 +1,49 @@ +/** + * File: two_sum.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_searching + +/* Method 1: Brute force enumeration */ +fun twoSumBruteForce(nums: IntArray, target: Int): IntArray { + val size = nums.size + // Two nested loops, time complexity is O(n^2) + for (i in 0..() + // Single loop, time complexity is O(n) + for (i in 0.. nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + val temp = nums[j] + nums[j] = nums[j + 1] + nums[j + 1] = temp + } + } + } +} + +/* Bubble sort (flag optimization) */ +fun bubbleSortWithFlag(nums: IntArray) { + // Outer loop: unsorted range is [0, i] + for (i in nums.size - 1 downTo 1) { + var flag = false // Initialize flag + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (j in 0.. nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + val temp = nums[j] + nums[j] = nums[j + 1] + nums[j + 1] = temp + flag = true // Record element swap + } + } + if (!flag) break // No elements were swapped in this round of "bubbling", exit directly + } +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(4, 1, 3, 1, 5, 2) + bubbleSort(nums) + println("After bubble sort, nums = ${nums.contentToString()}") + + val nums1 = intArrayOf(4, 1, 3, 1, 5, 2) + bubbleSortWithFlag(nums1) + println("After bubble sort, nums1 = ${nums1.contentToString()}") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_sorting/bucket_sort.kt b/en/codes/kotlin/chapter_sorting/bucket_sort.kt new file mode 100644 index 000000000..d97871c67 --- /dev/null +++ b/en/codes/kotlin/chapter_sorting/bucket_sort.kt @@ -0,0 +1,44 @@ +/** + * File: bucket_sort.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* Bucket sort */ +fun bucketSort(nums: FloatArray) { + // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + val k = nums.size / 2 + val buckets = mutableListOf>() + for (i in 0.. nums[ma]) + ma = l + if (r < n && nums[r] > nums[ma]) + ma = r + // Swap two nodes + if (ma == i) + break + // Swap two nodes + val temp = nums[i] + nums[i] = nums[ma] + nums[ma] = temp + // Loop downwards heapification + i = ma + } +} + +/* Heap sort */ +fun heapSort(nums: IntArray) { + // Build heap operation: heapify all nodes except leaves + for (i in nums.size / 2 - 1 downTo 0) { + siftDown(nums, nums.size, i) + } + // Extract the largest element from the heap and repeat for n-1 rounds + for (i in nums.size - 1 downTo 1) { + // Delete node + val temp = nums[0] + nums[0] = nums[i] + nums[i] = temp + // Start heapifying the root node, from top to bottom + siftDown(nums, i, 0) + } +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(4, 1, 3, 1, 5, 2) + heapSort(nums) + println("After heap sort, nums = ${nums.contentToString()}") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_sorting/insertion_sort.kt b/en/codes/kotlin/chapter_sorting/insertion_sort.kt new file mode 100644 index 000000000..e84f2ba6f --- /dev/null +++ b/en/codes/kotlin/chapter_sorting/insertion_sort.kt @@ -0,0 +1,29 @@ +/** + * File: insertion_sort.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* Insertion sort */ +fun insertionSort(nums: IntArray) { + // Outer loop: sorted elements are 1, 2, ..., n + for (i in nums.indices) { + val base = nums[i] + var j = i - 1 + // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j] // Move nums[j] to the right by one position + j-- + } + nums[j + 1] = base // Assign base to the correct position + } +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(4, 1, 3, 1, 5, 2) + insertionSort(nums) + println("After insertion sort, nums = ${nums.contentToString()}") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_sorting/merge_sort.kt b/en/codes/kotlin/chapter_sorting/merge_sort.kt new file mode 100644 index 000000000..65cdd032d --- /dev/null +++ b/en/codes/kotlin/chapter_sorting/merge_sort.kt @@ -0,0 +1,56 @@ +/** + * File: merge_sort.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* Merge left subarray and right subarray */ +fun merge(nums: IntArray, left: Int, mid: Int, right: Int) { + // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + // Create a temporary array tmp to store the merged results + val tmp = IntArray(right - left + 1) + // Initialize the start indices of the left and right subarrays + var i = left + var j = mid + 1 + var k = 0 + // While both subarrays still have elements, compare and copy the smaller element into the temporary array + while (i <= mid && j <= right) { + if (nums[i] <= nums[j]) + tmp[k++] = nums[i++] + else + tmp[k++] = nums[j++] + } + // Copy the remaining elements of the left and right subarrays into the temporary array + while (i <= mid) { + tmp[k++] = nums[i++] + } + while (j <= right) { + tmp[k++] = nums[j++] + } + // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + for (l in tmp.indices) { + nums[left + l] = tmp[l] + } +} + +/* Merge sort */ +fun mergeSort(nums: IntArray, left: Int, right: Int) { + // Termination condition + if (left >= right) return // Terminate recursion when subarray length is 1 + // Divide and conquer stage + val mid = left + (right - left) / 2 // Calculate midpoint + mergeSort(nums, left, mid) // Recursively process the left subarray + mergeSort(nums, mid + 1, right) // Recursively process the right subarray + // Merge stage + merge(nums, left, mid, right) +} + +/* Driver Code */ +fun main() { + /* Merge sort */ + val nums = intArrayOf(7, 3, 2, 6, 0, 1, 5, 4) + mergeSort(nums, 0, nums.size - 1) + println("After merge sort, nums = ${nums.contentToString()}") +} diff --git a/en/codes/kotlin/chapter_sorting/quick_sort.kt b/en/codes/kotlin/chapter_sorting/quick_sort.kt new file mode 100644 index 000000000..257ab0aec --- /dev/null +++ b/en/codes/kotlin/chapter_sorting/quick_sort.kt @@ -0,0 +1,121 @@ +/** + * File: quick_sort.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* Swap elements */ +fun swap(nums: IntArray, i: Int, j: Int) { + val temp = nums[i] + nums[i] = nums[j] + nums[j] = temp +} + +/* Sentinel partition */ +fun partition(nums: IntArray, left: Int, right: Int): Int { + // Use nums[left] as the pivot + var i = left + var j = right + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j-- // Search from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) + i++ // Search from left to right for the first element greater than the pivot + swap(nums, i, j) // Swap these two elements + } + swap(nums, i, left) // Swap the pivot to the boundary between the two subarrays + return i // Return the index of the pivot +} + +/* Quick sort */ +fun quickSort(nums: IntArray, left: Int, right: Int) { + // Terminate recursion when subarray length is 1 + if (left >= right) return + // Sentinel partition + val pivot = partition(nums, left, right) + // Recursively process the left subarray and right subarray + quickSort(nums, left, pivot - 1) + quickSort(nums, pivot + 1, right) +} + +/* Select the median of three candidate elements */ +fun medianThree(nums: IntArray, left: Int, mid: Int, right: Int): Int { + val l = nums[left] + val m = nums[mid] + val r = nums[right] + if ((m in l..r) || (m in r..l)) + return mid // m is between l and r + if ((l in m..r) || (l in r..m)) + return left // l is between m and r + return right +} + +/* Sentinel partition (median of three) */ +fun partitionMedian(nums: IntArray, left: Int, right: Int): Int { + // Select the median of three candidate elements + val med = medianThree(nums, left, (left + right) / 2, right) + // Swap the median to the array's leftmost position + swap(nums, left, med) + // Use nums[left] as the pivot + var i = left + var j = right + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j-- // Search from right to left for the first element smaller than the pivot + while (i < j && nums[i] <= nums[left]) + i++ // Search from left to right for the first element greater than the pivot + swap(nums, i, j) // Swap these two elements + } + swap(nums, i, left) // Swap the pivot to the boundary between the two subarrays + return i // Return the index of the pivot +} + +/* Quick sort */ +fun quickSortMedian(nums: IntArray, left: Int, right: Int) { + // Terminate recursion when subarray length is 1 + if (left >= right) return + // Sentinel partition + val pivot = partitionMedian(nums, left, right) + // Recursively process the left subarray and right subarray + quickSort(nums, left, pivot - 1) + quickSort(nums, pivot + 1, right) +} + +/* Quick sort (recursion depth optimization) */ +fun quickSortTailCall(nums: IntArray, left: Int, right: Int) { + // Terminate when subarray length is 1 + var l = left + var r = right + while (l < r) { + // Sentinel partition operation + val pivot = partition(nums, l, r) + // Perform quick sort on the shorter of the two subarrays + if (pivot - l < r - pivot) { + quickSort(nums, l, pivot - 1) // Recursively sort the left subarray + l = pivot + 1 // Remaining unsorted interval is [pivot + 1, right] + } else { + quickSort(nums, pivot + 1, r) // Recursively sort the right subarray + r = pivot - 1 // Remaining unsorted interval is [left, pivot - 1] + } + } +} + +/* Driver Code */ +fun main() { + /* Quick sort */ + val nums = intArrayOf(2, 4, 1, 0, 3, 5) + quickSort(nums, 0, nums.size - 1) + println("After quick sort, nums = ${nums.contentToString()}") + + /* Quick sort (recursion depth optimization) */ + val nums1 = intArrayOf(2, 4, 1, 0, 3, 5) + quickSortMedian(nums1, 0, nums1.size - 1) + println("After quick sort (median pivot optimization), nums1 = ${nums1.contentToString()}") + + /* Quick sort (recursion depth optimization) */ + val nums2 = intArrayOf(2, 4, 1, 0, 3, 5) + quickSortTailCall(nums2, 0, nums2.size - 1) + println("After quick sort (recursion depth optimization), nums2 = ${nums2.contentToString()}") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_sorting/radix_sort.kt b/en/codes/kotlin/chapter_sorting/radix_sort.kt new file mode 100644 index 000000000..9003f9239 --- /dev/null +++ b/en/codes/kotlin/chapter_sorting/radix_sort.kt @@ -0,0 +1,68 @@ +/** + * File: radix_sort.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* Get the k-th digit of element num, where exp = 10^(k-1) */ +fun digit(num: Int, exp: Int): Int { + // Passing exp instead of k can avoid repeated expensive exponentiation here + return (num / exp) % 10 +} + +/* Counting sort (based on nums k-th digit) */ +fun countingSortDigit(nums: IntArray, exp: Int) { + // Decimal digit range is 0~9, therefore need a bucket array of length 10 + val counter = IntArray(10) + val n = nums.size + // Count the occurrence of digits 0~9 + for (i in 0.. m) m = num + var exp = 1 + // Traverse from the lowest to the highest digit + while (exp <= m) { + // Perform counting sort on the k-th digit of array elements + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // i.e., exp = 10^(k-1) + countingSortDigit(nums, exp) + exp *= 10 + } +} + +/* Driver Code */ +fun main() { + // Radix sort + val nums = intArrayOf( + 10546151, 35663510, 42865989, 34862445, 81883077, + 88906420, 72429244, 30524779, 82060337, 63832996 + ) + radixSort(nums) + println("After radix sort, nums = ${nums.contentToString()}") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_sorting/selection_sort.kt b/en/codes/kotlin/chapter_sorting/selection_sort.kt new file mode 100644 index 000000000..c0eab82ec --- /dev/null +++ b/en/codes/kotlin/chapter_sorting/selection_sort.kt @@ -0,0 +1,32 @@ +/** + * File: selection_sort.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* Selection sort */ +fun selectionSort(nums: IntArray) { + val n = nums.size + // Outer loop: unsorted interval is [i, n-1] + for (i in 0..() + + /* Get the length of the stack */ + fun size(): Int { + return stack.size + } + + /* Check if the stack is empty */ + fun isEmpty(): Boolean { + return size() == 0 + } + + /* Push */ + fun push(num: Int) { + stack.add(num) + } + + /* Pop */ + fun pop(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return stack.removeAt(size() - 1) + } + + /* Return list for printing */ + fun peek(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return stack[size() - 1] + } + + /* Convert List to Array and return */ + fun toArray(): Array { + return stack.toTypedArray() + } +} + +/* Driver Code */ +fun main() { + /* Access top of the stack element */ + val stack = ArrayStack() + + /* Elements push onto stack */ + stack.push(1) + stack.push(3) + stack.push(2) + stack.push(5) + stack.push(4) + println("Stack stack = ${stack.toArray().contentToString()}") + + /* Return list for printing */ + val peek = stack.peek() + println("Top element peek = $peek") + + /* Element pop from stack */ + val pop = stack.pop() + println("Pop element pop = $pop, after pop stack = ${stack.toArray().contentToString()}") + + /* Get the length of the stack */ + val size = stack.size() + println("Stack length size = $size") + + /* Check if empty */ + val isEmpty = stack.isEmpty() + println("Is stack empty = $isEmpty") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/deque.kt b/en/codes/kotlin/chapter_stack_and_queue/deque.kt new file mode 100644 index 000000000..ccbde78bd --- /dev/null +++ b/en/codes/kotlin/chapter_stack_and_queue/deque.kt @@ -0,0 +1,45 @@ +/** + * File: deque.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_stack_and_queue + +import java.util.* + +/* Driver Code */ +fun main() { + /* Get the length of the double-ended queue */ + val deque = LinkedList() + deque.offerLast(3) + deque.offerLast(2) + deque.offerLast(5) + println("Deque deque = $deque") + + /* Update element */ + val peekFirst = deque.peekFirst() + println("Front element peekFirst = $peekFirst") + val peekLast = deque.peekLast() + println("Rear element peekLast = $peekLast") + + /* Elements enqueue */ + deque.offerLast(4) + println("After element 4 enqueues at rear, deque = $deque") + deque.offerFirst(1) + println("After element 1 enqueues at front, deque = $deque") + + /* Element dequeue */ + val popLast = deque.pollLast() + println("Dequeue rear element = $popLast, after rear dequeue deque = $deque") + val popFirst = deque.pollFirst() + println("Dequeue front element = $popFirst, after front dequeue deque = $deque") + + /* Get the length of the double-ended queue */ + val size = deque.size + println("Deque length size = $size") + + /* Check if the double-ended queue is empty */ + val isEmpty = deque.isEmpty() + println("Is deque empty = $isEmpty") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/linkedlist_deque.kt b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_deque.kt new file mode 100644 index 000000000..75b555c85 --- /dev/null +++ b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_deque.kt @@ -0,0 +1,163 @@ +/** + * File: linkedlist_deque.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_stack_and_queue + +/* Doubly linked list node */ +class ListNode(var _val: Int) { + // Node value + var next: ListNode? = null // Successor node reference + var prev: ListNode? = null // Predecessor node reference +} + +/* Double-ended queue based on doubly linked list implementation */ +class LinkedListDeque { + private var front: ListNode? = null // Head node front + private var rear: ListNode? = null // Tail node rear + private var queSize: Int = 0 // Length of the double-ended queue + + /* Get the length of the double-ended queue */ + fun size(): Int { + return queSize + } + + /* Check if the double-ended queue is empty */ + fun isEmpty(): Boolean { + return size() == 0 + } + + /* Enqueue operation */ + fun push(num: Int, isFront: Boolean) { + val node = ListNode(num) + // If the linked list is empty, make both front and rear point to node + if (isEmpty()) { + rear = node + front = rear + // Front of the queue enqueue operation + } else if (isFront) { + // Add node to the head of the linked list + front?.prev = node + node.next = front + front = node // Update head node + // Rear of the queue enqueue operation + } else { + // Add node to the tail of the linked list + rear?.next = node + node.prev = rear + rear = node // Update tail node + } + queSize++ // Update queue length + } + + /* Front of the queue enqueue */ + fun pushFirst(num: Int) { + push(num, true) + } + + /* Rear of the queue enqueue */ + fun pushLast(num: Int) { + push(num, false) + } + + /* Dequeue operation */ + fun pop(isFront: Boolean): Int { + if (isEmpty()) + throw IndexOutOfBoundsException() + val _val: Int + // Temporarily store head node value + if (isFront) { + _val = front!!._val // Delete head node + // Delete head node + val fNext = front!!.next + if (fNext != null) { + fNext.prev = null + front!!.next = null + } + front = fNext // Update head node + // Temporarily store tail node value + } else { + _val = rear!!._val // Delete tail node + // Update tail node + val rPrev = rear!!.prev + if (rPrev != null) { + rPrev.next = null + rear!!.prev = null + } + rear = rPrev // Update tail node + } + queSize-- // Update queue length + return _val + } + + /* Rear of the queue dequeue */ + fun popFirst(): Int { + return pop(true) + } + + /* Access rear of the queue element */ + fun popLast(): Int { + return pop(false) + } + + /* Return list for printing */ + fun peekFirst(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return front!!._val + } + + /* Driver Code */ + fun peekLast(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return rear!!._val + } + + /* Return array for printing */ + fun toArray(): IntArray { + var node = front + val res = IntArray(size()) + for (i in res.indices) { + res[i] = node!!._val + node = node.next + } + return res + } +} + +/* Driver Code */ +fun main() { + /* Get the length of the double-ended queue */ + val deque = LinkedListDeque() + deque.pushLast(3) + deque.pushLast(2) + deque.pushLast(5) + println("Deque deque = ${deque.toArray().contentToString()}") + + /* Update element */ + val peekFirst = deque.peekFirst() + println("Front element peekFirst = $peekFirst") + val peekLast = deque.peekLast() + println("Rear element peekLast = $peekLast") + + /* Elements enqueue */ + deque.pushLast(4) + println("After element 4 enqueues at rear, deque = ${deque.toArray().contentToString()}") + deque.pushFirst(1) + println("After element 1 enqueues at front, deque = ${deque.toArray().contentToString()}") + + /* Element dequeue */ + val popLast = deque.popLast() + println("Dequeue rear element = ${popLast}, after rear dequeue deque = ${deque.toArray().contentToString()}") + val popFirst = deque.popFirst() + println("Dequeue front element = ${popFirst}, after front dequeue deque = ${deque.toArray().contentToString()}") + + /* Get the length of the double-ended queue */ + val size = deque.size() + println("Deque length size = $size") + + /* Check if the double-ended queue is empty */ + val isEmpty = deque.isEmpty() + println("Is deque empty = $isEmpty") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/linkedlist_queue.kt b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_queue.kt new file mode 100644 index 000000000..13af0dbd3 --- /dev/null +++ b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_queue.kt @@ -0,0 +1,98 @@ +/** + * File: linkedlist_queue.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_stack_and_queue + +/* Queue based on linked list implementation */ +class LinkedListQueue( + // Head node front, tail node rear + private var front: ListNode? = null, + private var rear: ListNode? = null, + private var queSize: Int = 0 +) { + + /* Get the length of the queue */ + fun size(): Int { + return queSize + } + + /* Check if the queue is empty */ + fun isEmpty(): Boolean { + return size() == 0 + } + + /* Enqueue */ + fun push(num: Int) { + // Add num after the tail node + val node = ListNode(num) + // If the queue is empty, make both front and rear point to the node + if (front == null) { + front = node + rear = node + // If the queue is not empty, add the node after the tail node + } else { + rear?.next = node + rear = node + } + queSize++ + } + + /* Dequeue */ + fun pop(): Int { + val num = peek() + // Delete head node + front = front?.next + queSize-- + return num + } + + /* Return list for printing */ + fun peek(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return front!!._val + } + + /* Convert linked list to Array and return */ + fun toArray(): IntArray { + var node = front + val res = IntArray(size()) + for (i in res.indices) { + res[i] = node!!._val + node = node.next + } + return res + } +} + +/* Driver Code */ +fun main() { + /* Access front of the queue element */ + val queue = LinkedListQueue() + + /* Elements enqueue */ + queue.push(1) + queue.push(3) + queue.push(2) + queue.push(5) + queue.push(4) + println("Queue queue = ${queue.toArray().contentToString()}") + + /* Return list for printing */ + val peek = queue.peek() + println("Front element peek = $peek") + + /* Element dequeue */ + val pop = queue.pop() + println("Dequeue element pop = $pop, after dequeue queue = ${queue.toArray().contentToString()}") + + /* Get the length of the queue */ + val size = queue.size() + println("Queue length size = $size") + + /* Check if the queue is empty */ + val isEmpty = queue.isEmpty() + println("Is queue empty = $isEmpty") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/linkedlist_stack.kt b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_stack.kt new file mode 100644 index 000000000..574a5c073 --- /dev/null +++ b/en/codes/kotlin/chapter_stack_and_queue/linkedlist_stack.kt @@ -0,0 +1,87 @@ +/** + * File: linkedlist_stack.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_stack_and_queue + +/* Stack based on linked list implementation */ +class LinkedListStack( + private var stackPeek: ListNode? = null, // Use head node as stack top + private var stkSize: Int = 0 // Stack length +) { + + /* Get the length of the stack */ + fun size(): Int { + return stkSize + } + + /* Check if the stack is empty */ + fun isEmpty(): Boolean { + return size() == 0 + } + + /* Push */ + fun push(num: Int) { + val node = ListNode(num) + node.next = stackPeek + stackPeek = node + stkSize++ + } + + /* Pop */ + fun pop(): Int? { + val num = peek() + stackPeek = stackPeek?.next + stkSize-- + return num + } + + /* Return list for printing */ + fun peek(): Int? { + if (isEmpty()) throw IndexOutOfBoundsException() + return stackPeek?._val + } + + /* Convert List to Array and return */ + fun toArray(): IntArray { + var node = stackPeek + val res = IntArray(size()) + for (i in res.size - 1 downTo 0) { + res[i] = node?._val!! + node = node.next + } + return res + } +} + +/* Driver Code */ +fun main() { + /* Access top of the stack element */ + val stack = LinkedListStack() + + /* Elements push onto stack */ + stack.push(1) + stack.push(3) + stack.push(2) + stack.push(5) + stack.push(4) + println("Stack stack = ${stack.toArray().contentToString()}") + + /* Return list for printing */ + val peek = stack.peek()!! + println("Top element peek = $peek") + + /* Element pop from stack */ + val pop = stack.pop()!! + println("Pop element pop = $pop, after pop stack = ${stack.toArray().contentToString()}") + + /* Get the length of the stack */ + val size = stack.size() + println("Stack length size = $size") + + /* Check if empty */ + val isEmpty = stack.isEmpty() + println("Is stack empty = $isEmpty") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/queue.kt b/en/codes/kotlin/chapter_stack_and_queue/queue.kt new file mode 100644 index 000000000..cbfc562b3 --- /dev/null +++ b/en/codes/kotlin/chapter_stack_and_queue/queue.kt @@ -0,0 +1,39 @@ +/** + * File: queue.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_stack_and_queue + +import java.util.* + +/* Driver Code */ +fun main() { + /* Access front of the queue element */ + val queue = LinkedList() + + /* Elements enqueue */ + queue.offer(1) + queue.offer(3) + queue.offer(2) + queue.offer(5) + queue.offer(4) + println("Queue queue = $queue") + + /* Return list for printing */ + val peek = queue.peek() + println("Front element peek = $peek") + + /* Element dequeue */ + val pop = queue.poll() + println("Dequeue element pop = $pop, after dequeue queue = $queue") + + /* Get the length of the queue */ + val size = queue.size + println("Queue length size = $size") + + /* Check if the queue is empty */ + val isEmpty = queue.isEmpty() + println("Is queue empty = $isEmpty") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_stack_and_queue/stack.kt b/en/codes/kotlin/chapter_stack_and_queue/stack.kt new file mode 100644 index 000000000..81e09dbad --- /dev/null +++ b/en/codes/kotlin/chapter_stack_and_queue/stack.kt @@ -0,0 +1,39 @@ +/** + * File: stack.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_stack_and_queue + +import java.util.* + +/* Driver Code */ +fun main() { + /* Access top of the stack element */ + val stack = Stack() + + /* Elements push onto stack */ + stack.push(1) + stack.push(3) + stack.push(2) + stack.push(5) + stack.push(4) + println("Stack stack = $stack") + + /* Return list for printing */ + val peek = stack.peek() + println("Top element peek = $peek") + + /* Element pop from stack */ + val pop = stack.pop() + println("Pop element pop = $pop, after pop stack = $stack") + + /* Get the length of the stack */ + val size = stack.size + println("Stack length size = $size") + + /* Check if empty */ + val isEmpty = stack.isEmpty() + println("Is stack empty = $isEmpty") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/array_binary_tree.kt b/en/codes/kotlin/chapter_tree/array_binary_tree.kt new file mode 100644 index 000000000..51a195a96 --- /dev/null +++ b/en/codes/kotlin/chapter_tree/array_binary_tree.kt @@ -0,0 +1,127 @@ +/** + * File: array_binary_tree.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree + +/* Binary tree class represented by array */ +class ArrayBinaryTree(private val tree: MutableList) { + /* List capacity */ + fun size(): Int { + return tree.size + } + + /* Get value of node at index i */ + fun _val(i: Int): Int? { + // If index out of bounds, return null to represent empty position + if (i < 0 || i >= size()) return null + return tree[i] + } + + /* Get index of left child node of node at index i */ + fun left(i: Int): Int { + return 2 * i + 1 + } + + /* Get index of right child node of node at index i */ + fun right(i: Int): Int { + return 2 * i + 2 + } + + /* Get index of parent node of node at index i */ + fun parent(i: Int): Int { + return (i - 1) / 2 + } + + /* Level-order traversal */ + fun levelOrder(): MutableList { + val res = mutableListOf() + // Traverse array directly + for (i in 0..) { + // If empty position, return + if (_val(i) == null) + return + // Preorder traversal + if ("pre" == order) + res.add(_val(i)) + dfs(left(i), order, res) + // Inorder traversal + if ("in" == order) + res.add(_val(i)) + dfs(right(i), order, res) + // Postorder traversal + if ("post" == order) + res.add(_val(i)) + } + + /* Preorder traversal */ + fun preOrder(): MutableList { + val res = mutableListOf() + dfs(0, "pre", res) + return res + } + + /* Inorder traversal */ + fun inOrder(): MutableList { + val res = mutableListOf() + dfs(0, "in", res) + return res + } + + /* Postorder traversal */ + fun postOrder(): MutableList { + val res = mutableListOf() + dfs(0, "post", res) + return res + } +} + +/* Driver Code */ +fun main() { + // Initialize binary tree + // Here we use a function to generate binary tree directly from list + val arr = mutableListOf(1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15) + + val root = TreeNode.listToTree(arr) + println("\nInitialize binary tree\n") + println("Array representation of binary tree:") + println(arr) + println("Linked list representation of binary tree:") + printTree(root) + + // Binary tree class represented by array + val abt = ArrayBinaryTree(arr) + + // Access node + val i = 1 + val l = abt.left(i) + val r = abt.right(i) + val p = abt.parent(i) + println("Current node index is $i, value is ${abt._val(i)}") + println("Its left child index is $l, value is ${abt._val(l)}") + println("Its right child index is $r, value is ${abt._val(r)}") + println("Its parent node index is $p, value is ${abt._val(p)}") + + // Traverse tree + var res = abt.levelOrder() + println("\nLevel-order traversal is: $res") + res = abt.preOrder() + println("Pre-order traversal is: $res") + res = abt.inOrder() + println("In-order traversal is: $res") + res = abt.postOrder() + println("Post-order traversal is: $res") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/avl_tree.kt b/en/codes/kotlin/chapter_tree/avl_tree.kt new file mode 100644 index 000000000..ec99a6958 --- /dev/null +++ b/en/codes/kotlin/chapter_tree/avl_tree.kt @@ -0,0 +1,223 @@ +/** + * File: avl_tree.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree +import kotlin.math.max + +/* AVL tree */ +class AVLTree { + var root: TreeNode? = null // Root node + + /* Get node height */ + fun height(node: TreeNode?): Int { + // Empty node height is -1, leaf node height is 0 + return node?.height ?: -1 + } + + /* Update node height */ + private fun updateHeight(node: TreeNode?) { + // Node height equals the height of the tallest subtree + 1 + node?.height = max(height(node?.left), height(node?.right)) + 1 + } + + /* Get balance factor */ + fun balanceFactor(node: TreeNode?): Int { + // Empty node balance factor is 0 + if (node == null) return 0 + // Node balance factor = left subtree height - right subtree height + return height(node.left) - height(node.right) + } + + /* Right rotation operation */ + private fun rightRotate(node: TreeNode?): TreeNode { + val child = node!!.left + val grandChild = child!!.right + // Using child as pivot, rotate node to the right + child.right = node + node.left = grandChild + // Update node height + updateHeight(node) + updateHeight(child) + // Return root node of subtree after rotation + return child + } + + /* Left rotation operation */ + private fun leftRotate(node: TreeNode?): TreeNode { + val child = node!!.right + val grandChild = child!!.left + // Using child as pivot, rotate node to the left + child.left = node + node.right = grandChild + // Update node height + updateHeight(node) + updateHeight(child) + // Return root node of subtree after rotation + return child + } + + /* Perform rotation operation to restore balance to this subtree */ + private fun rotate(node: TreeNode): TreeNode { + // Get balance factor of node + val balanceFactor = balanceFactor(node) + // Left-leaning tree + if (balanceFactor > 1) { + if (balanceFactor(node.left) >= 0) { + // Right rotation + return rightRotate(node) + } else { + // First left rotation then right rotation + node.left = leftRotate(node.left) + return rightRotate(node) + } + } + // Right-leaning tree + if (balanceFactor < -1) { + if (balanceFactor(node.right) <= 0) { + // Left rotation + return leftRotate(node) + } else { + // First right rotation then left rotation + node.right = rightRotate(node.right) + return leftRotate(node) + } + } + // Balanced tree, no rotation needed, return directly + return node + } + + /* Insert node */ + fun insert(_val: Int) { + root = insertHelper(root, _val) + } + + /* Recursively insert node (helper method) */ + private fun insertHelper(n: TreeNode?, _val: Int): TreeNode { + if (n == null) + return TreeNode(_val) + var node = n + /* 1. Find insertion position and insert node */ + if (_val < node._val) + node.left = insertHelper(node.left, _val) + else if (_val > node._val) + node.right = insertHelper(node.right, _val) + else + return node // Duplicate node not inserted, return directly + updateHeight(node) // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = rotate(node) + // Return root node of subtree + return node + } + + /* Remove node */ + fun remove(_val: Int) { + root = removeHelper(root, _val) + } + + /* Recursively delete node (helper method) */ + private fun removeHelper(n: TreeNode?, _val: Int): TreeNode? { + var node = n ?: return null + /* 1. Find node and delete */ + if (_val < node._val) + node.left = removeHelper(node.left, _val) + else if (_val > node._val) + node.right = removeHelper(node.right, _val) + else { + if (node.left == null || node.right == null) { + val child = if (node.left != null) + node.left + else + node.right + // Number of child nodes = 0, delete node directly and return + if (child == null) + return null + // Number of child nodes = 1, delete node directly + else + node = child + } else { + // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + var temp = node.right + while (temp!!.left != null) { + temp = temp.left + } + node.right = removeHelper(node.right, temp._val) + node._val = temp._val + } + } + updateHeight(node) // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = rotate(node) + // Return root node of subtree + return node + } + + /* Search node */ + fun search(_val: Int): TreeNode? { + var cur = root + // Loop search, exit after passing leaf node + while (cur != null) { + // Target node is in cur's right subtree + cur = if (cur._val < _val) + cur.right!! + // Target node is in cur's left subtree + else if (cur._val > _val) + cur.left + // Found target node, exit loop + else + break + } + // Return target node + return cur + } +} + +fun testInsert(tree: AVLTree, _val: Int) { + tree.insert(_val) + println("\nAfter inserting node $_val, AVL tree is") + printTree(tree.root) +} + +fun testRemove(tree: AVLTree, _val: Int) { + tree.remove(_val) + println("\nAfter deleting node $_val, AVL tree is") + printTree(tree.root) +} + +/* Driver Code */ +fun main() { + /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + val avlTree = AVLTree() + + /* Insert node */ + // Delete nodes + testInsert(avlTree, 1) + testInsert(avlTree, 2) + testInsert(avlTree, 3) + testInsert(avlTree, 4) + testInsert(avlTree, 5) + testInsert(avlTree, 8) + testInsert(avlTree, 7) + testInsert(avlTree, 9) + testInsert(avlTree, 10) + testInsert(avlTree, 6) + + /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + testInsert(avlTree, 7) + + /* Remove node */ + // Delete node with degree 1 + testRemove(avlTree, 8) // Delete node with degree 2 + testRemove(avlTree, 5) // Remove node with degree 1 + testRemove(avlTree, 4) // Remove node with degree 2 + + /* Search node */ + val node = avlTree.search(7) + println("\n Found node object is $node, node value = ${node?._val}") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/binary_search_tree.kt b/en/codes/kotlin/chapter_tree/binary_search_tree.kt new file mode 100644 index 000000000..d07341954 --- /dev/null +++ b/en/codes/kotlin/chapter_tree/binary_search_tree.kt @@ -0,0 +1,157 @@ +/** + * File: binary_search_tree.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree + +/* Binary search tree */ +class BinarySearchTree { + // Initialize empty tree + private var root: TreeNode? = null + + /* Get binary tree root node */ + fun getRoot(): TreeNode? { + return root + } + + /* Search node */ + fun search(num: Int): TreeNode? { + var cur = root + // Loop search, exit after passing leaf node + while (cur != null) { + // Target node is in cur's right subtree + cur = if (cur._val < num) + cur.right + // Target node is in cur's left subtree + else if (cur._val > num) + cur.left + // Found target node, exit loop + else + break + } + // Return target node + return cur + } + + /* Insert node */ + fun insert(num: Int) { + // If tree is empty, initialize root node + if (root == null) { + root = TreeNode(num) + return + } + var cur = root + var pre: TreeNode? = null + // Loop search, exit after passing leaf node + while (cur != null) { + // Found duplicate node, return directly + if (cur._val == num) + return + pre = cur + // Insertion position is in cur's right subtree + cur = if (cur._val < num) + cur.right + // Insertion position is in cur's left subtree + else + cur.left + } + // Insert node + val node = TreeNode(num) + if (pre?._val!! < num) + pre.right = node + else + pre.left = node + } + + /* Remove node */ + fun remove(num: Int) { + // If tree is empty, return directly + if (root == null) + return + var cur = root + var pre: TreeNode? = null + // Loop search, exit after passing leaf node + while (cur != null) { + // Found node to delete, exit loop + if (cur._val == num) + break + pre = cur + // Node to delete is in cur's right subtree + cur = if (cur._val < num) + cur.right + // Node to delete is in cur's left subtree + else + cur.left + } + // If no node to delete, return directly + if (cur == null) + return + // Number of child nodes = 0 or 1 + if (cur.left == null || cur.right == null) { + // When number of child nodes = 0 / 1, child = null / that child node + val child = if (cur.left != null) + cur.left + else + cur.right + // Delete node cur + if (cur != root) { + if (pre!!.left == cur) + pre.left = child + else + pre.right = child + } else { + // If deleted node is root node, reassign root node + root = child + } + // Number of child nodes = 2 + } else { + // Get next node of cur in inorder traversal + var tmp = cur.right + while (tmp!!.left != null) { + tmp = tmp.left + } + // Recursively delete node tmp + remove(tmp._val) + // Replace cur with tmp + cur._val = tmp._val + } + } +} + +/* Driver Code */ +fun main() { + /* Initialize binary search tree */ + val bst = BinarySearchTree() + // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + val nums = intArrayOf(8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15) + for (num in nums) { + bst.insert(num) + } + println("\nInitialized binary tree is\n") + printTree(bst.getRoot()) + + /* Search node */ + val node = bst.search(7) + println("Found node object is $node, node value = ${node?._val}") + + /* Insert node */ + bst.insert(16) + println("\nAfter inserting node 16, binary tree is\n") + printTree(bst.getRoot()) + + /* Remove node */ + bst.remove(1) + println("\nAfter removing node 1, binary tree is\n") + printTree(bst.getRoot()) + bst.remove(2) + println("\nAfter removing node 2, binary tree is\n") + printTree(bst.getRoot()) + bst.remove(4) + println("\nAfter removing node 4, binary tree is\n") + printTree(bst.getRoot()) +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/binary_tree.kt b/en/codes/kotlin/chapter_tree/binary_tree.kt new file mode 100644 index 000000000..53fe6b4dc --- /dev/null +++ b/en/codes/kotlin/chapter_tree/binary_tree.kt @@ -0,0 +1,40 @@ +/** + * File: binary_tree.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree + +/* Driver Code */ +fun main() { + /* Initialize binary tree */ + // Initialize nodes + val n1 = TreeNode(1) + val n2 = TreeNode(2) + val n3 = TreeNode(3) + val n4 = TreeNode(4) + val n5 = TreeNode(5) + // Build references (pointers) between nodes + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 + println("\nInitialize binary tree\n") + printTree(n1) + + /* Insert node P between n1 -> n2 */ + val P = TreeNode(0) + // Delete node + n1.left = P + P.left = n2 + println("\nAfter inserting node P\n") + printTree(n1) + // Remove node P + n1.left = n2 + println("\nAfter removing node P\n") + printTree(n1) +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/binary_tree_bfs.kt b/en/codes/kotlin/chapter_tree/binary_tree_bfs.kt new file mode 100644 index 000000000..84656fdc9 --- /dev/null +++ b/en/codes/kotlin/chapter_tree/binary_tree_bfs.kt @@ -0,0 +1,42 @@ +/** + * File: binary_tree_bfs.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree +import java.util.* + +/* Level-order traversal */ +fun levelOrder(root: TreeNode?): MutableList { + // Initialize queue, add root node + val queue = LinkedList() + queue.add(root) + // Initialize a list to save the traversal sequence + val list = mutableListOf() + while (queue.isNotEmpty()) { + val node = queue.poll() // Dequeue + list.add(node?._val!!) // Save node value + if (node.left != null) + queue.offer(node.left) // Left child node enqueue + if (node.right != null) + queue.offer(node.right) // Right child node enqueue + } + return list +} + +/* Driver Code */ +fun main() { + /* Initialize binary tree */ + // Here we use a function to generate binary tree directly from list + val root = TreeNode.listToTree(mutableListOf(1, 2, 3, 4, 5, 6, 7)) + println("\nInitialize binary tree\n") + printTree(root) + + /* Level-order traversal */ + val list = levelOrder(root) + println("\nLevel-order traversal node print sequence = $list") +} \ No newline at end of file diff --git a/en/codes/kotlin/chapter_tree/binary_tree_dfs.kt b/en/codes/kotlin/chapter_tree/binary_tree_dfs.kt new file mode 100644 index 000000000..cad1f6239 --- /dev/null +++ b/en/codes/kotlin/chapter_tree/binary_tree_dfs.kt @@ -0,0 +1,64 @@ +/** + * File: binary_tree_dfs.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree + +// Initialize list for storing traversal sequence +var list = mutableListOf() + +/* Preorder traversal */ +fun preOrder(root: TreeNode?) { + if (root == null) return + // Visit priority: root node -> left subtree -> right subtree + list.add(root._val) + preOrder(root.left) + preOrder(root.right) +} + +/* Inorder traversal */ +fun inOrder(root: TreeNode?) { + if (root == null) return + // Visit priority: left subtree -> root node -> right subtree + inOrder(root.left) + list.add(root._val) + inOrder(root.right) +} + +/* Postorder traversal */ +fun postOrder(root: TreeNode?) { + if (root == null) return + // Visit priority: left subtree -> right subtree -> root node + postOrder(root.left) + postOrder(root.right) + list.add(root._val) +} + +/* Driver Code */ +fun main() { + /* Initialize binary tree */ + // Here we use a function to generate binary tree directly from list + val root = TreeNode.listToTree(mutableListOf(1, 2, 3, 4, 5, 6, 7)) + println("\nInitialize binary tree\n") + printTree(root) + + /* Preorder traversal */ + list.clear() + preOrder(root) + println("\nPre-order traversal node print sequence = $list") + + /* Inorder traversal */ + list.clear() + inOrder(root) + println("\nIn-order traversal node print sequence = $list") + + /* Postorder traversal */ + list.clear() + postOrder(root) + println("\nPost-order traversal node print sequence = $list") +} \ No newline at end of file diff --git a/en/codes/kotlin/utils/ListNode.kt b/en/codes/kotlin/utils/ListNode.kt new file mode 100644 index 000000000..218161c1a --- /dev/null +++ b/en/codes/kotlin/utils/ListNode.kt @@ -0,0 +1,25 @@ +/** + * File: ListNode.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package utils + +/* Linked list node */ +class ListNode(var _val: Int) { + var next: ListNode? = null + + companion object { + /* Deserialize a list into a linked list */ + fun arrToLinkedList(arr: IntArray): ListNode? { + val dum = ListNode(0) + var head = dum + for (_val in arr) { + head.next = ListNode(_val) + head = head.next!! + } + return dum.next + } + } +} \ No newline at end of file diff --git a/en/codes/kotlin/utils/PrintUtil.kt b/en/codes/kotlin/utils/PrintUtil.kt new file mode 100644 index 000000000..04a138ab3 --- /dev/null +++ b/en/codes/kotlin/utils/PrintUtil.kt @@ -0,0 +1,107 @@ +/** + * File: PrintUtil.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package utils + +import java.util.* + +class Trunk(var prev: Trunk?, var str: String) + +/* Print matrix (Array) */ +fun printMatrix(matrix: Array>) { + println("[") + for (row in matrix) { + println(" $row,") + } + println("]") +} + +/* Print matrix (List) */ +fun printMatrix(matrix: MutableList>) { + println("[") + for (row in matrix) { + println(" $row,") + } + println("]") +} + +/* Print linked list */ +fun printLinkedList(h: ListNode?) { + var head = h + val list = mutableListOf() + while (head != null) { + list.add(head._val.toString()) + head = head.next + } + println(list.joinToString(separator = " -> ")) +} + +/* Print binary tree */ +fun printTree(root: TreeNode?) { + printTree(root, null, false) +} + +/** + * Print binary tree + * This tree printer is borrowed from TECHIE DELIGHT + * https://www.techiedelight.com/c-program-print-binary-tree/ + */ +fun printTree(root: TreeNode?, prev: Trunk?, isRight: Boolean) { + if (root == null) { + return + } + + var prevStr = " " + val trunk = Trunk(prev, prevStr) + + printTree(root.right, trunk, true) + + if (prev == null) { + trunk.str = "———" + } else if (isRight) { + trunk.str = "/———" + prevStr = " |" + } else { + trunk.str = "\\———" + prev.str = prevStr + } + + showTrunks(trunk) + println(" ${root._val}") + + if (prev != null) { + prev.str = prevStr + } + trunk.str = " |" + + printTree(root.left, trunk, false) +} + +fun showTrunks(p: Trunk?) { + if (p == null) { + return + } + showTrunks(p.prev) + print(p.str) +} + +/* Print hash table */ +fun printHashMap(map: Map) { + for ((key, value) in map) { + println("${key.toString()} -> $value") + } +} + +/* Print heap */ +fun printHeap(queue: Queue?) { + val list = mutableListOf() + queue?.let { list.addAll(it) } + print("Heap array representation:") + println(list) + println("Heap tree representation:") + val root = TreeNode.listToTree(list) + printTree(root) +} \ No newline at end of file diff --git a/en/codes/kotlin/utils/TreeNode.kt b/en/codes/kotlin/utils/TreeNode.kt new file mode 100644 index 000000000..ccbfebdf5 --- /dev/null +++ b/en/codes/kotlin/utils/TreeNode.kt @@ -0,0 +1,69 @@ +/** + * File: TreeNode.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package utils + +/* Binary tree node class */ +/* Constructor */ +class TreeNode( + var _val: Int // Node value +) { + var height: Int = 0 // Node height + var left: TreeNode? = null // Reference to left child node + var right: TreeNode? = null // Reference to right child node + + // For the serialization encoding rules, please refer to: + // https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ + // Array representation of binary tree: + // [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] + // Linked list representation of binary tree: + // /——— 15 + // /——— 7 + // /——— 3 + // | \——— 6 + // | \——— 12 + // ——— 1 + // \——— 2 + // | /——— 9 + // \——— 4 + // \——— 8 + + /* Deserialize a list into a binary tree: recursion */ + companion object { + private fun listToTreeDFS(arr: MutableList, i: Int): TreeNode? { + if (i < 0 || i >= arr.size || arr[i] == null) { + return null + } + val root = TreeNode(arr[i]!!) + root.left = listToTreeDFS(arr, 2 * i + 1) + root.right = listToTreeDFS(arr, 2 * i + 2) + return root + } + + /* Deserialize a list into a binary tree */ + fun listToTree(arr: MutableList): TreeNode? { + return listToTreeDFS(arr, 0) + } + + /* Serialize a binary tree into a list: recursion */ + private fun treeToListDFS(root: TreeNode?, i: Int, res: MutableList) { + if (root == null) return + while (i >= res.size) { + res.add(null) + } + res[i] = root._val + treeToListDFS(root.left, 2 * i + 1, res) + treeToListDFS(root.right, 2 * i + 2, res) + } + + /* Serialize a binary tree into a list */ + fun treeToList(root: TreeNode?): MutableList { + val res = mutableListOf() + treeToListDFS(root, 0, res) + return res + } + } +} \ No newline at end of file diff --git a/en/codes/kotlin/utils/Vertex.kt b/en/codes/kotlin/utils/Vertex.kt new file mode 100644 index 000000000..2af403e38 --- /dev/null +++ b/en/codes/kotlin/utils/Vertex.kt @@ -0,0 +1,30 @@ +/** + * File: Vertex.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package utils + +/* Vertex class */ +class Vertex(val _val: Int) { + companion object { + /* Input value list vals, return vertex list vets */ + fun valsToVets(vals: IntArray): Array { + val vets = arrayOfNulls(vals.size) + for (i in vals.indices) { + vets[i] = Vertex(vals[i]) + } + return vets + } + + /* Input vertex list vets, return value list vals */ + fun vetsToVals(vets: MutableList): MutableList { + val vals = mutableListOf() + for (vet in vets) { + vals.add(vet!!._val) + } + return vals + } + } +} \ No newline at end of file diff --git a/en/codes/python/chapter_array_and_linkedlist/array.py b/en/codes/python/chapter_array_and_linkedlist/array.py index ab7810dcf..480bcc96f 100644 --- a/en/codes/python/chapter_array_and_linkedlist/array.py +++ b/en/codes/python/chapter_array_and_linkedlist/array.py @@ -8,39 +8,39 @@ import random def random_access(nums: list[int]) -> int: - """Random access to elements""" + """Random access to element""" # Randomly select a number from the interval [0, len(nums)-1] random_index = random.randint(0, len(nums) - 1) - # Retrieve and return a random element + # Retrieve and return the random element random_num = nums[random_index] return random_num -# Note that Python's list is a dynamic array that can be extended -# For ease of learning, this function treats the list as a static array +# Please note that Python's list is a dynamic array and can be extended directly +# For learning purposes, this function treats the list as an array with immutable length def extend(nums: list[int], enlarge: int) -> list[int]: """Extend array length""" - # Initialize an extended length array + # Initialize an array with extended length res = [0] * (len(nums) + enlarge) # Copy all elements from the original array to the new array for i in range(len(nums)): res[i] = nums[i] - # Return the new array after expansion + # Return the extended new array return res def insert(nums: list[int], num: int, index: int): - """Insert element num at `index`""" - # Move all elements after `index` one position backward + """Insert element num at index index in the array""" + # Move all elements at and after index index backward by one position for i in range(len(nums) - 1, index, -1): nums[i] = nums[i - 1] - # Assign num to the element at index + # Assign num to the element at index index nums[index] = num def remove(nums: list[int], index: int): - """Remove the element at `index`""" - # Move all elements after `index` one position forward + """Remove the element at index index""" + # Move all elements after index index forward by one position for i in range(index, len(nums) - 1): nums[i] = nums[i + 1] @@ -51,17 +51,17 @@ def traverse(nums: list[int]): # Traverse array by index for i in range(len(nums)): count += nums[i] - # Traverse array elements + # Direct traversal of array elements for num in nums: count += num - # Traverse both data index and elements + # Traverse simultaneously data index and elements for i, num in enumerate(nums): count += nums[i] count += num def find(nums: list[int], target: int) -> int: - """Search for a specified element in the array""" + """Find the specified element in the array""" for i in range(len(nums)): if nums[i] == target: return i @@ -70,7 +70,7 @@ def find(nums: list[int], target: int) -> int: """Driver Code""" if __name__ == "__main__": - # Initialize an array + # Initialize array arr = [0] * 5 print("Array arr =", arr) nums = [1, 3, 2, 5, 4] @@ -78,23 +78,23 @@ if __name__ == "__main__": # Random access random_num: int = random_access(nums) - print("Retrieve a random element in nums", random_num) + print("Get random element from nums", random_num) # Length extension nums: list[int] = extend(nums, 3) - print("Extend the array length to 8, resulting in nums =", nums) + print("Extend the array length to 8, get nums =", nums) # Insert element insert(nums, 6, 3) - print("Insert number 6 at index 3, resulting in nums =", nums) + print("Insert number 6 at index 3, get nums =", nums) # Remove element remove(nums, 2) - print("Remove the element at index 2, resulting in nums =", nums) + print("Remove the element at index 2, get nums =", nums) # Traverse array traverse(nums) - # Search for elements + # Find element index: int = find(nums, 3) - print("Search for element 3 in nums, resulting in index =", index) + print("Search for element 3 in nums, get index =", index) diff --git a/en/codes/python/chapter_array_and_linkedlist/linked_list.py b/en/codes/python/chapter_array_and_linkedlist/linked_list.py index c203c0862..942536b70 100644 --- a/en/codes/python/chapter_array_and_linkedlist/linked_list.py +++ b/en/codes/python/chapter_array_and_linkedlist/linked_list.py @@ -29,7 +29,7 @@ def remove(n0: ListNode): def access(head: ListNode, index: int) -> ListNode | None: - """Access the node at `index` in the linked list""" + """Access the node at index index in the linked list""" for _ in range(index): if not head: return None @@ -38,7 +38,7 @@ def access(head: ListNode, index: int) -> ListNode | None: def find(head: ListNode, target: int) -> int: - """Search for the first node with value target in the linked list""" + """Find the first node with value target in the linked list""" index = 0 while head: if head.val == target: @@ -68,18 +68,18 @@ if __name__ == "__main__": # Insert node p = ListNode(0) insert(n0, p) - print("Linked list after inserting the node is") + print("The linked list after inserting a node is") print_linked_list(n0) # Remove node remove(n0) - print("Linked list after removing the node is") + print("The linked list after removing a node is") print_linked_list(n0) # Access node node: ListNode = access(n0, 3) print("The value of the node at index 3 in the linked list = {}".format(node.val)) - # Search node + # Find node index: int = find(n0, 2) print("The index of the node with value 2 in the linked list = {}".format(index)) diff --git a/en/codes/python/chapter_array_and_linkedlist/list.py b/en/codes/python/chapter_array_and_linkedlist/list.py index 32c2c0c24..22851a22a 100644 --- a/en/codes/python/chapter_array_and_linkedlist/list.py +++ b/en/codes/python/chapter_array_and_linkedlist/list.py @@ -12,45 +12,45 @@ if __name__ == "__main__": # Access element x: int = nums[1] - print("\nAccess the element at index 1, resulting in x =", x) + print("\nAccess the element at index 1, get x =", x) # Update element nums[1] = 0 - print("\nUpdate the element at index 1 to 0, resulting in nums =", nums) + print("\nUpdate the element at index 1 to 0, get nums =", nums) # Clear list nums.clear() - print("\nAfter clearing the list, nums =", nums) + print("\nAfter clearing the list nums =", nums) - # Add element at the end + # Add elements at the end nums.append(1) nums.append(3) nums.append(2) nums.append(5) nums.append(4) - print("\nAfter adding the element, nums =", nums) + print("\nAfter adding elements nums =", nums) # Insert element in the middle nums.insert(3, 6) - print("\nInsert number 6 at index 3, resulting in nums =", nums) + print("\nInsert number 6 at index 3, get nums =", nums) # Remove element nums.pop(3) - print("\nRemove the element at index 3, resulting in nums =", nums) + print("\nRemove the element at index 3, get nums =", nums) - # Traverse the list by index + # Traverse list by index count = 0 for i in range(len(nums)): count += nums[i] - # Traverse the list elements + # Direct traversal of list elements for num in nums: count += num # Concatenate two lists nums1 = [6, 8, 7, 10, 9] nums += nums1 - print("\nConcatenate list nums1 to nums, resulting in nums =", nums) + print("\nConcatenate list nums1 to nums, get nums =", nums) # Sort list nums.sort() - print("\nAfter sorting the list, nums =", nums) + print("\nAfter sorting the list nums =", nums) diff --git a/en/codes/python/chapter_array_and_linkedlist/my_list.py b/en/codes/python/chapter_array_and_linkedlist/my_list.py index c1c7361a7..1309019b4 100644 --- a/en/codes/python/chapter_array_and_linkedlist/my_list.py +++ b/en/codes/python/chapter_array_and_linkedlist/my_list.py @@ -13,7 +13,7 @@ class MyList: self._capacity: int = 10 # List capacity self._arr: list[int] = [0] * self._capacity # Array (stores list elements) self._size: int = 0 # List length (current number of elements) - self._extend_ratio: int = 2 # Multiple for each list expansion + self._extend_ratio: int = 2 # Multiple by which the list capacity is extended each time def size(self) -> int: """Get list length (current number of elements)""" @@ -38,7 +38,7 @@ class MyList: def add(self, num: int): """Add element at the end""" - # When the number of elements exceeds capacity, trigger the expansion mechanism + # When the number of elements exceeds capacity, trigger the extension mechanism if self.size() == self.capacity(): self.extend_capacity() self._arr[self._size] = num @@ -48,10 +48,10 @@ class MyList: """Insert element in the middle""" if index < 0 or index >= self._size: raise IndexError("Index out of bounds") - # When the number of elements exceeds capacity, trigger the expansion mechanism + # When the number of elements exceeds capacity, trigger the extension mechanism if self._size == self.capacity(): self.extend_capacity() - # Move all elements after `index` one position backward + # Move all elements at and after index index backward by one position for j in range(self._size - 1, index - 1, -1): self._arr[j + 1] = self._arr[j] self._arr[index] = num @@ -63,7 +63,7 @@ class MyList: if index < 0 or index >= self._size: raise IndexError("Index out of bounds") num = self._arr[index] - # Move all elements after `index` one position forward + # Move all elements after index index forward by one position for j in range(index, self._size - 1): self._arr[j] = self._arr[j + 1] # Update the number of elements @@ -72,14 +72,14 @@ class MyList: return num def extend_capacity(self): - """Extend list""" - # Create a new array of _extend_ratio times the length of the original array and copy the original array to the new array + """Extend list capacity""" + # Create a new array with length _extend_ratio times the original array, and copy the original array to the new array self._arr = self._arr + [0] * self.capacity() * (self._extend_ratio - 1) # Update list capacity self._capacity = len(self._arr) def to_array(self) -> list[int]: - """Return a list of valid lengths""" + """Return list with valid length""" return self._arr[: self._size] @@ -87,32 +87,32 @@ class MyList: if __name__ == "__main__": # Initialize list nums = MyList() - # Add element at the end + # Add elements at the end nums.add(1) nums.add(3) nums.add(2) nums.add(5) nums.add(4) - print(f"List nums = {nums.to_array()} ,capacity = {nums.capacity()} ,length = {nums.size()}") + print(f"List nums = {nums.to_array()}, capacity = {nums.capacity()}, length = {nums.size()}") # Insert element in the middle nums.insert(6, index=3) - print("Insert number 6 at index 3, resulting in nums =", nums.to_array()) + print("Insert number 6 at index 3, get nums =", nums.to_array()) # Remove element nums.remove(3) - print("Remove the element at index 3, resulting in nums =", nums.to_array()) + print("Remove the element at index 3, get nums =", nums.to_array()) # Access element num = nums.get(1) - print("Access the element at index 1, resulting in num =", num) + print("Access the element at index 1, get num =", num) # Update element nums.set(0, 1) - print("Update the element at index 1 to 0, resulting in nums =", nums.to_array()) + print("Update the element at index 1 to 0, get nums =", nums.to_array()) - # Test expansion mechanism + # Test extension mechanism for i in range(10): - # At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism at this time + # At i = 5, the list length will exceed the list capacity, triggering the extension mechanism at this point nums.add(i) - print(f"After expansion, the list {nums.to_array()} ,capacity = {nums.capacity()} ,length = {nums.size()}") + print(f"List after extension {nums.to_array()}, capacity = {nums.capacity()}, length = {nums.size()}") diff --git a/en/codes/python/chapter_backtracking/n_queens.py b/en/codes/python/chapter_backtracking/n_queens.py index 5bb300448..100861a37 100644 --- a/en/codes/python/chapter_backtracking/n_queens.py +++ b/en/codes/python/chapter_backtracking/n_queens.py @@ -14,35 +14,35 @@ def backtrack( diags1: list[bool], diags2: list[bool], ): - """Backtracking algorithm: n queens""" + """Backtracking algorithm: N queens""" # When all rows are placed, record the solution if row == n: res.append([list(row) for row in state]) return # Traverse all columns for col in range(n): - # Calculate the main and minor diagonals corresponding to the cell + # Calculate the main diagonal and anti-diagonal corresponding to this cell diag1 = row - col + n - 1 diag2 = row + col - # Pruning: do not allow queens on the column, main diagonal, or minor diagonal of the cell + # Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell if not cols[col] and not diags1[diag1] and not diags2[diag2]: - # Attempt: place the queen in the cell + # Attempt: place the queen in this cell state[row][col] = "Q" cols[col] = diags1[diag1] = diags2[diag2] = True # Place the next row backtrack(row + 1, n, state, res, cols, diags1, diags2) - # Retract: restore the cell to an empty spot + # Backtrack: restore this cell to an empty cell state[row][col] = "#" cols[col] = diags1[diag1] = diags2[diag2] = False def n_queens(n: int) -> list[list[list[str]]]: - """Solve n queens""" - # Initialize an n*n size chessboard, where 'Q' represents the queen and '#' represents an empty spot + """Solve N queens""" + # Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell state = [["#" for _ in range(n)] for _ in range(n)] - cols = [False] * n # Record columns with queens - diags1 = [False] * (2 * n - 1) # Record main diagonals with queens - diags2 = [False] * (2 * n - 1) # Record minor diagonals with queens + cols = [False] * n # Record whether there is a queen in the column + diags1 = [False] * (2 * n - 1) # Record whether there is a queen on the main diagonal + diags2 = [False] * (2 * n - 1) # Record whether there is a queen on the anti-diagonal res = [] backtrack(0, n, state, res, cols, diags1, diags2) @@ -54,8 +54,8 @@ if __name__ == "__main__": n = 4 res = n_queens(n) - print(f"Input chessboard dimensions as {n}") - print(f"The total number of queen placement solutions is {len(res)}") + print(f"Input chessboard size is {n}") + print(f"There are {len(res)} queen placement solutions") for state in res: print("--------------------") for row in state: diff --git a/en/codes/python/chapter_backtracking/permutations_i.py b/en/codes/python/chapter_backtracking/permutations_i.py index cd4f164b7..75dc63f6a 100644 --- a/en/codes/python/chapter_backtracking/permutations_i.py +++ b/en/codes/python/chapter_backtracking/permutations_i.py @@ -8,7 +8,7 @@ Author: krahets (krahets@163.com) def backtrack( state: list[int], choices: list[int], selected: list[bool], res: list[list[int]] ): - """Backtracking algorithm: Permutation I""" + """Backtracking algorithm: Permutations I""" # When the state length equals the number of elements, record the solution if len(state) == len(choices): res.append(list(state)) @@ -17,18 +17,18 @@ def backtrack( for i, choice in enumerate(choices): # Pruning: do not allow repeated selection of elements if not selected[i]: - # Attempt: make a choice, update the state + # Attempt: make choice, update state selected[i] = True state.append(choice) # Proceed to the next round of selection backtrack(state, choices, selected, res) - # Retract: undo the choice, restore to the previous state + # Backtrack: undo choice, restore to previous state selected[i] = False state.pop() def permutations_i(nums: list[int]) -> list[list[int]]: - """Permutation I""" + """Permutations I""" res = [] backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res) return res diff --git a/en/codes/python/chapter_backtracking/permutations_ii.py b/en/codes/python/chapter_backtracking/permutations_ii.py index 5f18076e8..bdaa6f4d3 100644 --- a/en/codes/python/chapter_backtracking/permutations_ii.py +++ b/en/codes/python/chapter_backtracking/permutations_ii.py @@ -8,7 +8,7 @@ Author: krahets (krahets@163.com) def backtrack( state: list[int], choices: list[int], selected: list[bool], res: list[list[int]] ): - """Backtracking algorithm: Permutation II""" + """Backtracking algorithm: Permutations II""" # When the state length equals the number of elements, record the solution if len(state) == len(choices): res.append(list(state)) @@ -18,19 +18,19 @@ def backtrack( for i, choice in enumerate(choices): # Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements if not selected[i] and choice not in duplicated: - # Attempt: make a choice, update the state - duplicated.add(choice) # Record selected element values + # Attempt: make choice, update state + duplicated.add(choice) # Record the selected element value selected[i] = True state.append(choice) # Proceed to the next round of selection backtrack(state, choices, selected, res) - # Retract: undo the choice, restore to the previous state + # Backtrack: undo choice, restore to previous state selected[i] = False state.pop() def permutations_ii(nums: list[int]) -> list[list[int]]: - """Permutation II""" + """Permutations II""" res = [] backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res) return res diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_i_compact.py b/en/codes/python/chapter_backtracking/preorder_traversal_i_compact.py index 91a7c9cdd..c53aead9d 100644 --- a/en/codes/python/chapter_backtracking/preorder_traversal_i_compact.py +++ b/en/codes/python/chapter_backtracking/preorder_traversal_i_compact.py @@ -12,7 +12,7 @@ from modules import TreeNode, print_tree, list_to_tree def pre_order(root: TreeNode): - """Pre-order traversal: Example one""" + """Preorder traversal: Example 1""" if root is None: return if root.val == 7: @@ -28,7 +28,7 @@ if __name__ == "__main__": print("\nInitialize binary tree") print_tree(root) - # Pre-order traversal + # Preorder traversal res = list[TreeNode]() pre_order(root) diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_ii_compact.py b/en/codes/python/chapter_backtracking/preorder_traversal_ii_compact.py index d6fc3469e..7626190f8 100644 --- a/en/codes/python/chapter_backtracking/preorder_traversal_ii_compact.py +++ b/en/codes/python/chapter_backtracking/preorder_traversal_ii_compact.py @@ -12,7 +12,7 @@ from modules import TreeNode, print_tree, list_to_tree def pre_order(root: TreeNode): - """Pre-order traversal: Example two""" + """Preorder traversal: Example 2""" if root is None: return # Attempt @@ -22,7 +22,7 @@ def pre_order(root: TreeNode): res.append(list(path)) pre_order(root.left) pre_order(root.right) - # Retract + # Backtrack path.pop() @@ -32,11 +32,11 @@ if __name__ == "__main__": print("\nInitialize binary tree") print_tree(root) - # Pre-order traversal + # Preorder traversal path = list[TreeNode]() res = list[list[TreeNode]]() pre_order(root) - print("\nOutput all root-to-node 7 paths") + print("\nOutput all paths from root node to node 7") for path in res: print([node.val for node in path]) diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_iii_compact.py b/en/codes/python/chapter_backtracking/preorder_traversal_iii_compact.py index 9c98318bc..4e36db871 100644 --- a/en/codes/python/chapter_backtracking/preorder_traversal_iii_compact.py +++ b/en/codes/python/chapter_backtracking/preorder_traversal_iii_compact.py @@ -12,7 +12,7 @@ from modules import TreeNode, print_tree, list_to_tree def pre_order(root: TreeNode): - """Pre-order traversal: Example three""" + """Preorder traversal: Example 3""" # Pruning if root is None or root.val == 3: return @@ -23,7 +23,7 @@ def pre_order(root: TreeNode): res.append(list(path)) pre_order(root.left) pre_order(root.right) - # Retract + # Backtrack path.pop() @@ -33,11 +33,11 @@ if __name__ == "__main__": print("\nInitialize binary tree") print_tree(root) - # Pre-order traversal + # Preorder traversal path = list[TreeNode]() res = list[list[TreeNode]]() pre_order(root) - print("\nOutput all root-to-node 7 paths, not including nodes with value 3") + print("\nOutput all paths from root node to node 7, excluding paths with nodes of value 3") for path in res: print([node.val for node in path]) diff --git a/en/codes/python/chapter_backtracking/preorder_traversal_iii_template.py b/en/codes/python/chapter_backtracking/preorder_traversal_iii_template.py index f35c5a485..6a9a7bb97 100644 --- a/en/codes/python/chapter_backtracking/preorder_traversal_iii_template.py +++ b/en/codes/python/chapter_backtracking/preorder_traversal_iii_template.py @@ -12,7 +12,7 @@ from modules import TreeNode, print_tree, list_to_tree def is_solution(state: list[TreeNode]) -> bool: - """Determine if the current state is a solution""" + """Check if the current state is a solution""" return state and state[-1].val == 7 @@ -22,7 +22,7 @@ def record_solution(state: list[TreeNode], res: list[list[TreeNode]]): def is_valid(state: list[TreeNode], choice: TreeNode) -> bool: - """Determine if the choice is legal under the current state""" + """Check if the choice is valid under the current state""" return choice is not None and choice.val != 3 @@ -39,20 +39,20 @@ def undo_choice(state: list[TreeNode], choice: TreeNode): def backtrack( state: list[TreeNode], choices: list[TreeNode], res: list[list[TreeNode]] ): - """Backtracking algorithm: Example three""" - # Check if it's a solution + """Backtracking algorithm: Example 3""" + # Check if it is a solution if is_solution(state): # Record solution record_solution(state, res) # Traverse all choices for choice in choices: - # Pruning: check if the choice is legal + # Pruning: check if the choice is valid if is_valid(state, choice): - # Attempt: make a choice, update the state + # Attempt: make choice, update state make_choice(state, choice) # Proceed to the next round of selection backtrack(state, [choice.left, choice.right], res) - # Retract: undo the choice, restore to the previous state + # Backtrack: undo choice, restore to previous state undo_choice(state, choice) @@ -66,6 +66,6 @@ if __name__ == "__main__": res = [] backtrack(state=[], choices=[root], res=res) - print("\nOutput all root-to-node 7 paths, requiring paths not to include nodes with value 3") + print("\nOutput all paths from root node to node 7, excluding paths with nodes of value 3") for path in res: print([node.val for node in path]) diff --git a/en/codes/python/chapter_backtracking/subset_sum_i.py b/en/codes/python/chapter_backtracking/subset_sum_i.py index a8de835d0..fb5c08f6e 100644 --- a/en/codes/python/chapter_backtracking/subset_sum_i.py +++ b/en/codes/python/chapter_backtracking/subset_sum_i.py @@ -8,28 +8,28 @@ Author: krahets (krahets@163.com) def backtrack( state: list[int], target: int, choices: list[int], start: int, res: list[list[int]] ): - """Backtracking algorithm: Subset Sum I""" + """Backtracking algorithm: Subset sum I""" # When the subset sum equals target, record the solution if target == 0: res.append(list(state)) return # Traverse all choices - # Pruning two: start traversing from start to avoid generating duplicate subsets + # Pruning 2: start traversing from start to avoid generating duplicate subsets for i in range(start, len(choices)): - # Pruning one: if the subset sum exceeds target, end the loop immediately + # Pruning 1: if the subset sum exceeds target, end the loop directly # This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target if target - choices[i] < 0: break - # Attempt: make a choice, update target, start + # Attempt: make choice, update target, start state.append(choices[i]) # Proceed to the next round of selection backtrack(state, target - choices[i], choices, i, res) - # Retract: undo the choice, restore to the previous state + # Backtrack: undo choice, restore to previous state state.pop() def subset_sum_i(nums: list[int], target: int) -> list[list[int]]: - """Solve Subset Sum I""" + """Solve subset sum I""" state = [] # State (subset) nums.sort() # Sort nums start = 0 # Start point for traversal @@ -45,4 +45,4 @@ if __name__ == "__main__": res = subset_sum_i(nums, target) print(f"Input array nums = {nums}, target = {target}") - print(f"All subsets equal to {target} res = {res}") + print(f"All subsets with sum equal to {target} res = {res}") diff --git a/en/codes/python/chapter_backtracking/subset_sum_i_naive.py b/en/codes/python/chapter_backtracking/subset_sum_i_naive.py index 3c471dbb5..8b272cffc 100644 --- a/en/codes/python/chapter_backtracking/subset_sum_i_naive.py +++ b/en/codes/python/chapter_backtracking/subset_sum_i_naive.py @@ -12,26 +12,26 @@ def backtrack( choices: list[int], res: list[list[int]], ): - """Backtracking algorithm: Subset Sum I""" + """Backtracking algorithm: Subset sum I""" # When the subset sum equals target, record the solution if total == target: res.append(list(state)) return # Traverse all choices for i in range(len(choices)): - # Pruning: if the subset sum exceeds target, skip that choice + # Pruning: if the subset sum exceeds target, skip this choice if total + choices[i] > target: continue - # Attempt: make a choice, update elements and total + # Attempt: make choice, update element sum total state.append(choices[i]) # Proceed to the next round of selection backtrack(state, target, total + choices[i], choices, res) - # Retract: undo the choice, restore to the previous state + # Backtrack: undo choice, restore to previous state state.pop() def subset_sum_i_naive(nums: list[int], target: int) -> list[list[int]]: - """Solve Subset Sum I (including duplicate subsets)""" + """Solve subset sum I (including duplicate subsets)""" state = [] # State (subset) total = 0 # Subset sum res = [] # Result list (subset list) @@ -46,5 +46,5 @@ if __name__ == "__main__": res = subset_sum_i_naive(nums, target) print(f"Input array nums = {nums}, target = {target}") - print(f"All subsets equal to {target} res = {res}") - print(f"The result of this method includes duplicate sets") + print(f"All subsets with sum equal to {target} res = {res}") + print(f"Please note that the result output by this method contains duplicate sets") diff --git a/en/codes/python/chapter_backtracking/subset_sum_ii.py b/en/codes/python/chapter_backtracking/subset_sum_ii.py index d404343e3..35f34b3b0 100644 --- a/en/codes/python/chapter_backtracking/subset_sum_ii.py +++ b/en/codes/python/chapter_backtracking/subset_sum_ii.py @@ -8,32 +8,32 @@ Author: krahets (krahets@163.com) def backtrack( state: list[int], target: int, choices: list[int], start: int, res: list[list[int]] ): - """Backtracking algorithm: Subset Sum II""" + """Backtracking algorithm: Subset sum II""" # When the subset sum equals target, record the solution if target == 0: res.append(list(state)) return # Traverse all choices - # Pruning two: start traversing from start to avoid generating duplicate subsets - # Pruning three: start traversing from start to avoid repeatedly selecting the same element + # Pruning 2: start traversing from start to avoid generating duplicate subsets + # Pruning 3: start traversing from start to avoid repeatedly selecting the same element for i in range(start, len(choices)): - # Pruning one: if the subset sum exceeds target, end the loop immediately + # Pruning 1: if the subset sum exceeds target, end the loop directly # This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target if target - choices[i] < 0: break - # Pruning four: if the element equals the left element, it indicates that the search branch is repeated, skip it + # Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly if i > start and choices[i] == choices[i - 1]: continue - # Attempt: make a choice, update target, start + # Attempt: make choice, update target, start state.append(choices[i]) # Proceed to the next round of selection backtrack(state, target - choices[i], choices, i + 1, res) - # Retract: undo the choice, restore to the previous state + # Backtrack: undo choice, restore to previous state state.pop() def subset_sum_ii(nums: list[int], target: int) -> list[list[int]]: - """Solve Subset Sum II""" + """Solve subset sum II""" state = [] # State (subset) nums.sort() # Sort nums start = 0 # Start point for traversal @@ -49,4 +49,4 @@ if __name__ == "__main__": res = subset_sum_ii(nums, target) print(f"Input array nums = {nums}, target = {target}") - print(f"All subsets equal to {target} res = {res}") + print(f"All subsets with sum equal to {target} res = {res}") diff --git a/en/codes/python/chapter_computational_complexity/iteration.py b/en/codes/python/chapter_computational_complexity/iteration.py index 0eb1984d4..61d778da2 100644 --- a/en/codes/python/chapter_computational_complexity/iteration.py +++ b/en/codes/python/chapter_computational_complexity/iteration.py @@ -8,7 +8,7 @@ Author: krahets (krahets@163.com) def for_loop(n: int) -> int: """for loop""" res = 0 - # Loop sum 1, 2, ..., n-1, n + # Sum 1, 2, ..., n-1, n for i in range(1, n + 1): res += i return res @@ -18,7 +18,7 @@ def while_loop(n: int) -> int: """while loop""" res = 0 i = 1 # Initialize condition variable - # Loop sum 1, 2, ..., n-1, n + # Sum 1, 2, ..., n-1, n while i <= n: res += i i += 1 # Update condition variable @@ -29,7 +29,7 @@ def while_loop_ii(n: int) -> int: """while loop (two updates)""" res = 0 i = 1 # Initialize condition variable - # Loop sum 1, 4, 10, ... + # Sum 1, 4, 10, ... while i <= n: res += i # Update condition variable @@ -39,7 +39,7 @@ def while_loop_ii(n: int) -> int: def nested_for_loop(n: int) -> str: - """Double for loop""" + """Nested for loop""" res = "" # Loop i = 1, 2, ..., n-1, n for i in range(1, n + 1): @@ -53,13 +53,13 @@ def nested_for_loop(n: int) -> str: if __name__ == "__main__": n = 5 res = for_loop(n) - print(f"\nfor loop sum result res = {res}") + print(f"\nSum result of for loop res = {res}") res = while_loop(n) - print(f"\nwhile loop sum result res = {res}") + print(f"\nSum result of while loop res = {res}") res = while_loop_ii(n) - print(f"\nwhile loop (two updates) sum result res = {res}") + print(f"\nSum result of while loop (two updates) res = {res}") res = nested_for_loop(n) - print(f"\nDouble for loop traversal result {res}") + print(f"\nTraversal result of nested for loop {res}") diff --git a/en/codes/python/chapter_computational_complexity/recursion.py b/en/codes/python/chapter_computational_complexity/recursion.py index 33bca5221..954915c9d 100644 --- a/en/codes/python/chapter_computational_complexity/recursion.py +++ b/en/codes/python/chapter_computational_complexity/recursion.py @@ -10,24 +10,24 @@ def recur(n: int) -> int: # Termination condition if n == 1: return 1 - # Recursive: recursive call + # Recurse: recursive call res = recur(n - 1) # Return: return result return n + res def for_loop_recur(n: int) -> int: - """Simulate recursion with iteration""" + """Simulate recursion using iteration""" # Use an explicit stack to simulate the system call stack stack = [] res = 0 - # Recursive: recursive call + # Recurse: recursive call for i in range(n, 0, -1): - # Simulate "recursive" by "pushing onto the stack" + # Simulate "recurse" with "push" stack.append(i) # Return: return result while stack: - # Simulate "return" by "popping from the stack" + # Simulate "return" with "pop" res += stack.pop() # res = 1+2+3+...+n return res @@ -43,7 +43,7 @@ def tail_recur(n, res): def fib(n: int) -> int: - """Fibonacci sequence: Recursion""" + """Fibonacci sequence: recursion""" # Termination condition f(1) = 0, f(2) = 1 if n == 1 or n == 2: return n - 1 @@ -57,13 +57,13 @@ def fib(n: int) -> int: if __name__ == "__main__": n = 5 res = recur(n) - print(f"\nRecursive function sum result res = {res}") + print(f"\nSum result of recursive function res = {res}") res = for_loop_recur(n) - print(f"\nSimulate recursion with iteration sum result res = {res}") + print(f"\nSum result of simulating recursion using iteration res = {res}") res = tail_recur(n, 0) - print(f"\nTail recursive function sum result res = {res}") + print(f"\nSum result of tail recursive function res = {res}") res = fib(n) - print(f"\nThe n th term of the Fibonacci sequence is {res}") + print(f"\nThe {n}th term of the Fibonacci sequence is {res}") diff --git a/en/codes/python/chapter_computational_complexity/space_complexity.py b/en/codes/python/chapter_computational_complexity/space_complexity.py index 7d91a16d1..cf20bc795 100644 --- a/en/codes/python/chapter_computational_complexity/space_complexity.py +++ b/en/codes/python/chapter_computational_complexity/space_complexity.py @@ -18,21 +18,21 @@ def function() -> int: def constant(n: int): - """Constant complexity""" + """Constant order""" # Constants, variables, objects occupy O(1) space a = 0 nums = [0] * 10000 node = ListNode(0) - # Variables in a loop occupy O(1) space + # Variables in the loop occupy O(1) space for _ in range(n): c = 0 - # Functions in a loop occupy O(1) space + # Functions in the loop occupy O(1) space for _ in range(n): function() def linear(n: int): - """Linear complexity""" + """Linear order""" # A list of length n occupies O(n) space nums = [0] * n # A hash table of length n occupies O(n) space @@ -42,30 +42,30 @@ def linear(n: int): def linear_recur(n: int): - """Linear complexity (recursive implementation)""" - print("Recursive n =", n) + """Linear order (recursive implementation)""" + print("Recursion n =", n) if n == 1: return linear_recur(n - 1) def quadratic(n: int): - """Quadratic complexity""" - # A two-dimensional list occupies O(n^2) space + """Quadratic order""" + # A 2D list occupies O(n^2) space num_matrix = [[0] * n for _ in range(n)] def quadratic_recur(n: int) -> int: - """Quadratic complexity (recursive implementation)""" + """Quadratic order (recursive implementation)""" if n <= 0: return 0 - # Array nums length = n, n-1, ..., 2, 1 + # Array nums length is n, n-1, ..., 2, 1 nums = [0] * n return quadratic_recur(n - 1) def build_tree(n: int) -> TreeNode | None: - """Exponential complexity (building a full binary tree)""" + """Exponential order (build full binary tree)""" if n == 0: return None root = TreeNode(0) @@ -77,14 +77,14 @@ def build_tree(n: int) -> TreeNode | None: """Driver Code""" if __name__ == "__main__": n = 5 - # Constant complexity + # Constant order constant(n) - # Linear complexity + # Linear order linear(n) linear_recur(n) - # Quadratic complexity + # Quadratic order quadratic(n) quadratic_recur(n) - # Exponential complexity + # Exponential order root = build_tree(n) print_tree(root) diff --git a/en/codes/python/chapter_computational_complexity/time_complexity.py b/en/codes/python/chapter_computational_complexity/time_complexity.py index 661d5dcee..d1ed54447 100644 --- a/en/codes/python/chapter_computational_complexity/time_complexity.py +++ b/en/codes/python/chapter_computational_complexity/time_complexity.py @@ -6,7 +6,7 @@ Author: krahets (krahets@163.com) def constant(n: int) -> int: - """Constant complexity""" + """Constant order""" count = 0 size = 100000 for _ in range(size): @@ -15,7 +15,7 @@ def constant(n: int) -> int: def linear(n: int) -> int: - """Linear complexity""" + """Linear order""" count = 0 for _ in range(n): count += 1 @@ -23,18 +23,18 @@ def linear(n: int) -> int: def array_traversal(nums: list[int]) -> int: - """Linear complexity (traversing an array)""" + """Linear order (traversing array)""" count = 0 - # Loop count is proportional to the length of the array + # Number of iterations is proportional to the array length for num in nums: count += 1 return count def quadratic(n: int) -> int: - """Quadratic complexity""" + """Quadratic order""" count = 0 - # Loop count is squared in relation to the data size n + # Number of iterations is quadratically related to the data size n for i in range(n): for j in range(n): count += 1 @@ -42,26 +42,26 @@ def quadratic(n: int) -> int: def bubble_sort(nums: list[int]) -> int: - """Quadratic complexity (bubble sort)""" + """Quadratic order (bubble sort)""" count = 0 # Counter # Outer loop: unsorted range is [0, i] for i in range(len(nums) - 1, 0, -1): - # Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + # Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range for j in range(i): if nums[j] > nums[j + 1]: # Swap nums[j] and nums[j + 1] tmp: int = nums[j] nums[j] = nums[j + 1] nums[j + 1] = tmp - count += 3 # Element swap includes 3 individual operations + count += 3 # Element swap includes 3 unit operations return count def exponential(n: int) -> int: - """Exponential complexity (loop implementation)""" + """Exponential order (loop implementation)""" count = 0 base = 1 - # Cells split into two every round, forming the sequence 1, 2, 4, 8, ..., 2^(n-1) + # Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) for _ in range(n): for _ in range(base): count += 1 @@ -71,14 +71,14 @@ def exponential(n: int) -> int: def exp_recur(n: int) -> int: - """Exponential complexity (recursive implementation)""" + """Exponential order (recursive implementation)""" if n == 1: return 1 return exp_recur(n - 1) + exp_recur(n - 1) + 1 def logarithmic(n: int) -> int: - """Logarithmic complexity (loop implementation)""" + """Logarithmic order (loop implementation)""" count = 0 while n > 1: n = n / 2 @@ -87,28 +87,30 @@ def logarithmic(n: int) -> int: def log_recur(n: int) -> int: - """Logarithmic complexity (recursive implementation)""" + """Logarithmic order (recursive implementation)""" if n <= 1: return 0 return log_recur(n / 2) + 1 def linear_log_recur(n: int) -> int: - """Linear logarithmic complexity""" + """Linearithmic order""" if n <= 1: return 1 - count: int = linear_log_recur(n // 2) + linear_log_recur(n // 2) + # Divide into two, the scale of subproblems is reduced by half + count = linear_log_recur(n // 2) + linear_log_recur(n // 2) + # Current subproblem contains n operations for _ in range(n): count += 1 return count def factorial_recur(n: int) -> int: - """Factorial complexity (recursive implementation)""" + """Factorial order (recursive implementation)""" if n == 0: return 1 count = 0 - # From 1 split into n + # Split from 1 into n for _ in range(n): count += factorial_recur(n - 1) return count @@ -116,36 +118,36 @@ def factorial_recur(n: int) -> int: """Driver Code""" if __name__ == "__main__": - # Can modify n to experience the trend of operation count changes under various complexities + # You can modify n to run and observe the trend of the number of operations for various complexities n = 8 print("Input data size n =", n) - count: int = constant(n) - print("Constant complexity operation count =", count) + count = constant(n) + print("Number of operations of constant order =", count) - count: int = linear(n) - print("Linear complexity operation count =", count) - count: int = array_traversal([0] * n) - print("Linear complexity (traversing an array) operation count =", count) + count = linear(n) + print("Number of operations of linear order =", count) + count = array_traversal([0] * n) + print("Number of operations of linear order (traversing array) =", count) - count: int = quadratic(n) - print("Quadratic complexity operation count =", count) + count = quadratic(n) + print("Number of operations of quadratic order =", count) nums = [i for i in range(n, 0, -1)] # [n, n-1, ..., 2, 1] - count: int = bubble_sort(nums) - print("Quadratic complexity (bubble sort) operation count =", count) + count = bubble_sort(nums) + print("Number of operations of quadratic order (bubble sort) =", count) - count: int = exponential(n) - print("Exponential complexity (loop implementation) operation count =", count) - count: int = exp_recur(n) - print("Exponential complexity (recursive implementation) operation count =", count) + count = exponential(n) + print("Number of operations of exponential order (loop implementation) =", count) + count = exp_recur(n) + print("Number of operations of exponential order (recursive implementation) =", count) - count: int = logarithmic(n) - print("Logarithmic complexity (loop implementation) operation count =", count) - count: int = log_recur(n) - print("Logarithmic complexity (recursive implementation) operation count =", count) + count = logarithmic(n) + print("Number of operations of logarithmic order (loop implementation) =", count) + count = log_recur(n) + print("Number of operations of logarithmic order (recursive implementation) =", count) - count: int = linear_log_recur(n) - print("Linear logarithmic complexity (recursive implementation) operation count =", count) + count = linear_log_recur(n) + print("Number of operations of linearithmic order (recursive implementation) =", count) - count: int = factorial_recur(n) - print("Factorial complexity (recursive implementation) operation count =", count) + count = factorial_recur(n) + print("Number of operations of factorial order (recursive implementation) =", count) diff --git a/en/codes/python/chapter_computational_complexity/worst_best_time_complexity.py b/en/codes/python/chapter_computational_complexity/worst_best_time_complexity.py index 82a0e3a58..4a67d248d 100644 --- a/en/codes/python/chapter_computational_complexity/worst_best_time_complexity.py +++ b/en/codes/python/chapter_computational_complexity/worst_best_time_complexity.py @@ -8,7 +8,7 @@ import random def random_numbers(n: int) -> list[int]: - """Generate an array with elements: 1, 2, ..., n, order shuffled""" + """Generate an array with elements: 1, 2, ..., n, shuffled in order""" # Generate array nums =: 1, 2, 3, ..., n nums = [i for i in range(1, n + 1)] # Randomly shuffle array elements @@ -19,8 +19,8 @@ def random_numbers(n: int) -> list[int]: def find_one(nums: list[int]) -> int: """Find the index of number 1 in array nums""" for i in range(len(nums)): - # When element 1 is at the start of the array, achieve best time complexity O(1) - # When element 1 is at the end of the array, achieve worst time complexity O(n) + # When element 1 is at the head of the array, best time complexity O(1) is achieved + # When element 1 is at the tail of the array, worst time complexity O(n) is achieved if nums[i] == 1: return i return -1 @@ -32,5 +32,5 @@ if __name__ == "__main__": n = 100 nums: list[int] = random_numbers(n) index: int = find_one(nums) - print("\nThe array [ 1, 2, ..., n ] after being shuffled =", nums) - print("Index of number 1 =", index) + print("\nArray [ 1, 2, ..., n ] after being shuffled =", nums) + print("The index of number 1 is", index) diff --git a/en/codes/python/chapter_divide_and_conquer/binary_search_recur.py b/en/codes/python/chapter_divide_and_conquer/binary_search_recur.py index 2bd9e9bdc..d56139ab1 100644 --- a/en/codes/python/chapter_divide_and_conquer/binary_search_recur.py +++ b/en/codes/python/chapter_divide_and_conquer/binary_search_recur.py @@ -7,26 +7,26 @@ Author: krahets (krahets@163.com) def dfs(nums: list[int], target: int, i: int, j: int) -> int: """Binary search: problem f(i, j)""" - # If the interval is empty, indicating no target element, return -1 + # If the interval is empty, it means there is no target element, return -1 if i > j: return -1 - # Calculate midpoint index m + # Calculate the midpoint index m m = (i + j) // 2 if nums[m] < target: - # Recursive subproblem f(m+1, j) + # Recursion subproblem f(m+1, j) return dfs(nums, target, m + 1, j) elif nums[m] > target: - # Recursive subproblem f(i, m-1) + # Recursion subproblem f(i, m-1) return dfs(nums, target, i, m - 1) else: - # Found the target element, thus return its index + # Found the target element, return its index return m def binary_search(nums: list[int], target: int) -> int: """Binary search""" n = len(nums) - # Solve problem f(0, n-1) + # Solve the problem f(0, n-1) return dfs(nums, target, 0, n - 1) @@ -35,6 +35,6 @@ if __name__ == "__main__": target = 6 nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] - # Binary search (double closed interval) + # Binary search (closed interval on both sides) index = binary_search(nums, target) - print("Index of target element 6 =", index) + print("Index of target element 6 = ", index) diff --git a/en/codes/python/chapter_divide_and_conquer/build_tree.py b/en/codes/python/chapter_divide_and_conquer/build_tree.py index 8c2e96027..9102d42de 100644 --- a/en/codes/python/chapter_divide_and_conquer/build_tree.py +++ b/en/codes/python/chapter_divide_and_conquer/build_tree.py @@ -18,25 +18,25 @@ def dfs( l: int, r: int, ) -> TreeNode | None: - """Build binary tree: Divide and conquer""" - # Terminate when subtree interval is empty + """Build binary tree: divide and conquer""" + # Terminate when the subtree interval is empty if r - l < 0: return None - # Initialize root node + # Initialize the root node root = TreeNode(preorder[i]) - # Query m to divide left and right subtrees + # Query m to divide the left and right subtrees m = inorder_map[preorder[i]] - # Subproblem: build left subtree + # Subproblem: build the left subtree root.left = dfs(preorder, inorder_map, i + 1, l, m - 1) - # Subproblem: build right subtree + # Subproblem: build the right subtree root.right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r) - # Return root node + # Return the root node return root def build_tree(preorder: list[int], inorder: list[int]) -> TreeNode | None: """Build binary tree""" - # Initialize hash table, storing in-order elements to indices mapping + # Initialize hash map, storing the mapping from inorder elements to indices inorder_map = {val: i for i, val in enumerate(inorder)} root = dfs(preorder, inorder_map, 0, 0, len(inorder) - 1) return root @@ -46,8 +46,8 @@ def build_tree(preorder: list[int], inorder: list[int]) -> TreeNode | None: if __name__ == "__main__": preorder = [3, 9, 2, 1, 7] inorder = [9, 3, 1, 2, 7] - print(f"Pre-order traversal = {preorder}") - print(f"In-order traversal = {inorder}") + print(f"Preorder traversal = {preorder}") + print(f"Inorder traversal = {inorder}") root = build_tree(preorder, inorder) print("The built binary tree is:") diff --git a/en/codes/python/chapter_divide_and_conquer/hanota.py b/en/codes/python/chapter_divide_and_conquer/hanota.py index 306d11312..1e5c5fe41 100644 --- a/en/codes/python/chapter_divide_and_conquer/hanota.py +++ b/en/codes/python/chapter_divide_and_conquer/hanota.py @@ -6,37 +6,37 @@ Author: krahets (krahets@163.com) def move(src: list[int], tar: list[int]): - """Move a disc""" - # Take out a disc from the top of src + """Move a disk""" + # Take out a disk from the top of src pan = src.pop() - # Place the disc on top of tar + # Place the disk on top of tar tar.append(pan) def dfs(i: int, src: list[int], buf: list[int], tar: list[int]): """Solve the Tower of Hanoi problem f(i)""" - # If only one disc remains on src, move it to tar + # If there is only one disk left in src, move it directly to tar if i == 1: move(src, tar) return - # Subproblem f(i-1): move the top i-1 discs from src with the help of tar to buf + # Subproblem f(i-1): move the top i-1 disks from src to buf using tar dfs(i - 1, src, tar, buf) - # Subproblem f(1): move the remaining one disc from src to tar + # Subproblem f(1): move the remaining disk from src to tar move(src, tar) - # Subproblem f(i-1): move the top i-1 discs from buf with the help of src to tar + # Subproblem f(i-1): move the top i-1 disks from buf to tar using src dfs(i - 1, buf, src, tar) def solve_hanota(A: list[int], B: list[int], C: list[int]): """Solve the Tower of Hanoi problem""" n = len(A) - # Move the top n discs from A with the help of B to C + # Move the top n disks from A to C using B dfs(n, A, B, C) """Driver Code""" if __name__ == "__main__": - # The tail of the list is the top of the pillar + # The tail of the list is the top of the rod A = [5, 4, 3, 2, 1] B = [] C = [] @@ -47,7 +47,7 @@ if __name__ == "__main__": solve_hanota(A, B, C) - print("After the discs are moved:") + print("After moving the disks:") print(f"A = {A}") print(f"B = {B}") print(f"C = {C}") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_backtrack.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_backtrack.py index 0b2ea8471..e3928681b 100644 --- a/en/codes/python/chapter_dynamic_programming/climbing_stairs_backtrack.py +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_backtrack.py @@ -7,24 +7,24 @@ Author: krahets (krahets@163.com) def backtrack(choices: list[int], state: int, n: int, res: list[int]) -> int: """Backtracking""" - # When climbing to the nth step, add 1 to the number of solutions + # When climbing to the n-th stair, add 1 to the solution count if state == n: res[0] += 1 # Traverse all choices for choice in choices: - # Pruning: do not allow climbing beyond the nth step + # Pruning: not allowed to go beyond the n-th stair if state + choice > n: continue - # Attempt: make a choice, update the state + # Attempt: make a choice, update state backtrack(choices, state + choice, n, res) - # Retract + # Backtrack def climbing_stairs_backtrack(n: int) -> int: """Climbing stairs: Backtracking""" - choices = [1, 2] # Can choose to climb up 1 step or 2 steps - state = 0 # Start climbing from the 0th step - res = [0] # Use res[0] to record the number of solutions + choices = [1, 2] # Can choose to climb up 1 or 2 stairs + state = 0 # Start climbing from the 0-th stair + res = [0] # Use res[0] to record the solution count backtrack(choices, state, n, res) return res[0] @@ -34,4 +34,4 @@ if __name__ == "__main__": n = 9 res = climbing_stairs_backtrack(n) - print(f"Climb {n} steps, there are {res} solutions in total") + print(f"Climbing {n} stairs has {res} solutions") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_constraint_dp.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_constraint_dp.py index 77b5b5030..15ece1800 100644 --- a/en/codes/python/chapter_dynamic_programming/climbing_stairs_constraint_dp.py +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_constraint_dp.py @@ -6,12 +6,12 @@ Author: krahets (krahets@163.com) def climbing_stairs_constraint_dp(n: int) -> int: - """Constrained climbing stairs: Dynamic programming""" + """Climbing stairs with constraint: Dynamic programming""" if n == 1 or n == 2: return 1 - # Initialize dp table, used to store subproblem solutions + # Initialize dp table, used to store solutions to subproblems dp = [[0] * 3 for _ in range(n + 1)] - # Initial state: preset the smallest subproblem solution + # Initial state: preset the solution to the smallest subproblem dp[1][1], dp[1][2] = 1, 0 dp[2][1], dp[2][2] = 0, 1 # State transition: gradually solve larger subproblems from smaller ones @@ -26,4 +26,4 @@ if __name__ == "__main__": n = 9 res = climbing_stairs_constraint_dp(n) - print(f"Climb {n} steps, there are {res} solutions in total") + print(f"Climbing {n} stairs has {res} solutions") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs.py index 018445ea7..f435763f3 100644 --- a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs.py +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs.py @@ -25,4 +25,4 @@ if __name__ == "__main__": n = 9 res = climbing_stairs_dfs(n) - print(f"Climb {n} steps, there are {res} solutions in total") + print(f"Climbing {n} stairs has {res} solutions") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs_mem.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs_mem.py index 2f517a649..a753f5583 100644 --- a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs_mem.py +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dfs_mem.py @@ -6,11 +6,11 @@ Author: krahets (krahets@163.com) def dfs(i: int, mem: list[int]) -> int: - """Memoized search""" + """Memoization search""" # Known dp[1] and dp[2], return them if i == 1 or i == 2: return i - # If there is a record for dp[i], return it + # If record dp[i] exists, return it directly if mem[i] != -1: return mem[i] # dp[i] = dp[i-1] + dp[i-2] @@ -21,8 +21,8 @@ def dfs(i: int, mem: list[int]) -> int: def climbing_stairs_dfs_mem(n: int) -> int: - """Climbing stairs: Memoized search""" - # mem[i] records the total number of solutions for climbing to the ith step, -1 means no record + """Climbing stairs: Memoization search""" + # mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record mem = [-1] * (n + 1) return dfs(n, mem) @@ -32,4 +32,4 @@ if __name__ == "__main__": n = 9 res = climbing_stairs_dfs_mem(n) - print(f"Climb {n} steps, there are {res} solutions in total") + print(f"Climbing {n} stairs has {res} solutions") diff --git a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dp.py b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dp.py index 39cb58174..5cf0f0e9f 100644 --- a/en/codes/python/chapter_dynamic_programming/climbing_stairs_dp.py +++ b/en/codes/python/chapter_dynamic_programming/climbing_stairs_dp.py @@ -9,9 +9,9 @@ def climbing_stairs_dp(n: int) -> int: """Climbing stairs: Dynamic programming""" if n == 1 or n == 2: return n - # Initialize dp table, used to store subproblem solutions + # Initialize dp table, used to store solutions to subproblems dp = [0] * (n + 1) - # Initial state: preset the smallest subproblem solution + # Initial state: preset the solution to the smallest subproblem dp[1], dp[2] = 1, 2 # State transition: gradually solve larger subproblems from smaller ones for i in range(3, n + 1): @@ -34,7 +34,7 @@ if __name__ == "__main__": n = 9 res = climbing_stairs_dp(n) - print(f"Climb {n} steps, there are {res} solutions in total") + print(f"Climbing {n} stairs has {res} solutions") res = climbing_stairs_dp_comp(n) - print(f"Climb {n} steps, there are {res} solutions in total") + print(f"Climbing {n} stairs has {res} solutions") diff --git a/en/codes/python/chapter_dynamic_programming/coin_change.py b/en/codes/python/chapter_dynamic_programming/coin_change.py index 8ed789c51..b4f36f12f 100644 --- a/en/codes/python/chapter_dynamic_programming/coin_change.py +++ b/en/codes/python/chapter_dynamic_programming/coin_change.py @@ -14,14 +14,14 @@ def coin_change_dp(coins: list[int], amt: int) -> int: # State transition: first row and first column for a in range(1, amt + 1): dp[0][a] = MAX - # State transition: the rest of the rows and columns + # State transition: rest of the rows and columns for i in range(1, n + 1): for a in range(1, amt + 1): if coins[i - 1] > a: - # If exceeding the target amount, do not choose coin i + # If exceeds target amount, don't select coin i dp[i][a] = dp[i - 1][a] else: - # The smaller value between not choosing and choosing coin i + # The smaller value between not selecting and selecting coin i dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1) return dp[n][amt] if dp[n][amt] != MAX else -1 @@ -35,13 +35,13 @@ def coin_change_dp_comp(coins: list[int], amt: int) -> int: dp[0] = 0 # State transition for i in range(1, n + 1): - # Traverse in order + # Traverse in forward order for a in range(1, amt + 1): if coins[i - 1] > a: - # If exceeding the target amount, do not choose coin i + # If exceeds target amount, don't select coin i dp[a] = dp[a] else: - # The smaller value between not choosing and choosing coin i + # The smaller value between not selecting and selecting coin i dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1) return dp[amt] if dp[amt] != MAX else -1 @@ -53,8 +53,8 @@ if __name__ == "__main__": # Dynamic programming res = coin_change_dp(coins, amt) - print(f"Minimum number of coins required to reach the target amount = {res}") + print(f"The minimum number of coins needed to make up the target amount is {res}") # Space-optimized dynamic programming res = coin_change_dp_comp(coins, amt) - print(f"Minimum number of coins required to reach the target amount = {res}") + print(f"The minimum number of coins needed to make up the target amount is {res}") diff --git a/en/codes/python/chapter_dynamic_programming/coin_change_ii.py b/en/codes/python/chapter_dynamic_programming/coin_change_ii.py index f389dd536..42db91a75 100644 --- a/en/codes/python/chapter_dynamic_programming/coin_change_ii.py +++ b/en/codes/python/chapter_dynamic_programming/coin_change_ii.py @@ -17,10 +17,10 @@ def coin_change_ii_dp(coins: list[int], amt: int) -> int: for i in range(1, n + 1): for a in range(1, amt + 1): if coins[i - 1] > a: - # If exceeding the target amount, do not choose coin i + # If exceeds target amount, don't select coin i dp[i][a] = dp[i - 1][a] else: - # The sum of the two options of not choosing and choosing coin i + # Sum of the two options: not selecting and selecting coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]] return dp[n][amt] @@ -33,13 +33,13 @@ def coin_change_ii_dp_comp(coins: list[int], amt: int) -> int: dp[0] = 1 # State transition for i in range(1, n + 1): - # Traverse in order + # Traverse in forward order for a in range(1, amt + 1): if coins[i - 1] > a: - # If exceeding the target amount, do not choose coin i + # If exceeds target amount, don't select coin i dp[a] = dp[a] else: - # The sum of the two options of not choosing and choosing coin i + # Sum of the two options: not selecting and selecting coin i dp[a] = dp[a] + dp[a - coins[i - 1]] return dp[amt] diff --git a/en/codes/python/chapter_dynamic_programming/edit_distance.py b/en/codes/python/chapter_dynamic_programming/edit_distance.py index d62ad7355..59d8710c7 100644 --- a/en/codes/python/chapter_dynamic_programming/edit_distance.py +++ b/en/codes/python/chapter_dynamic_programming/edit_distance.py @@ -6,49 +6,49 @@ Author: krahets (krahets@163.com) def edit_distance_dfs(s: str, t: str, i: int, j: int) -> int: - """Edit distance: Brute force search""" + """Edit distance: Brute-force search""" # If both s and t are empty, return 0 if i == 0 and j == 0: return 0 - # If s is empty, return the length of t + # If s is empty, return length of t if i == 0: return j - # If t is empty, return the length of s + # If t is empty, return length of s if j == 0: return i - # If the two characters are equal, skip these two characters + # If two characters are equal, skip both characters if s[i - 1] == t[j - 1]: return edit_distance_dfs(s, t, i - 1, j - 1) - # The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 insert = edit_distance_dfs(s, t, i, j - 1) delete = edit_distance_dfs(s, t, i - 1, j) replace = edit_distance_dfs(s, t, i - 1, j - 1) - # Return the minimum number of edits + # Return minimum edit steps return min(insert, delete, replace) + 1 def edit_distance_dfs_mem(s: str, t: str, mem: list[list[int]], i: int, j: int) -> int: - """Edit distance: Memoized search""" + """Edit distance: Memoization search""" # If both s and t are empty, return 0 if i == 0 and j == 0: return 0 - # If s is empty, return the length of t + # If s is empty, return length of t if i == 0: return j - # If t is empty, return the length of s + # If t is empty, return length of s if j == 0: return i - # If there is a record, return it + # If there's a record, return it directly if mem[i][j] != -1: return mem[i][j] - # If the two characters are equal, skip these two characters + # If two characters are equal, skip both characters if s[i - 1] == t[j - 1]: return edit_distance_dfs_mem(s, t, mem, i - 1, j - 1) - # The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 insert = edit_distance_dfs_mem(s, t, mem, i, j - 1) delete = edit_distance_dfs_mem(s, t, mem, i - 1, j) replace = edit_distance_dfs_mem(s, t, mem, i - 1, j - 1) - # Record and return the minimum number of edits + # Record and return minimum edit steps mem[i][j] = min(insert, delete, replace) + 1 return mem[i][j] @@ -62,14 +62,14 @@ def edit_distance_dp(s: str, t: str) -> int: dp[i][0] = i for j in range(1, m + 1): dp[0][j] = j - # State transition: the rest of the rows and columns + # State transition: rest of the rows and columns for i in range(1, n + 1): for j in range(1, m + 1): if s[i - 1] == t[j - 1]: - # If the two characters are equal, skip these two characters + # If two characters are equal, skip both characters dp[i][j] = dp[i - 1][j - 1] else: - # The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 dp[i][j] = min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1 return dp[n][m] @@ -81,21 +81,21 @@ def edit_distance_dp_comp(s: str, t: str) -> int: # State transition: first row for j in range(1, m + 1): dp[j] = j - # State transition: the rest of the rows + # State transition: rest of the rows for i in range(1, n + 1): # State transition: first column leftup = dp[0] # Temporarily store dp[i-1, j-1] dp[0] += 1 - # State transition: the rest of the columns + # State transition: rest of the columns for j in range(1, m + 1): temp = dp[j] if s[i - 1] == t[j - 1]: - # If the two characters are equal, skip these two characters + # If two characters are equal, skip both characters dp[j] = leftup else: - # The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 + # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 dp[j] = min(dp[j - 1], dp[j], leftup) + 1 - leftup = temp # Update for the next round of dp[i-1, j-1] + leftup = temp # Update for next round's dp[i-1, j-1] return dp[m] @@ -105,19 +105,19 @@ if __name__ == "__main__": t = "pack" n, m = len(s), len(t) - # Brute force search + # Brute-force search res = edit_distance_dfs(s, t, n, m) - print(f"To change {s} to {t}, the minimum number of edits required is {res}") + print(f"Changing {s} to {t} requires a minimum of {res} edits") - # Memoized search + # Memoization search mem = [[-1] * (m + 1) for _ in range(n + 1)] res = edit_distance_dfs_mem(s, t, mem, n, m) - print(f"To change {s} to {t}, the minimum number of edits required is {res}") + print(f"Changing {s} to {t} requires a minimum of {res} edits") # Dynamic programming res = edit_distance_dp(s, t) - print(f"To change {s} to {t}, the minimum number of edits required is {res}") + print(f"Changing {s} to {t} requires a minimum of {res} edits") # Space-optimized dynamic programming res = edit_distance_dp_comp(s, t) - print(f"To change {s} to {t}, the minimum number of edits required is {res}") + print(f"Changing {s} to {t} requires a minimum of {res} edits") diff --git a/en/codes/python/chapter_dynamic_programming/knapsack.py b/en/codes/python/chapter_dynamic_programming/knapsack.py index 8c06208f5..a40281c49 100644 --- a/en/codes/python/chapter_dynamic_programming/knapsack.py +++ b/en/codes/python/chapter_dynamic_programming/knapsack.py @@ -6,43 +6,43 @@ Author: krahets (krahets@163.com) def knapsack_dfs(wgt: list[int], val: list[int], i: int, c: int) -> int: - """0-1 Knapsack: Brute force search""" - # If all items have been chosen or the knapsack has no remaining capacity, return value 0 + """0-1 knapsack: Brute-force search""" + # If all items have been selected or knapsack has no remaining capacity, return value 0 if i == 0 or c == 0: return 0 - # If exceeding the knapsack capacity, can only choose not to put it in the knapsack + # If exceeds knapsack capacity, can only choose not to put it in if wgt[i - 1] > c: return knapsack_dfs(wgt, val, i - 1, c) # Calculate the maximum value of not putting in and putting in item i no = knapsack_dfs(wgt, val, i - 1, c) yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1] - # Return the greater value of the two options + # Return the larger value of the two options return max(no, yes) def knapsack_dfs_mem( wgt: list[int], val: list[int], mem: list[list[int]], i: int, c: int ) -> int: - """0-1 Knapsack: Memoized search""" - # If all items have been chosen or the knapsack has no remaining capacity, return value 0 + """0-1 knapsack: Memoization search""" + # If all items have been selected or knapsack has no remaining capacity, return value 0 if i == 0 or c == 0: return 0 - # If there is a record, return it + # If there's a record, return it directly if mem[i][c] != -1: return mem[i][c] - # If exceeding the knapsack capacity, can only choose not to put it in the knapsack + # If exceeds knapsack capacity, can only choose not to put it in if wgt[i - 1] > c: return knapsack_dfs_mem(wgt, val, mem, i - 1, c) # Calculate the maximum value of not putting in and putting in item i no = knapsack_dfs_mem(wgt, val, mem, i - 1, c) yes = knapsack_dfs_mem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1] - # Record and return the greater value of the two options + # Record and return the larger value of the two options mem[i][c] = max(no, yes) return mem[i][c] def knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: - """0-1 Knapsack: Dynamic programming""" + """0-1 knapsack: Dynamic programming""" n = len(wgt) # Initialize dp table dp = [[0] * (cap + 1) for _ in range(n + 1)] @@ -50,16 +50,16 @@ def knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: for i in range(1, n + 1): for c in range(1, cap + 1): if wgt[i - 1] > c: - # If exceeding the knapsack capacity, do not choose item i + # If exceeds knapsack capacity, don't select item i dp[i][c] = dp[i - 1][c] else: - # The greater value between not choosing and choosing item i + # The larger value between not selecting and selecting item i dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]) return dp[n][cap] def knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: - """0-1 Knapsack: Space-optimized dynamic programming""" + """0-1 knapsack: Space-optimized dynamic programming""" n = len(wgt) # Initialize dp table dp = [0] * (cap + 1) @@ -68,10 +68,10 @@ def knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: # Traverse in reverse order for c in range(cap, 0, -1): if wgt[i - 1] > c: - # If exceeding the knapsack capacity, do not choose item i + # If exceeds knapsack capacity, don't select item i dp[c] = dp[c] else: - # The greater value between not choosing and choosing item i + # The larger value between not selecting and selecting item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) return dp[cap] @@ -83,19 +83,19 @@ if __name__ == "__main__": cap = 50 n = len(wgt) - # Brute force search + # Brute-force search res = knapsack_dfs(wgt, val, n, cap) - print(f"The maximum item value without exceeding knapsack capacity is {res}") + print(f"The maximum item value not exceeding knapsack capacity is {res}") - # Memoized search + # Memoization search mem = [[-1] * (cap + 1) for _ in range(n + 1)] res = knapsack_dfs_mem(wgt, val, mem, n, cap) - print(f"The maximum item value without exceeding knapsack capacity is {res}") + print(f"The maximum item value not exceeding knapsack capacity is {res}") # Dynamic programming res = knapsack_dp(wgt, val, cap) - print(f"The maximum item value without exceeding knapsack capacity is {res}") + print(f"The maximum item value not exceeding knapsack capacity is {res}") # Space-optimized dynamic programming res = knapsack_dp_comp(wgt, val, cap) - print(f"The maximum item value without exceeding knapsack capacity is {res}") + print(f"The maximum item value not exceeding knapsack capacity is {res}") diff --git a/en/codes/python/chapter_dynamic_programming/min_cost_climbing_stairs_dp.py b/en/codes/python/chapter_dynamic_programming/min_cost_climbing_stairs_dp.py index 47730c185..94abbfd4a 100644 --- a/en/codes/python/chapter_dynamic_programming/min_cost_climbing_stairs_dp.py +++ b/en/codes/python/chapter_dynamic_programming/min_cost_climbing_stairs_dp.py @@ -6,13 +6,13 @@ Author: krahets (krahets@163.com) def min_cost_climbing_stairs_dp(cost: list[int]) -> int: - """Climbing stairs with minimum cost: Dynamic programming""" + """Minimum cost climbing stairs: Dynamic programming""" n = len(cost) - 1 if n == 1 or n == 2: return cost[n] - # Initialize dp table, used to store subproblem solutions + # Initialize dp table, used to store solutions to subproblems dp = [0] * (n + 1) - # Initial state: preset the smallest subproblem solution + # Initial state: preset the solution to the smallest subproblem dp[1], dp[2] = cost[1], cost[2] # State transition: gradually solve larger subproblems from smaller ones for i in range(3, n + 1): @@ -21,7 +21,7 @@ def min_cost_climbing_stairs_dp(cost: list[int]) -> int: def min_cost_climbing_stairs_dp_comp(cost: list[int]) -> int: - """Climbing stairs with minimum cost: Space-optimized dynamic programming""" + """Minimum cost climbing stairs: Space-optimized dynamic programming""" n = len(cost) - 1 if n == 1 or n == 2: return cost[n] @@ -34,10 +34,10 @@ def min_cost_climbing_stairs_dp_comp(cost: list[int]) -> int: """Driver Code""" if __name__ == "__main__": cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1] - print(f"Enter the list of stair costs as {cost}") + print(f"Input stair cost list is {cost}") res = min_cost_climbing_stairs_dp(cost) - print(f"Minimum cost to climb the stairs {res}") + print(f"The minimum cost to finish climbing the stairs is {res}") res = min_cost_climbing_stairs_dp_comp(cost) - print(f"Minimum cost to climb the stairs {res}") + print(f"The minimum cost to finish climbing the stairs is {res}") diff --git a/en/codes/python/chapter_dynamic_programming/min_path_sum.py b/en/codes/python/chapter_dynamic_programming/min_path_sum.py index 2c9a14739..48cdeb00e 100644 --- a/en/codes/python/chapter_dynamic_programming/min_path_sum.py +++ b/en/codes/python/chapter_dynamic_programming/min_path_sum.py @@ -8,37 +8,37 @@ from math import inf def min_path_sum_dfs(grid: list[list[int]], i: int, j: int) -> int: - """Minimum path sum: Brute force search""" + """Minimum path sum: Brute-force search""" # If it's the top-left cell, terminate the search if i == 0 and j == 0: return grid[0][0] - # If the row or column index is out of bounds, return a +∞ cost + # If row or column index is out of bounds, return +∞ cost if i < 0 or j < 0: return inf - # Calculate the minimum path cost from the top-left to (i-1, j) and (i, j-1) + # Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) up = min_path_sum_dfs(grid, i - 1, j) left = min_path_sum_dfs(grid, i, j - 1) - # Return the minimum path cost from the top-left to (i, j) + # Return the minimum path cost from top-left to (i, j) return min(left, up) + grid[i][j] def min_path_sum_dfs_mem( grid: list[list[int]], mem: list[list[int]], i: int, j: int ) -> int: - """Minimum path sum: Memoized search""" + """Minimum path sum: Memoization search""" # If it's the top-left cell, terminate the search if i == 0 and j == 0: return grid[0][0] - # If the row or column index is out of bounds, return a +∞ cost + # If row or column index is out of bounds, return +∞ cost if i < 0 or j < 0: return inf - # If there is a record, return it + # If there's a record, return it directly if mem[i][j] != -1: return mem[i][j] - # The minimum path cost from the left and top cells + # Minimum path cost for left and upper cells up = min_path_sum_dfs_mem(grid, mem, i - 1, j) left = min_path_sum_dfs_mem(grid, mem, i, j - 1) - # Record and return the minimum path cost from the top-left to (i, j) + # Record and return the minimum path cost from top-left to (i, j) mem[i][j] = min(left, up) + grid[i][j] return mem[i][j] @@ -55,7 +55,7 @@ def min_path_sum_dp(grid: list[list[int]]) -> int: # State transition: first column for i in range(1, n): dp[i][0] = dp[i - 1][0] + grid[i][0] - # State transition: the rest of the rows and columns + # State transition: rest of the rows and columns for i in range(1, n): for j in range(1, m): dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j] @@ -71,11 +71,11 @@ def min_path_sum_dp_comp(grid: list[list[int]]) -> int: dp[0] = grid[0][0] for j in range(1, m): dp[j] = dp[j - 1] + grid[0][j] - # State transition: the rest of the rows + # State transition: rest of the rows for i in range(1, n): # State transition: first column dp[0] = dp[0] + grid[i][0] - # State transition: the rest of the columns + # State transition: rest of the columns for j in range(1, m): dp[j] = min(dp[j - 1], dp[j]) + grid[i][j] return dp[m - 1] @@ -86,19 +86,19 @@ if __name__ == "__main__": grid = [[1, 3, 1, 5], [2, 2, 4, 2], [5, 3, 2, 1], [4, 3, 5, 2]] n, m = len(grid), len(grid[0]) - # Brute force search + # Brute-force search res = min_path_sum_dfs(grid, n - 1, m - 1) - print(f"The minimum path sum from the top-left to the bottom-right corner is {res}") + print(f"The minimum path sum from top-left to bottom-right is {res}") - # Memoized search + # Memoization search mem = [[-1] * m for _ in range(n)] res = min_path_sum_dfs_mem(grid, mem, n - 1, m - 1) - print(f"The minimum path sum from the top-left to the bottom-right corner is {res}") + print(f"The minimum path sum from top-left to bottom-right is {res}") # Dynamic programming res = min_path_sum_dp(grid) - print(f"The minimum path sum from the top-left to the bottom-right corner is {res}") + print(f"The minimum path sum from top-left to bottom-right is {res}") # Space-optimized dynamic programming res = min_path_sum_dp_comp(grid) - print(f"The minimum path sum from the top-left to the bottom-right corner is {res}") + print(f"The minimum path sum from top-left to bottom-right is {res}") diff --git a/en/codes/python/chapter_dynamic_programming/unbounded_knapsack.py b/en/codes/python/chapter_dynamic_programming/unbounded_knapsack.py index e2ad1de7b..72b568c15 100644 --- a/en/codes/python/chapter_dynamic_programming/unbounded_knapsack.py +++ b/en/codes/python/chapter_dynamic_programming/unbounded_knapsack.py @@ -6,7 +6,7 @@ Author: krahets (krahets@163.com) def unbounded_knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: - """Complete knapsack: Dynamic programming""" + """Unbounded knapsack: Dynamic programming""" n = len(wgt) # Initialize dp table dp = [[0] * (cap + 1) for _ in range(n + 1)] @@ -14,28 +14,28 @@ def unbounded_knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: for i in range(1, n + 1): for c in range(1, cap + 1): if wgt[i - 1] > c: - # If exceeding the knapsack capacity, do not choose item i + # If exceeds knapsack capacity, don't select item i dp[i][c] = dp[i - 1][c] else: - # The greater value between not choosing and choosing item i + # The larger value between not selecting and selecting item i dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]) return dp[n][cap] def unbounded_knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: - """Complete knapsack: Space-optimized dynamic programming""" + """Unbounded knapsack: Space-optimized dynamic programming""" n = len(wgt) # Initialize dp table dp = [0] * (cap + 1) # State transition for i in range(1, n + 1): - # Traverse in order + # Traverse in forward order for c in range(1, cap + 1): if wgt[i - 1] > c: - # If exceeding the knapsack capacity, do not choose item i + # If exceeds knapsack capacity, don't select item i dp[c] = dp[c] else: - # The greater value between not choosing and choosing item i + # The larger value between not selecting and selecting item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) return dp[cap] @@ -48,8 +48,8 @@ if __name__ == "__main__": # Dynamic programming res = unbounded_knapsack_dp(wgt, val, cap) - print(f"The maximum item value without exceeding knapsack capacity is {res}") + print(f"The maximum item value not exceeding knapsack capacity is {res}") # Space-optimized dynamic programming res = unbounded_knapsack_dp_comp(wgt, val, cap) - print(f"The maximum item value without exceeding knapsack capacity is {res}") + print(f"The maximum item value not exceeding knapsack capacity is {res}") diff --git a/en/codes/python/chapter_graph/graph_adjacency_list.py b/en/codes/python/chapter_graph/graph_adjacency_list.py index ecc24eb8d..adbd43827 100644 --- a/en/codes/python/chapter_graph/graph_adjacency_list.py +++ b/en/codes/python/chapter_graph/graph_adjacency_list.py @@ -48,22 +48,22 @@ class GraphAdjList: """Add vertex""" if vet in self.adj_list: return - # Add a new linked list to the adjacency list + # Add a new linked list in the adjacency list self.adj_list[vet] = [] def remove_vertex(self, vet: Vertex): """Remove vertex""" if vet not in self.adj_list: raise ValueError() - # Remove the vertex vet's corresponding linked list from the adjacency list + # Remove the linked list corresponding to vertex vet in the adjacency list self.adj_list.pop(vet) - # Traverse other vertices' linked lists, removing all edges containing vet + # Traverse the linked lists of other vertices and remove all edges containing vet for vertex in self.adj_list: if vet in self.adj_list[vertex]: self.adj_list[vertex].remove(vet) def print(self): - """Print the adjacency list""" + """Print adjacency list""" print("Adjacency list =") for vertex in self.adj_list: tmp = [v.val for v in self.adj_list[vertex]] @@ -87,13 +87,13 @@ if __name__ == "__main__": graph.print() # Add edge - # Vertices 1, 2 i.e., v[0], v[2] + # Vertices 1, 2 are v[0], v[2] graph.add_edge(v[0], v[2]) print("\nAfter adding edge 1-2, the graph is") graph.print() # Remove edge - # Vertices 1, 3 i.e., v[0], v[1] + # Vertices 1, 3 are v[0], v[1] graph.remove_edge(v[0], v[1]) print("\nAfter removing edge 1-3, the graph is") graph.print() @@ -105,7 +105,7 @@ if __name__ == "__main__": graph.print() # Remove vertex - # Vertex 3 i.e., v[1] + # Vertex 3 is v[1] graph.remove_vertex(v[1]) print("\nAfter removing vertex 3, the graph is") graph.print() diff --git a/en/codes/python/chapter_graph/graph_adjacency_matrix.py b/en/codes/python/chapter_graph/graph_adjacency_matrix.py index b404b680d..4b86c6b1c 100644 --- a/en/codes/python/chapter_graph/graph_adjacency_matrix.py +++ b/en/codes/python/chapter_graph/graph_adjacency_matrix.py @@ -16,15 +16,15 @@ class GraphAdjMat: def __init__(self, vertices: list[int], edges: list[list[int]]): """Constructor""" - # Vertex list, elements represent "vertex value", index represents "vertex index" + # Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" self.vertices: list[int] = [] - # Adjacency matrix, row and column indices correspond to "vertex index" + # Adjacency matrix, where the row and column indices correspond to the "vertex index" self.adj_mat: list[list[int]] = [] - # Add vertex + # Add vertices for val in vertices: self.add_vertex(val) - # Add edge - # Edges elements represent vertex indices + # Add edges + # Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices for e in edges: self.add_edge(e[0], e[1]) @@ -35,7 +35,7 @@ class GraphAdjMat: def add_vertex(self, val: int): """Add vertex""" n = self.size() - # Add new vertex value to the vertex list + # Add the value of the new vertex to the vertex list self.vertices.append(val) # Add a row to the adjacency matrix new_row = [0] * n @@ -48,27 +48,27 @@ class GraphAdjMat: """Remove vertex""" if index >= self.size(): raise IndexError() - # Remove vertex at `index` from the vertex list + # Remove the vertex at index from the vertex list self.vertices.pop(index) - # Remove the row at `index` from the adjacency matrix + # Remove the row at index from the adjacency matrix self.adj_mat.pop(index) - # Remove the column at `index` from the adjacency matrix + # Remove the column at index from the adjacency matrix for row in self.adj_mat: row.pop(index) def add_edge(self, i: int, j: int): """Add edge""" - # Parameters i, j correspond to vertices element indices + # Parameters i, j correspond to the vertices element indices # Handle index out of bounds and equality if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j: raise IndexError() - # In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., satisfies (i, j) == (j, i) + # In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) self.adj_mat[i][j] = 1 self.adj_mat[j][i] = 1 def remove_edge(self, i: int, j: int): """Remove edge""" - # Parameters i, j correspond to vertices element indices + # Parameters i, j correspond to the vertices element indices # Handle index out of bounds and equality if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j: raise IndexError() @@ -85,7 +85,7 @@ class GraphAdjMat: """Driver Code""" if __name__ == "__main__": # Initialize undirected graph - # Edges elements represent vertex indices + # Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices vertices = [1, 3, 2, 5, 4] edges = [[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]] graph = GraphAdjMat(vertices, edges) @@ -93,13 +93,13 @@ if __name__ == "__main__": graph.print() # Add edge - # Indices of vertices 1, 2 are 0, 2 respectively + # Vertices 1, 2 have indices 0, 2 respectively graph.add_edge(0, 2) print("\nAfter adding edge 1-2, the graph is") graph.print() # Remove edge - # Indices of vertices 1, 3 are 0, 1 respectively + # Vertices 1, 3 have indices 0, 1 respectively graph.remove_edge(0, 1) print("\nAfter removing edge 1-3, the graph is") graph.print() @@ -110,7 +110,7 @@ if __name__ == "__main__": graph.print() # Remove vertex - # Index of vertex 3 is 1 + # Vertex 3 has index 1 graph.remove_vertex(1) print("\nAfter removing vertex 3, the graph is") graph.print() diff --git a/en/codes/python/chapter_graph/graph_bfs.py b/en/codes/python/chapter_graph/graph_bfs.py index b1e73b9d3..59b6b66d1 100644 --- a/en/codes/python/chapter_graph/graph_bfs.py +++ b/en/codes/python/chapter_graph/graph_bfs.py @@ -15,24 +15,24 @@ from graph_adjacency_list import GraphAdjList def graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: """Breadth-first traversal""" - # Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex + # Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex # Vertex traversal sequence res = [] - # Hash set, used to record visited vertices + # Hash set for recording vertices that have been visited visited = set[Vertex]([start_vet]) # Queue used to implement BFS que = deque[Vertex]([start_vet]) # Starting from vertex vet, loop until all vertices are visited while len(que) > 0: - vet = que.popleft() # Dequeue the vertex at the head of the queue + vet = que.popleft() # Dequeue the front vertex res.append(vet) # Record visited vertex - # Traverse all adjacent vertices of that vertex + # Traverse all adjacent vertices of this vertex for adj_vet in graph.adj_list[vet]: if adj_vet in visited: - continue # Skip already visited vertices + continue # Skip vertices that have been visited que.append(adj_vet) # Only enqueue unvisited vertices - visited.add(adj_vet) # Mark the vertex as visited - # Return the vertex traversal sequence + visited.add(adj_vet) # Mark this vertex as visited + # Return vertex traversal sequence return res diff --git a/en/codes/python/chapter_graph/graph_dfs.py b/en/codes/python/chapter_graph/graph_dfs.py index de2b619e9..a4000b241 100644 --- a/en/codes/python/chapter_graph/graph_dfs.py +++ b/en/codes/python/chapter_graph/graph_dfs.py @@ -15,21 +15,21 @@ from graph_adjacency_list import GraphAdjList def dfs(graph: GraphAdjList, visited: set[Vertex], res: list[Vertex], vet: Vertex): """Depth-first traversal helper function""" res.append(vet) # Record visited vertex - visited.add(vet) # Mark the vertex as visited - # Traverse all adjacent vertices of that vertex + visited.add(vet) # Mark this vertex as visited + # Traverse all adjacent vertices of this vertex for adjVet in graph.adj_list[vet]: if adjVet in visited: - continue # Skip already visited vertices + continue # Skip vertices that have been visited # Recursively visit adjacent vertices dfs(graph, visited, res, adjVet) def graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: """Depth-first traversal""" - # Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex + # Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex # Vertex traversal sequence res = [] - # Hash set, used to record visited vertices + # Hash set for recording vertices that have been visited visited = set[Vertex]() dfs(graph, visited, res, start_vet) return res diff --git a/en/codes/python/chapter_greedy/coin_change_greedy.py b/en/codes/python/chapter_greedy/coin_change_greedy.py index 9f3afb31a..0be2171d3 100644 --- a/en/codes/python/chapter_greedy/coin_change_greedy.py +++ b/en/codes/python/chapter_greedy/coin_change_greedy.py @@ -6,13 +6,13 @@ Author: krahets (krahets@163.com) def coin_change_greedy(coins: list[int], amt: int) -> int: - """Coin change: Greedy""" - # Assume coins list is ordered + """Coin change: Greedy algorithm""" + # Assume coins list is sorted i = len(coins) - 1 count = 0 - # Loop for greedy selection until no remaining amount + # Loop to make greedy choices until no remaining amount while amt > 0: - # Find the smallest coin close to and less than the remaining amount + # Find the coin that is less than and closest to the remaining amount while i > 0 and coins[i] > amt: i -= 1 # Choose coins[i] @@ -24,25 +24,25 @@ def coin_change_greedy(coins: list[int], amt: int) -> int: """Driver Code""" if __name__ == "__main__": - # Greedy: can ensure finding a global optimal solution + # Greedy algorithm: Can guarantee finding the global optimal solution coins = [1, 5, 10, 20, 50, 100] amt = 186 res = coin_change_greedy(coins, amt) print(f"\ncoins = {coins}, amt = {amt}") - print(f"The minimum number of coins needed to make up {amt} is {res}") + print(f"The minimum number of coins needed to make {amt} is {res}") - # Greedy: cannot ensure finding a global optimal solution + # Greedy algorithm: Cannot guarantee finding the global optimal solution coins = [1, 20, 50] amt = 60 res = coin_change_greedy(coins, amt) print(f"\ncoins = {coins}, amt = {amt}") - print(f"The minimum number of coins needed to make up {amt} is {res}") - print(f"In reality, the minimum number needed is 3, i.e., 20 + 20 + 20") + print(f"The minimum number of coins needed to make {amt} is {res}") + print(f"Actually the minimum number needed is 3, i.e., 20 + 20 + 20") - # Greedy: cannot ensure finding a global optimal solution + # Greedy algorithm: Cannot guarantee finding the global optimal solution coins = [1, 49, 50] amt = 98 res = coin_change_greedy(coins, amt) print(f"\ncoins = {coins}, amt = {amt}") - print(f"The minimum number of coins needed to make up {amt} is {res}") - print(f"In reality, the minimum number needed is 2, i.e., 49 + 49") + print(f"The minimum number of coins needed to make {amt} is {res}") + print(f"Actually the minimum number needed is 2, i.e., 49 + 49") diff --git a/en/codes/python/chapter_greedy/fractional_knapsack.py b/en/codes/python/chapter_greedy/fractional_knapsack.py index 96be87030..2390181c7 100644 --- a/en/codes/python/chapter_greedy/fractional_knapsack.py +++ b/en/codes/python/chapter_greedy/fractional_knapsack.py @@ -14,8 +14,8 @@ class Item: def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int: - """Fractional knapsack: Greedy""" - # Create an item list, containing two properties: weight, value + """Fractional knapsack: Greedy algorithm""" + # Create item list with two attributes: weight, value items = [Item(w, v) for w, v in zip(wgt, val)] # Sort by unit value item.v / item.w from high to low items.sort(key=lambda item: item.v / item.w, reverse=True) @@ -23,13 +23,13 @@ def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int: res = 0 for item in items: if item.w <= cap: - # If the remaining capacity is sufficient, put the entire item into the knapsack + # If remaining capacity is sufficient, put the entire current item into the knapsack res += item.v cap -= item.w else: - # If the remaining capacity is insufficient, put part of the item into the knapsack + # If remaining capacity is insufficient, put part of the current item into the knapsack res += (item.v / item.w) * cap - # No remaining capacity left, thus break the loop + # No remaining capacity, so break out of the loop break return res diff --git a/en/codes/python/chapter_greedy/max_capacity.py b/en/codes/python/chapter_greedy/max_capacity.py index 411d62357..b71576a2a 100644 --- a/en/codes/python/chapter_greedy/max_capacity.py +++ b/en/codes/python/chapter_greedy/max_capacity.py @@ -6,14 +6,14 @@ Author: krahets (krahets@163.com) def max_capacity(ht: list[int]) -> int: - """Maximum capacity: Greedy""" - # Initialize i, j, making them split the array at both ends + """Max capacity: Greedy algorithm""" + # Initialize i, j to be at both ends of the array i, j = 0, len(ht) - 1 - # Initial maximum capacity is 0 + # Initial max capacity is 0 res = 0 # Loop for greedy selection until the two boards meet while i < j: - # Update maximum capacity + # Update max capacity cap = min(ht[i], ht[j]) * (j - i) res = max(res, cap) # Move the shorter board inward @@ -30,4 +30,4 @@ if __name__ == "__main__": # Greedy algorithm res = max_capacity(ht) - print(f"Maximum capacity is {res}") + print(f"Max capacity is {res}") diff --git a/en/codes/python/chapter_greedy/max_product_cutting.py b/en/codes/python/chapter_greedy/max_product_cutting.py index 923c95350..e47d3d2c9 100644 --- a/en/codes/python/chapter_greedy/max_product_cutting.py +++ b/en/codes/python/chapter_greedy/max_product_cutting.py @@ -8,14 +8,14 @@ import math def max_product_cutting(n: int) -> int: - """Maximum product of cutting: Greedy""" + """Max product cutting: Greedy algorithm""" # When n <= 3, must cut out a 1 if n <= 3: return 1 * (n - 1) - # Greedy cut out 3s, a is the number of 3s, b is the remainder + # Greedily cut out 3, a is the number of 3s, b is the remainder a, b = n // 3, n % 3 if b == 1: - # When the remainder is 1, convert a pair of 1 * 3 into 2 * 2 + # When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 return int(math.pow(3, a - 1)) * 2 * 2 if b == 2: # When the remainder is 2, do nothing @@ -30,4 +30,4 @@ if __name__ == "__main__": # Greedy algorithm res = max_product_cutting(n) - print(f"Maximum product of cutting is {res}") + print(f"Max product cutting is {res}") diff --git a/en/codes/python/chapter_hashing/array_hash_map.py b/en/codes/python/chapter_hashing/array_hash_map.py index 88daadf3a..b727b6a70 100644 --- a/en/codes/python/chapter_hashing/array_hash_map.py +++ b/en/codes/python/chapter_hashing/array_hash_map.py @@ -18,7 +18,7 @@ class ArrayHashMap: def __init__(self): """Constructor""" - # Initialize an array, containing 100 buckets + # Initialize array with 100 buckets self.buckets: list[Pair | None] = [None] * 100 def hash_func(self, key: int) -> int: @@ -26,7 +26,7 @@ class ArrayHashMap: index = key % 100 return index - def get(self, key: int) -> str: + def get(self, key: int) -> str | None: """Query operation""" index: int = self.hash_func(key) pair: Pair = self.buckets[index] @@ -35,7 +35,7 @@ class ArrayHashMap: return pair.val def put(self, key: int, val: str): - """Add operation""" + """Add and update operation""" pair = Pair(key, val) index: int = self.hash_func(key) self.buckets[index] = pair @@ -43,7 +43,7 @@ class ArrayHashMap: def remove(self, key: int): """Remove operation""" index: int = self.hash_func(key) - # Set to None, representing removal + # Set to None to represent removal self.buckets[index] = None def entry_set(self) -> list[Pair]: @@ -84,18 +84,18 @@ if __name__ == "__main__": # Add operation # Add key-value pair (key, value) to the hash table - hmap.put(12836, "Ha") - hmap.put(15937, "Luo") - hmap.put(16750, "Suan") - hmap.put(13276, "Fa") - hmap.put(10583, "Ya") + hmap.put(12836, "Xiao Ha") + hmap.put(15937, "Xiao Luo") + hmap.put(16750, "Xiao Suan") + hmap.put(13276, "Xiao Fa") + hmap.put(10583, "Xiao Ya") print("\nAfter adding, the hash table is\nKey -> Value") hmap.print() # Query operation - # Enter key to the hash table, get value + # Input key into the hash table to get value name = hmap.get(15937) - print("\nEnter student ID 15937, found name " + name) + print("\nInput student ID 15937, found name " + name) # Remove operation # Remove key-value pair (key, value) from the hash table @@ -108,10 +108,10 @@ if __name__ == "__main__": for pair in hmap.entry_set(): print(pair.key, "->", pair.val) - print("\nIndividually traverse keys Key") + print("\nTraverse keys only Key") for key in hmap.key_set(): print(key) - print("\nIndividually traverse values Value") + print("\nTraverse values only Value") for val in hmap.value_set(): print(val) diff --git a/en/codes/python/chapter_hashing/built_in_hash.py b/en/codes/python/chapter_hashing/built_in_hash.py index 323b6933c..a92137d13 100644 --- a/en/codes/python/chapter_hashing/built_in_hash.py +++ b/en/codes/python/chapter_hashing/built_in_hash.py @@ -14,24 +14,24 @@ from modules import ListNode if __name__ == "__main__": num = 3 hash_num = hash(num) - print(f"Integer {num}'s hash value is {hash_num}") + print(f"Hash value of integer {num} is {hash_num}") bol = True hash_bol = hash(bol) - print(f"Boolean {bol}'s hash value is {hash_bol}") + print(f"Hash value of boolean {bol} is {hash_bol}") dec = 3.14159 hash_dec = hash(dec) - print(f"Decimal {dec}'s hash value is {hash_dec}") + print(f"Hash value of decimal {dec} is {hash_dec}") - str = "Hello algorithm" + str = "Hello algo" hash_str = hash(str) - print(f"String {str}'s hash value is {hash_str}") + print(f"Hash value of string {str} is {hash_str}") - tup = (12836, "Ha") + tup = (12836, "Xiao Ha") hash_tup = hash(tup) - print(f"Tuple {tup}'s hash value is {hash(hash_tup)}") + print(f"Hash value of tuple {tup} is {hash(hash_tup)}") obj = ListNode(0) hash_obj = hash(obj) - print(f"Node object {obj}'s hash value is {hash_obj}") + print(f"Hash value of node object {obj} is {hash_obj}") diff --git a/en/codes/python/chapter_hashing/hash_map.py b/en/codes/python/chapter_hashing/hash_map.py index e1f2b7462..a250c658c 100644 --- a/en/codes/python/chapter_hashing/hash_map.py +++ b/en/codes/python/chapter_hashing/hash_map.py @@ -17,18 +17,18 @@ if __name__ == "__main__": # Add operation # Add key-value pair (key, value) to the hash table - hmap[12836] = "Ha" - hmap[15937] = "Luo" - hmap[16750] = "Suan" - hmap[13276] = "Fa" - hmap[10583] = "Ya" + hmap[12836] = "Xiao Ha" + hmap[15937] = "Xiao Luo" + hmap[16750] = "Xiao Suan" + hmap[13276] = "Xiao Fa" + hmap[10583] = "Xiao Ya" print("\nAfter adding, the hash table is\nKey -> Value") print_dict(hmap) # Query operation - # Enter key to the hash table, get value + # Input key into the hash table to get value name: str = hmap[15937] - print("\nEnter student ID 15937, found name " + name) + print("\nInput student ID 15937, found name " + name) # Remove operation # Remove key-value pair (key, value) from the hash table @@ -41,10 +41,10 @@ if __name__ == "__main__": for key, value in hmap.items(): print(key, "->", value) - print("\nIndividually traverse keys Key") + print("\nTraverse keys only Key") for key in hmap.keys(): print(key) - print("\nIndividually traverse values Value") + print("\nTraverse values only Value") for val in hmap.values(): print(val) diff --git a/en/codes/python/chapter_hashing/hash_map_chaining.py b/en/codes/python/chapter_hashing/hash_map_chaining.py index f6e47c2ea..d7cdd273c 100644 --- a/en/codes/python/chapter_hashing/hash_map_chaining.py +++ b/en/codes/python/chapter_hashing/hash_map_chaining.py @@ -12,7 +12,7 @@ from chapter_hashing.array_hash_map import Pair class HashMapChaining: - """Chained address hash table""" + """Hash table with separate chaining""" def __init__(self): """Constructor""" @@ -34,26 +34,26 @@ class HashMapChaining: """Query operation""" index = self.hash_func(key) bucket = self.buckets[index] - # Traverse the bucket, if the key is found, return the corresponding val + # Traverse bucket, if key is found, return corresponding val for pair in bucket: if pair.key == key: return pair.val - # If the key is not found, return None + # If key is not found, return None return None def put(self, key: int, val: str): """Add operation""" - # When the load factor exceeds the threshold, perform expansion + # When load factor exceeds threshold, perform expansion if self.load_factor() > self.load_thres: self.extend() index = self.hash_func(key) bucket = self.buckets[index] - # Traverse the bucket, if the specified key is encountered, update the corresponding val and return + # Traverse bucket, if specified key is encountered, update corresponding val and return for pair in bucket: if pair.key == key: pair.val = val return - # If the key is not found, add the key-value pair to the end + # If key does not exist, append key-value pair to the end pair = Pair(key, val) bucket.append(pair) self.size += 1 @@ -62,7 +62,7 @@ class HashMapChaining: """Remove operation""" index = self.hash_func(key) bucket = self.buckets[index] - # Traverse the bucket, remove the key-value pair from it + # Traverse bucket and remove key-value pair from it for pair in bucket: if pair.key == key: bucket.remove(pair) @@ -70,14 +70,14 @@ class HashMapChaining: break def extend(self): - """Extend hash table""" + """Expand hash table""" # Temporarily store the original hash table buckets = self.buckets - # Initialize the extended new hash table + # Initialize expanded new hash table self.capacity *= self.extend_ratio self.buckets = [[] for _ in range(self.capacity)] self.size = 0 - # Move key-value pairs from the original hash table to the new hash table + # Move key-value pairs from original hash table to new hash table for bucket in buckets: for pair in bucket: self.put(pair.key, pair.val) @@ -98,18 +98,18 @@ if __name__ == "__main__": # Add operation # Add key-value pair (key, value) to the hash table - hashmap.put(12836, "Ha") - hashmap.put(15937, "Luo") - hashmap.put(16750, "Suan") - hashmap.put(13276, "Fa") - hashmap.put(10583, "Ya") + hashmap.put(12836, "Xiao Ha") + hashmap.put(15937, "Xiao Luo") + hashmap.put(16750, "Xiao Suan") + hashmap.put(13276, "Xiao Fa") + hashmap.put(10583, "Xiao Ya") print("\nAfter adding, the hash table is\n[Key1 -> Value1, Key2 -> Value2, ...]") hashmap.print() # Query operation - # Enter key to the hash table, get value + # Input key into the hash table to get value name = hashmap.get(13276) - print("\nEnter student ID 13276, found name " + name) + print("\nInput student ID 13276, found name " + name) # Remove operation # Remove key-value pair (key, value) from the hash table diff --git a/en/codes/python/chapter_hashing/hash_map_open_addressing.py b/en/codes/python/chapter_hashing/hash_map_open_addressing.py index 9c9e7d14d..7ab92a95c 100644 --- a/en/codes/python/chapter_hashing/hash_map_open_addressing.py +++ b/en/codes/python/chapter_hashing/hash_map_open_addressing.py @@ -12,7 +12,7 @@ from chapter_hashing.array_hash_map import Pair class HashMapOpenAddressing: - """Open addressing hash table""" + """Hash table with open addressing""" def __init__(self): """Constructor""" @@ -21,7 +21,7 @@ class HashMapOpenAddressing: self.load_thres = 2.0 / 3.0 # Load factor threshold for triggering expansion self.extend_ratio = 2 # Expansion multiplier self.buckets: list[Pair | None] = [None] * self.capacity # Bucket array - self.TOMBSTONE = Pair(-1, "-1") # Removal mark + self.TOMBSTONE = Pair(-1, "-1") # Removal marker def hash_func(self, key: int) -> int: """Hash function""" @@ -32,70 +32,70 @@ class HashMapOpenAddressing: return self.size / self.capacity def find_bucket(self, key: int) -> int: - """Search for the bucket index corresponding to key""" + """Search for bucket index corresponding to key""" index = self.hash_func(key) first_tombstone = -1 # Linear probing, break when encountering an empty bucket while self.buckets[index] is not None: - # If the key is encountered, return the corresponding bucket index + # If key is encountered, return the corresponding bucket index if self.buckets[index].key == key: - # If a removal mark was encountered earlier, move the key-value pair to that index + # If a removal marker was encountered before, move the key-value pair to that index if first_tombstone != -1: self.buckets[first_tombstone] = self.buckets[index] self.buckets[index] = self.TOMBSTONE return first_tombstone # Return the moved bucket index return index # Return bucket index - # Record the first encountered removal mark + # Record the first removal marker encountered if first_tombstone == -1 and self.buckets[index] is self.TOMBSTONE: first_tombstone = index - # Calculate the bucket index, return to the head if exceeding the tail + # Calculate bucket index, wrap around to the head if past the tail index = (index + 1) % self.capacity - # If the key does not exist, return the index of the insertion point + # If key does not exist, return the index for insertion return index if first_tombstone == -1 else first_tombstone def get(self, key: int) -> str: """Query operation""" - # Search for the bucket index corresponding to key + # Search for bucket index corresponding to key index = self.find_bucket(key) - # If the key-value pair is found, return the corresponding val + # If key-value pair is found, return corresponding val if self.buckets[index] not in [None, self.TOMBSTONE]: return self.buckets[index].val - # If the key-value pair does not exist, return None + # If key-value pair does not exist, return None return None def put(self, key: int, val: str): """Add operation""" - # When the load factor exceeds the threshold, perform expansion + # When load factor exceeds threshold, perform expansion if self.load_factor() > self.load_thres: self.extend() - # Search for the bucket index corresponding to key + # Search for bucket index corresponding to key index = self.find_bucket(key) - # If the key-value pair is found, overwrite val and return + # If key-value pair is found, overwrite val and return if self.buckets[index] not in [None, self.TOMBSTONE]: self.buckets[index].val = val return - # If the key-value pair does not exist, add the key-value pair + # If key-value pair does not exist, add the key-value pair self.buckets[index] = Pair(key, val) self.size += 1 def remove(self, key: int): """Remove operation""" - # Search for the bucket index corresponding to key + # Search for bucket index corresponding to key index = self.find_bucket(key) - # If the key-value pair is found, cover it with a removal mark + # If key-value pair is found, overwrite it with removal marker if self.buckets[index] not in [None, self.TOMBSTONE]: self.buckets[index] = self.TOMBSTONE self.size -= 1 def extend(self): - """Extend hash table""" + """Expand hash table""" # Temporarily store the original hash table buckets_tmp = self.buckets - # Initialize the extended new hash table + # Initialize expanded new hash table self.capacity *= self.extend_ratio self.buckets = [None] * self.capacity self.size = 0 - # Move key-value pairs from the original hash table to the new hash table + # Move key-value pairs from original hash table to new hash table for pair in buckets_tmp: if pair not in [None, self.TOMBSTONE]: self.put(pair.key, pair.val) @@ -118,18 +118,18 @@ if __name__ == "__main__": # Add operation # Add key-value pair (key, val) to the hash table - hashmap.put(12836, "Ha") - hashmap.put(15937, "Luo") - hashmap.put(16750, "Suan") - hashmap.put(13276, "Fa") - hashmap.put(10583, "Ya") + hashmap.put(12836, "Xiao Ha") + hashmap.put(15937, "Xiao Luo") + hashmap.put(16750, "Xiao Suan") + hashmap.put(13276, "Xiao Fa") + hashmap.put(10583, "Xiao Ya") print("\nAfter adding, the hash table is\nKey -> Value") hashmap.print() # Query operation - # Enter key to the hash table, get value val + # Input key into the hash table to get val name = hashmap.get(13276) - print("\nEnter student ID 13276, found name " + name) + print("\nInput student ID 13276, found name " + name) # Remove operation # Remove key-value pair (key, val) from the hash table diff --git a/en/codes/python/chapter_hashing/simple_hash.py b/en/codes/python/chapter_hashing/simple_hash.py index 64614f558..3b3678cad 100644 --- a/en/codes/python/chapter_hashing/simple_hash.py +++ b/en/codes/python/chapter_hashing/simple_hash.py @@ -43,7 +43,7 @@ def rot_hash(key: str) -> int: """Driver Code""" if __name__ == "__main__": - key = "Hello algorithm" + key = "Hello algo" hash = add_hash(key) print(f"Additive hash value is {hash}") diff --git a/en/codes/python/chapter_heap/heap.py b/en/codes/python/chapter_heap/heap.py index 247e3cb46..ac7c8b284 100644 --- a/en/codes/python/chapter_heap/heap.py +++ b/en/codes/python/chapter_heap/heap.py @@ -14,41 +14,41 @@ import heapq def test_push(heap: list, val: int, flag: int = 1): - heapq.heappush(heap, flag * val) # Push the element into heap - print(f"\nElement {val} after pushed into heap") + heapq.heappush(heap, flag * val) # Element enters heap + print(f"\nAfter element {val} enters heap") print_heap([flag * val for val in heap]) def test_pop(heap: list, flag: int = 1): - val = flag * heapq.heappop(heap) # Pop the element at the heap top - print(f"\nHeap top element {val} after exiting heap") + val = flag * heapq.heappop(heap) # Top element exits heap + print(f"\nAfter top element {val} exits heap") print_heap([flag * val for val in heap]) """Driver Code""" if __name__ == "__main__": - # Initialize min-heap + # Initialize min heap min_heap, flag = [], 1 - # Initialize max-heap + # Initialize max heap max_heap, flag = [], -1 - print("\nThe following test case is for max-heap") - # Python's heapq module implements min-heap by default - # Consider "negating the elements" before entering the heap, thus reversing the comparator to implement a max-heap - # In this example, flag = 1 corresponds to min-heap, flag = -1 corresponds to max-heap + print("\nThe following test cases are for max heap") + # Python's heapq module implements min heap by default + # Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + # In this example, flag = 1 corresponds to min heap, flag = -1 corresponds to max heap - # Push the element into heap + # Elements enter heap test_push(max_heap, 1, flag) test_push(max_heap, 3, flag) test_push(max_heap, 2, flag) test_push(max_heap, 5, flag) test_push(max_heap, 4, flag) - # Access heap top element + # Get top element peek: int = flag * max_heap[0] - print(f"\nHeap top element is {peek}") + print(f"\nTop element is {peek}") - # Pop the element at the heap top + # Top element exits heap test_pop(max_heap, flag) test_pop(max_heap, flag) test_pop(max_heap, flag) @@ -59,13 +59,13 @@ if __name__ == "__main__": size: int = len(max_heap) print(f"\nNumber of heap elements is {size}") - # Determine if heap is empty + # Check if heap is empty is_empty: bool = not max_heap - print(f"\nIs the heap empty {is_empty}") + print(f"\nIs heap empty {is_empty}") - # Enter list and build heap + # Input list and build heap # Time complexity is O(n), not O(nlogn) min_heap = [1, 3, 2, 5, 4] heapq.heapify(min_heap) - print("\nEnter list and build min-heap") + print("\nAfter inputting list and building min heap") print_heap(min_heap) diff --git a/en/codes/python/chapter_heap/my_heap.py b/en/codes/python/chapter_heap/my_heap.py index bb78d9493..a94f12129 100644 --- a/en/codes/python/chapter_heap/my_heap.py +++ b/en/codes/python/chapter_heap/my_heap.py @@ -12,13 +12,13 @@ from modules import print_heap class MaxHeap: - """Max-heap""" + """Max heap""" def __init__(self, nums: list[int]): """Constructor, build heap based on input list""" - # Add all list elements into the heap + # Add list elements to heap as is self.max_heap = nums - # Heapify all nodes except leaves + # Heapify all nodes except leaf nodes for i in range(self.parent(self.size() - 1), -1, -1): self.sift_down(i) @@ -32,7 +32,7 @@ class MaxHeap: def parent(self, i: int) -> int: """Get index of parent node""" - return (i - 1) // 2 # Integer division down + return (i - 1) // 2 # Floor division def swap(self, i: int, j: int): """Swap elements""" @@ -43,62 +43,62 @@ class MaxHeap: return len(self.max_heap) def is_empty(self) -> bool: - """Determine if heap is empty""" + """Check if heap is empty""" return self.size() == 0 def peek(self) -> int: - """Access heap top element""" + """Access top element""" return self.max_heap[0] def push(self, val: int): - """Push the element into heap""" + """Element enters heap""" # Add node self.max_heap.append(val) # Heapify from bottom to top self.sift_up(self.size() - 1) def sift_up(self, i: int): - """Start heapifying node i, from bottom to top""" + """Starting from node i, heapify from bottom to top""" while True: # Get parent node of node i p = self.parent(i) - # When "crossing the root node" or "node does not need repair", end heapification + # When "crossing root node" or "node needs no repair", end heapify if p < 0 or self.max_heap[i] <= self.max_heap[p]: break # Swap two nodes self.swap(i, p) - # Loop upwards heapification + # Loop upward heapify i = p def pop(self) -> int: """Element exits heap""" - # Empty handling + # Handle empty case if self.is_empty(): raise IndexError("Heap is empty") - # Swap the root node with the rightmost leaf node (swap the first element with the last element) + # Swap root node with rightmost leaf node (swap first element with last element) self.swap(0, self.size() - 1) - # Remove node + # Delete node val = self.max_heap.pop() # Heapify from top to bottom self.sift_down(0) - # Return heap top element + # Return top element return val def sift_down(self, i: int): - """Start heapifying node i, from top to bottom""" + """Starting from node i, heapify from top to bottom""" while True: - # Determine the largest node among i, l, r, noted as ma + # Find node with largest value among i, l, r, denoted as ma l, r, ma = self.left(i), self.right(i), i if l < self.size() and self.max_heap[l] > self.max_heap[ma]: ma = l if r < self.size() and self.max_heap[r] > self.max_heap[ma]: ma = r - # If node i is the largest or indices l, r are out of bounds, no further heapification needed, break + # If node i is largest or indices l, r are out of bounds, no need to continue heapify, break if ma == i: break # Swap two nodes self.swap(i, ma) - # Loop downwards heapification + # Loop downward heapify i = ma def print(self): @@ -108,30 +108,30 @@ class MaxHeap: """Driver Code""" if __name__ == "__main__": - # Initialize max-heap + # Initialize max heap max_heap = MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]) - print("\nEnter list and build heap") + print("\nAfter inputting list and building heap") max_heap.print() - # Access heap top element + # Get top element peek = max_heap.peek() - print(f"\nHeap top element is {peek}") + print(f"\nTop element is {peek}") - # Push the element into heap + # Element enters heap val = 7 max_heap.push(val) - print(f"\nElement {val} after pushed into heap") + print(f"\nAfter element {val} enters heap") max_heap.print() - # Pop the element at the heap top + # Top element exits heap peek = max_heap.pop() - print(f"\nHeap top element {peek} after exiting heap") + print(f"\nAfter top element {peek} exits heap") max_heap.print() # Get heap size size = max_heap.size() print(f"\nNumber of heap elements is {size}") - # Determine if heap is empty + # Check if heap is empty is_empty = max_heap.is_empty() - print(f"\nIs the heap empty {is_empty}") + print(f"\nIs heap empty {is_empty}") diff --git a/en/codes/python/chapter_heap/top_k.py b/en/codes/python/chapter_heap/top_k.py index 804c29751..b79c1e448 100644 --- a/en/codes/python/chapter_heap/top_k.py +++ b/en/codes/python/chapter_heap/top_k.py @@ -14,15 +14,15 @@ import heapq def top_k_heap(nums: list[int], k: int) -> list[int]: - """Using heap to find the largest k elements in an array""" - # Initialize min-heap + """Find the largest k elements in array based on heap""" + # Initialize min heap heap = [] - # Enter the first k elements of the array into the heap + # Enter the first k elements of array into heap for i in range(k): heapq.heappush(heap, nums[i]) - # From the k+1th element, keep the heap length as k + # Starting from the (k+1)th element, maintain heap length as k for i in range(k, len(nums)): - # If the current element is larger than the heap top element, remove the heap top element and enter the current element into the heap + # If current element is greater than top element, top element exits heap, current element enters heap if nums[i] > heap[0]: heapq.heappop(heap) heapq.heappush(heap, nums[i]) diff --git a/en/codes/python/chapter_searching/binary_search.py b/en/codes/python/chapter_searching/binary_search.py index ec40fa69c..723689944 100644 --- a/en/codes/python/chapter_searching/binary_search.py +++ b/en/codes/python/chapter_searching/binary_search.py @@ -6,36 +6,36 @@ Author: timi (xisunyy@163.com) def binary_search(nums: list[int], target: int) -> int: - """Binary search (double closed interval)""" - # Initialize double closed interval [0, n-1], i.e., i, j point to the first element and last element of the array respectively + """Binary search (closed interval)""" + # Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array i, j = 0, len(nums) - 1 - # Loop until the search interval is empty (when i > j, it is empty) + # Loop, exit when the search interval is empty (empty when i > j) while i <= j: - # Theoretically, Python's numbers can be infinitely large (depending on memory size), so there is no need to consider large number overflow - m = i + (j - i) // 2 # Calculate midpoint index m + # In theory, Python numbers can be infinitely large (depending on memory size), no need to consider large number overflow + m = (i + j) // 2 # Calculate midpoint index m if nums[m] < target: - i = m + 1 # This situation indicates that target is in the interval [m+1, j] + i = m + 1 # This means target is in the interval [m+1, j] elif nums[m] > target: - j = m - 1 # This situation indicates that target is in the interval [i, m-1] + j = m - 1 # This means target is in the interval [i, m-1] else: - return m # Found the target element, thus return its index - return -1 # Did not find the target element, thus return -1 + return m # Found the target element, return its index + return -1 # Target element not found, return -1 def binary_search_lcro(nums: list[int], target: int) -> int: - """Binary search (left closed right open interval)""" - # Initialize left closed right open interval [0, n), i.e., i, j point to the first element and the last element +1 of the array respectively + """Binary search (left-closed right-open interval)""" + # Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 i, j = 0, len(nums) - # Loop until the search interval is empty (when i = j, it is empty) + # Loop, exit when the search interval is empty (empty when i = j) while i < j: - m = i + (j - i) // 2 # Calculate midpoint index m + m = (i + j) // 2 # Calculate midpoint index m if nums[m] < target: - i = m + 1 # This situation indicates that target is in the interval [m+1, j) + i = m + 1 # This means target is in the interval [m+1, j) elif nums[m] > target: - j = m # This situation indicates that target is in the interval [i, m) + j = m # This means target is in the interval [i, m) else: - return m # Found the target element, thus return its index - return -1 # Did not find the target element, thus return -1 + return m # Found the target element, return its index + return -1 # Target element not found, return -1 """Driver Code""" @@ -43,10 +43,10 @@ if __name__ == "__main__": target = 6 nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] - # Binary search (double closed interval) + # Binary search (closed interval) index = binary_search(nums, target) print("Index of target element 6 =", index) - # Binary search (left closed right open interval) + # Binary search (left-closed right-open interval) index = binary_search_lcro(nums, target) print("Index of target element 6 =", index) diff --git a/en/codes/python/chapter_searching/binary_search_edge.py b/en/codes/python/chapter_searching/binary_search_edge.py index 9edf577d1..01729c6ac 100644 --- a/en/codes/python/chapter_searching/binary_search_edge.py +++ b/en/codes/python/chapter_searching/binary_search_edge.py @@ -15,7 +15,7 @@ def binary_search_left_edge(nums: list[int], target: int) -> int: """Binary search for the leftmost target""" # Equivalent to finding the insertion point of target i = binary_search_insertion(nums, target) - # Did not find target, thus return -1 + # Target not found, return -1 if i == len(nums) or nums[i] != target: return -1 # Found target, return index i @@ -28,7 +28,7 @@ def binary_search_right_edge(nums: list[int], target: int) -> int: i = binary_search_insertion(nums, target + 1) # j points to the rightmost target, i points to the first element greater than target j = i - 1 - # Did not find target, thus return -1 + # Target not found, return -1 if j == -1 or nums[j] != target: return -1 # Found target, return index j @@ -41,9 +41,9 @@ if __name__ == "__main__": nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] print(f"\nArray nums = {nums}") - # Binary search for left and right boundaries + # Binary search for left boundary and right boundary for target in [6, 7]: index = binary_search_left_edge(nums, target) - print(f"The index of the leftmost element {target} is {index}") + print(f"Index of the leftmost element {target} is {index}") index = binary_search_right_edge(nums, target) - print(f"The index of the rightmost element {target} is {index}") + print(f"Index of the rightmost element {target} is {index}") diff --git a/en/codes/python/chapter_searching/binary_search_insertion.py b/en/codes/python/chapter_searching/binary_search_insertion.py index c532bc0eb..8d1271105 100644 --- a/en/codes/python/chapter_searching/binary_search_insertion.py +++ b/en/codes/python/chapter_searching/binary_search_insertion.py @@ -7,30 +7,30 @@ Author: krahets (krahets@163.com) def binary_search_insertion_simple(nums: list[int], target: int) -> int: """Binary search for insertion point (no duplicate elements)""" - i, j = 0, len(nums) - 1 # Initialize double closed interval [0, n-1] + i, j = 0, len(nums) - 1 # Initialize closed interval [0, n-1] while i <= j: - m = i + (j - i) // 2 # Calculate midpoint index m + m = (i + j) // 2 # Calculate midpoint index m if nums[m] < target: - i = m + 1 # Target is in interval [m+1, j] + i = m + 1 # target is in the interval [m+1, j] elif nums[m] > target: - j = m - 1 # Target is in interval [i, m-1] + j = m - 1 # target is in the interval [i, m-1] else: return m # Found target, return insertion point m - # Did not find target, return insertion point i + # Target not found, return insertion point i return i def binary_search_insertion(nums: list[int], target: int) -> int: """Binary search for insertion point (with duplicate elements)""" - i, j = 0, len(nums) - 1 # Initialize double closed interval [0, n-1] + i, j = 0, len(nums) - 1 # Initialize closed interval [0, n-1] while i <= j: - m = i + (j - i) // 2 # Calculate midpoint index m + m = (i + j) // 2 # Calculate midpoint index m if nums[m] < target: - i = m + 1 # Target is in interval [m+1, j] + i = m + 1 # target is in the interval [m+1, j] elif nums[m] > target: - j = m - 1 # Target is in interval [i, m-1] + j = m - 1 # target is in the interval [i, m-1] else: - j = m - 1 # First element less than target is in interval [i, m-1] + j = m - 1 # The first element less than target is in the interval [i, m-1] # Return insertion point i return i @@ -43,7 +43,7 @@ if __name__ == "__main__": # Binary search for insertion point for target in [6, 9]: index = binary_search_insertion_simple(nums, target) - print(f"Element {target}'s insertion point index is {index}") + print(f"Index of insertion point for element {target} is {index}") # Array with duplicate elements nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] @@ -51,4 +51,4 @@ if __name__ == "__main__": # Binary search for insertion point for target in [2, 6, 20]: index = binary_search_insertion(nums, target) - print(f"Element {target}'s insertion point index is {index}") + print(f"Index of insertion point for element {target} is {index}") diff --git a/en/codes/python/chapter_searching/hashing_search.py b/en/codes/python/chapter_searching/hashing_search.py index 835240639..409f9230c 100644 --- a/en/codes/python/chapter_searching/hashing_search.py +++ b/en/codes/python/chapter_searching/hashing_search.py @@ -14,7 +14,7 @@ from modules import ListNode, list_to_linked_list def hashing_search_array(hmap: dict[int, int], target: int) -> int: """Hash search (array)""" # Hash table's key: target element, value: index - # If the hash table does not contain this key, return -1 + # If this key does not exist in the hash table, return -1 return hmap.get(target, -1) @@ -23,7 +23,7 @@ def hashing_search_linkedlist( ) -> ListNode | None: """Hash search (linked list)""" # Hash table's key: target element, value: node object - # If the hash table does not contain this key, return None + # If this key does not exist in the hash table, return None return hmap.get(target, None) @@ -48,4 +48,4 @@ if __name__ == "__main__": map1[head.val] = head # key: node value, value: node head = head.next node: ListNode = hashing_search_linkedlist(map1, target) - print("Target node value 3's corresponding node object is", node) + print("The corresponding node object for target node value 3 is", node) diff --git a/en/codes/python/chapter_searching/linear_search.py b/en/codes/python/chapter_searching/linear_search.py index 84bd83301..9c17d99d9 100644 --- a/en/codes/python/chapter_searching/linear_search.py +++ b/en/codes/python/chapter_searching/linear_search.py @@ -13,21 +13,21 @@ from modules import ListNode, list_to_linked_list def linear_search_array(nums: list[int], target: int) -> int: """Linear search (array)""" - # Traverse array + # Traverse the array for i in range(len(nums)): - if nums[i] == target: # Found the target element, thus return its index + if nums[i] == target: # Found the target element, return its index return i - return -1 # Did not find the target element, thus return -1 + return -1 # Target element not found, return -1 def linear_search_linkedlist(head: ListNode, target: int) -> ListNode | None: """Linear search (linked list)""" - # Traverse the list + # Traverse the linked list while head: if head.val == target: # Found the target node, return it return head head = head.next - return None # Did not find the target node, thus return None + return None # Target node not found, return None """Driver Code""" @@ -42,4 +42,4 @@ if __name__ == "__main__": # Perform linear search in linked list head: ListNode = list_to_linked_list(nums) node: ListNode | None = linear_search_linkedlist(head, target) - print("Target node value 3's corresponding node object is", node) + print("The corresponding node object for target node value 3 is", node) diff --git a/en/codes/python/chapter_searching/two_sum.py b/en/codes/python/chapter_searching/two_sum.py index 52b34e460..83440629b 100644 --- a/en/codes/python/chapter_searching/two_sum.py +++ b/en/codes/python/chapter_searching/two_sum.py @@ -6,8 +6,8 @@ Author: krahets (krahets@163.com) def two_sum_brute_force(nums: list[int], target: int) -> list[int]: - """Method one: Brute force enumeration""" - # Two-layer loop, time complexity is O(n^2) + """Method 1: Brute force enumeration""" + # Two nested loops, time complexity is O(n^2) for i in range(len(nums) - 1): for j in range(i + 1, len(nums)): if nums[i] + nums[j] == target: @@ -16,10 +16,10 @@ def two_sum_brute_force(nums: list[int], target: int) -> list[int]: def two_sum_hash_table(nums: list[int], target: int) -> list[int]: - """Method two: Auxiliary hash table""" + """Method 2: Auxiliary hash table""" # Auxiliary hash table, space complexity is O(n) dic = {} - # Single-layer loop, time complexity is O(n) + # Single loop, time complexity is O(n) for i in range(len(nums)): if target - nums[i] in dic: return [dic[target - nums[i]], i] @@ -34,9 +34,9 @@ if __name__ == "__main__": target = 13 # ====== Driver Code ====== - # Method one + # Method 1 res: list[int] = two_sum_brute_force(nums, target) - print("Method one res =", res) - # Method two + print("Method 1 res =", res) + # Method 2 res: list[int] = two_sum_hash_table(nums, target) - print("Method two res =", res) + print("Method 2 res =", res) diff --git a/en/codes/python/chapter_sorting/bubble_sort.py b/en/codes/python/chapter_sorting/bubble_sort.py index 3e2d2766c..493bb4ccb 100644 --- a/en/codes/python/chapter_sorting/bubble_sort.py +++ b/en/codes/python/chapter_sorting/bubble_sort.py @@ -8,9 +8,9 @@ Author: timi (xisunyy@163.com) def bubble_sort(nums: list[int]): """Bubble sort""" n = len(nums) - # Outer loop: unsorted range is [0, i] + # Outer loop: unsorted interval is [0, i] for i in range(n - 1, 0, -1): - # Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + # Inner loop: swap the largest element in the unsorted interval [0, i] to the rightmost end of the interval for j in range(i): if nums[j] > nums[j + 1]: # Swap nums[j] and nums[j + 1] @@ -18,27 +18,27 @@ def bubble_sort(nums: list[int]): def bubble_sort_with_flag(nums: list[int]): - """Bubble sort (optimized with flag)""" + """Bubble sort (flag optimization)""" n = len(nums) - # Outer loop: unsorted range is [0, i] + # Outer loop: unsorted interval is [0, i] for i in range(n - 1, 0, -1): flag = False # Initialize flag - # Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range + # Inner loop: swap the largest element in the unsorted interval [0, i] to the rightmost end of the interval for j in range(i): if nums[j] > nums[j + 1]: # Swap nums[j] and nums[j + 1] nums[j], nums[j + 1] = nums[j + 1], nums[j] - flag = True # Record swapped elements + flag = True # Record element swap if not flag: - break # If no elements were swapped in this round of "bubbling", exit + break # No elements were swapped in this round of "bubbling", exit directly """Driver Code""" if __name__ == "__main__": nums = [4, 1, 3, 1, 5, 2] bubble_sort(nums) - print("Bubble sort completed nums =", nums) + print("After bubble sort, nums =", nums) nums1 = [4, 1, 3, 1, 5, 2] bubble_sort_with_flag(nums1) - print("Bubble sort completed nums =", nums1) + print("After bubble sort, nums =", nums1) diff --git a/en/codes/python/chapter_sorting/bucket_sort.py b/en/codes/python/chapter_sorting/bucket_sort.py index 6bcb71aca..cd556f9ad 100644 --- a/en/codes/python/chapter_sorting/bucket_sort.py +++ b/en/codes/python/chapter_sorting/bucket_sort.py @@ -29,7 +29,7 @@ def bucket_sort(nums: list[float]): if __name__ == "__main__": - # Assume input data is floating point, range [0, 1) + # Assume input data is floating point, interval [0, 1) nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37] bucket_sort(nums) - print("Bucket sort completed nums =", nums) + print("After bucket sort, nums =", nums) diff --git a/en/codes/python/chapter_sorting/counting_sort.py b/en/codes/python/chapter_sorting/counting_sort.py index 5302d2a79..41ed5e2e6 100644 --- a/en/codes/python/chapter_sorting/counting_sort.py +++ b/en/codes/python/chapter_sorting/counting_sort.py @@ -12,7 +12,7 @@ def counting_sort_naive(nums: list[int]): m = 0 for num in nums: m = max(m, num) - # 2. Count the occurrence of each digit + # 2. Count the occurrence of each number # counter[num] represents the occurrence of num counter = [0] * (m + 1) for num in nums: @@ -30,7 +30,7 @@ def counting_sort(nums: list[int]): # Complete implementation, can sort objects and is a stable sort # 1. Count the maximum element m in the array m = max(nums) - # 2. Count the occurrence of each digit + # 2. Count the occurrence of each number # counter[num] represents the occurrence of num counter = [0] * (m + 1) for num in nums: @@ -57,8 +57,8 @@ if __name__ == "__main__": nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] counting_sort_naive(nums) - print(f"Counting sort (unable to sort objects) completed nums = {nums}") + print(f"After counting sort (unable to sort objects), nums = {nums}") nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] counting_sort(nums1) - print(f"Counting sort completed nums1 = {nums1}") + print(f"After counting sort, nums1 = {nums1}") diff --git a/en/codes/python/chapter_sorting/heap_sort.py b/en/codes/python/chapter_sorting/heap_sort.py index f3429785b..d3756c549 100644 --- a/en/codes/python/chapter_sorting/heap_sort.py +++ b/en/codes/python/chapter_sorting/heap_sort.py @@ -42,4 +42,4 @@ def heap_sort(nums: list[int]): if __name__ == "__main__": nums = [4, 1, 3, 1, 5, 2] heap_sort(nums) - print("Heap sort completed nums =", nums) + print("After heap sort, nums =", nums) diff --git a/en/codes/python/chapter_sorting/insertion_sort.py b/en/codes/python/chapter_sorting/insertion_sort.py index 6ef4ace85..a750a60fb 100644 --- a/en/codes/python/chapter_sorting/insertion_sort.py +++ b/en/codes/python/chapter_sorting/insertion_sort.py @@ -7,11 +7,11 @@ Author: timi (xisunyy@163.com) def insertion_sort(nums: list[int]): """Insertion sort""" - # Outer loop: sorted range is [0, i-1] + # Outer loop: sorted interval is [0, i-1] for i in range(1, len(nums)): base = nums[i] j = i - 1 - # Inner loop: insert base into the correct position within the sorted range [0, i-1] + # Inner loop: insert base into the correct position within the sorted interval [0, i-1] while j >= 0 and nums[j] > base: nums[j + 1] = nums[j] # Move nums[j] to the right by one position j -= 1 @@ -22,4 +22,4 @@ def insertion_sort(nums: list[int]): if __name__ == "__main__": nums = [4, 1, 3, 1, 5, 2] insertion_sort(nums) - print("Insertion sort completed nums =", nums) + print("After insertion sort, nums =", nums) diff --git a/en/codes/python/chapter_sorting/merge_sort.py b/en/codes/python/chapter_sorting/merge_sort.py index 863618844..4bf2a5d51 100644 --- a/en/codes/python/chapter_sorting/merge_sort.py +++ b/en/codes/python/chapter_sorting/merge_sort.py @@ -40,8 +40,8 @@ def merge_sort(nums: list[int], left: int, right: int): # Termination condition if left >= right: return # Terminate recursion when subarray length is 1 - # Partition stage - mid = left + (right - left) // 2 # Calculate midpoint + # Divide and conquer stage + mid = (left + right) // 2 # Calculate midpoint merge_sort(nums, left, mid) # Recursively process the left subarray merge_sort(nums, mid + 1, right) # Recursively process the right subarray # Merge stage @@ -52,4 +52,4 @@ def merge_sort(nums: list[int], left: int, right: int): if __name__ == "__main__": nums = [7, 3, 2, 6, 0, 1, 5, 4] merge_sort(nums, 0, len(nums) - 1) - print("Merge sort completed nums =", nums) + print("After merge sort, nums =", nums) diff --git a/en/codes/python/chapter_sorting/quick_sort.py b/en/codes/python/chapter_sorting/quick_sort.py index 652fb3cba..c3d2aa639 100644 --- a/en/codes/python/chapter_sorting/quick_sort.py +++ b/en/codes/python/chapter_sorting/quick_sort.py @@ -9,7 +9,7 @@ class QuickSort: """Quick sort class""" def partition(self, nums: list[int], left: int, right: int) -> int: - """Partition""" + """Sentinel partition""" # Use nums[left] as the pivot i, j = left, right while i < j: @@ -28,7 +28,7 @@ class QuickSort: # Terminate recursion when subarray length is 1 if left >= right: return - # Partition + # Sentinel partition pivot = self.partition(nums, left, right) # Recursively process the left subarray and right subarray self.quick_sort(nums, left, pivot - 1) @@ -48,7 +48,7 @@ class QuickSortMedian: return right def partition(self, nums: list[int], left: int, right: int) -> int: - """Partition (median of three)""" + """Sentinel partition (median of three)""" # Use nums[left] as the pivot med = self.median_three(nums, left, (left + right) // 2, right) # Swap the median to the array's leftmost position @@ -71,7 +71,7 @@ class QuickSortMedian: # Terminate recursion when subarray length is 1 if left >= right: return - # Partition + # Sentinel partition pivot = self.partition(nums, left, right) # Recursively process the left subarray and right subarray self.quick_sort(nums, left, pivot - 1) @@ -79,10 +79,10 @@ class QuickSortMedian: class QuickSortTailCall: - """Quick sort class (tail recursion optimization)""" + """Quick sort class (recursion depth optimization)""" def partition(self, nums: list[int], left: int, right: int) -> int: - """Partition""" + """Sentinel partition""" # Use nums[left] as the pivot i, j = left, right while i < j: @@ -97,10 +97,10 @@ class QuickSortTailCall: return i # Return the index of the pivot def quick_sort(self, nums: list[int], left: int, right: int): - """Quick sort (tail recursion optimization)""" + """Quick sort (recursion depth optimization)""" # Terminate when subarray length is 1 while left < right: - # Partition operation + # Sentinel partition operation pivot = self.partition(nums, left, right) # Perform quick sort on the shorter of the two subarrays if pivot - left < right - pivot: @@ -116,14 +116,14 @@ if __name__ == "__main__": # Quick sort nums = [2, 4, 1, 0, 3, 5] QuickSort().quick_sort(nums, 0, len(nums) - 1) - print("Quick sort completed nums =", nums) + print("After quick sort, nums =", nums) # Quick sort (median pivot optimization) nums1 = [2, 4, 1, 0, 3, 5] QuickSortMedian().quick_sort(nums1, 0, len(nums1) - 1) - print("Quick sort (median pivot optimization) completed nums =", nums1) + print("After quick sort (median pivot optimization), nums =", nums1) - # Quick sort (tail recursion optimization) + # Quick sort (recursion depth optimization) nums2 = [2, 4, 1, 0, 3, 5] QuickSortTailCall().quick_sort(nums2, 0, len(nums2) - 1) - print("Quick sort (tail recursion optimization) completed nums =", nums2) + print("After quick sort (recursion depth optimization), nums =", nums2) diff --git a/en/codes/python/chapter_sorting/radix_sort.py b/en/codes/python/chapter_sorting/radix_sort.py index ce80caf97..0fd1abe22 100644 --- a/en/codes/python/chapter_sorting/radix_sort.py +++ b/en/codes/python/chapter_sorting/radix_sort.py @@ -66,4 +66,4 @@ if __name__ == "__main__": 63832996, ] radix_sort(nums) - print("Radix sort completed nums =", nums) + print("After radix sort, nums =", nums) diff --git a/en/codes/python/chapter_sorting/selection_sort.py b/en/codes/python/chapter_sorting/selection_sort.py index e4fffa3ca..f614f21a1 100644 --- a/en/codes/python/chapter_sorting/selection_sort.py +++ b/en/codes/python/chapter_sorting/selection_sort.py @@ -8,14 +8,14 @@ Author: krahets (krahets@163.com) def selection_sort(nums: list[int]): """Selection sort""" n = len(nums) - # Outer loop: unsorted range is [i, n-1] + # Outer loop: unsorted interval is [i, n-1] for i in range(n - 1): - # Inner loop: find the smallest element within the unsorted range + # Inner loop: find the smallest element within the unsorted interval k = i for j in range(i + 1, n): if nums[j] < nums[k]: k = j # Record the index of the smallest element - # Swap the smallest element with the first element of the unsorted range + # Swap the smallest element with the first element of the unsorted interval nums[i], nums[k] = nums[k], nums[i] @@ -23,4 +23,4 @@ def selection_sort(nums: list[int]): if __name__ == "__main__": nums = [4, 1, 3, 1, 5, 2] selection_sort(nums) - print("Selection sort completed nums =", nums) + print("After selection sort, nums =", nums) diff --git a/en/codes/python/chapter_stack_and_queue/array_deque.py b/en/codes/python/chapter_stack_and_queue/array_deque.py index 270a56e6d..414a7e0c8 100644 --- a/en/codes/python/chapter_stack_and_queue/array_deque.py +++ b/en/codes/python/chapter_stack_and_queue/array_deque.py @@ -6,7 +6,7 @@ Author: krahets (krahets@163.com) class ArrayDeque: - """Double-ended queue class based on circular array""" + """Double-ended queue based on circular array implementation""" def __init__(self, capacity: int): """Constructor""" @@ -23,70 +23,70 @@ class ArrayDeque: return self._size def is_empty(self) -> bool: - """Determine if the double-ended queue is empty""" + """Check if the double-ended queue is empty""" return self._size == 0 def index(self, i: int) -> int: """Calculate circular array index""" - # Implement circular array by modulo operation - # When i exceeds the tail of the array, return to the head - # When i exceeds the head of the array, return to the tail + # Use modulo operation to wrap the array head and tail together + # When i passes the tail of the array, return to the head + # When i passes the head of the array, return to the tail return (i + self.capacity()) % self.capacity() def push_first(self, num: int): - """Front enqueue""" + """Front of the queue enqueue""" if self._size == self.capacity(): print("Double-ended queue is full") return - # Move the front pointer one position to the left - # Implement front crossing the head of the array to return to the tail by modulo operation + # Front pointer moves one position to the left + # Use modulo operation to wrap front around to the tail after passing the head of the array self._front = self.index(self._front - 1) - # Add num to the front + # Add num to the front of the queue self._nums[self._front] = num self._size += 1 def push_last(self, num: int): - """Rear enqueue""" + """Rear of the queue enqueue""" if self._size == self.capacity(): print("Double-ended queue is full") return - # Calculate rear pointer, pointing to rear index + 1 + # Calculate rear pointer, points to rear index + 1 rear = self.index(self._front + self._size) - # Add num to the rear + # Add num to the rear of the queue self._nums[rear] = num self._size += 1 def pop_first(self) -> int: - """Front dequeue""" + """Front of the queue dequeue""" num = self.peek_first() - # Move front pointer one position backward + # Front pointer moves one position backward self._front = self.index(self._front + 1) self._size -= 1 return num def pop_last(self) -> int: - """Rear dequeue""" + """Rear of the queue dequeue""" num = self.peek_last() self._size -= 1 return num def peek_first(self) -> int: - """Access front element""" + """Access front of the queue element""" if self.is_empty(): raise IndexError("Double-ended queue is empty") return self._nums[self._front] def peek_last(self) -> int: - """Access rear element""" + """Access rear of the queue element""" if self.is_empty(): raise IndexError("Double-ended queue is empty") - # Calculate rear element index + # Calculate tail element index last = self.index(self._front + self._size - 1) return self._nums[last] def to_array(self) -> list[int]: """Return array for printing""" - # Only convert elements within valid length range + # Only convert list elements within the valid length range res = [] for i in range(self._size): res.append(self._nums[self.index(self._front + i)]) @@ -100,30 +100,30 @@ if __name__ == "__main__": deque.push_last(3) deque.push_last(2) deque.push_last(5) - print("Double-ended queue deque =", deque.to_array()) + print("double-ended queue deque =", deque.to_array()) - # Access element + # Access elements peek_first: int = deque.peek_first() - print("Front element peek_first =", peek_first) + print("Front of the queue element peek_first =", peek_first) peek_last: int = deque.peek_last() - print("Rear element peek_last =", peek_last) + print("Rear of the queue element peek_last =", peek_last) - # Element enqueue + # Elements enqueue deque.push_last(4) - print("Element 4 rear enqueued, deque =", deque.to_array()) + print("Element 4 rear enqueue after deque =", deque.to_array()) deque.push_first(1) - print("Element 1 front enqueued, deque =", deque.to_array()) + print("Element 1 front enqueue after deque =", deque.to_array()) - # Element dequeue + # Elements dequeue pop_last: int = deque.pop_last() - print("Rear dequeued element =", pop_last, ", deque after rear dequeue =", deque.to_array()) + print("Rear dequeued element =", pop_last, ", rear dequeue after deque =", deque.to_array()) pop_first: int = deque.pop_first() - print("Front dequeued element =", pop_first, ", deque after front dequeue =", deque.to_array()) + print("Front dequeued element =", pop_first, ", front dequeue after deque =", deque.to_array()) # Get the length of the double-ended queue size: int = deque.size() - print("Double-ended queue length size =", size) + print("Length of the double-ended queue size =", size) - # Determine if the double-ended queue is empty + # Check if the double-ended queue is empty is_empty: bool = deque.is_empty() print("Is the double-ended queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/array_queue.py b/en/codes/python/chapter_stack_and_queue/array_queue.py index 25f6356aa..ca06790f8 100644 --- a/en/codes/python/chapter_stack_and_queue/array_queue.py +++ b/en/codes/python/chapter_stack_and_queue/array_queue.py @@ -6,12 +6,12 @@ Author: Peng Chen (pengchzn@gmail.com) class ArrayQueue: - """Queue class based on circular array""" + """Queue based on circular array implementation""" def __init__(self, size: int): """Constructor""" self._nums: list[int] = [0] * size # Array for storing queue elements - self._front: int = 0 # Front pointer, pointing to the front element + self._front: int = 0 # Front pointer, points to the front of the queue element self._size: int = 0 # Queue length def capacity(self) -> int: @@ -23,36 +23,36 @@ class ArrayQueue: return self._size def is_empty(self) -> bool: - """Determine if the queue is empty""" + """Check if the queue is empty""" return self._size == 0 def push(self, num: int): """Enqueue""" if self._size == self.capacity(): raise IndexError("Queue is full") - # Calculate rear pointer, pointing to rear index + 1 - # Use modulo operation to wrap the rear pointer from the end of the array back to the start + # Calculate rear pointer, points to rear index + 1 + # Use modulo operation to wrap rear around to the head after passing the tail of the array rear: int = (self._front + self._size) % self.capacity() - # Add num to the rear + # Add num to the rear of the queue self._nums[rear] = num self._size += 1 def pop(self) -> int: """Dequeue""" num: int = self.peek() - # Move front pointer one position backward, returning to the head of the array if it exceeds the tail + # Front pointer moves one position backward, if it passes the tail, return to the head of the array self._front = (self._front + 1) % self.capacity() self._size -= 1 return num def peek(self) -> int: - """Access front element""" + """Access front of the queue element""" if self.is_empty(): raise IndexError("Queue is empty") return self._nums[self._front] def to_list(self) -> list[int]: - """Return array for printing""" + """Return list for printing""" res = [0] * self.size() j: int = self._front for i in range(self.size()): @@ -66,28 +66,28 @@ if __name__ == "__main__": # Initialize queue queue = ArrayQueue(10) - # Element enqueue + # Elements enqueue queue.push(1) queue.push(3) queue.push(2) queue.push(5) queue.push(4) - print("Queue queue =", queue.to_list()) + print("queue =", queue.to_list()) - # Access front element + # Access front of the queue element peek: int = queue.peek() - print("Front element peek =", peek) + print("Front of the queue element peek =", peek) # Element dequeue pop: int = queue.pop() print("Dequeued element pop =", pop) - print("Queue after dequeue =", queue.to_list()) + print("After dequeue queue =", queue.to_list()) # Get the length of the queue size: int = queue.size() - print("Queue length size =", size) + print("Length of the queue size =", size) - # Determine if the queue is empty + # Check if the queue is empty is_empty: bool = queue.is_empty() print("Is the queue empty =", is_empty) @@ -95,4 +95,4 @@ if __name__ == "__main__": for i in range(10): queue.push(i) queue.pop() - print("In the ", i, "th round of enqueue + dequeue, queue = ", queue.to_list()) + print("Round", i, "enqueue + dequeue after queue = ", queue.to_list()) diff --git a/en/codes/python/chapter_stack_and_queue/array_stack.py b/en/codes/python/chapter_stack_and_queue/array_stack.py index 2775c2602..ca227a37e 100644 --- a/en/codes/python/chapter_stack_and_queue/array_stack.py +++ b/en/codes/python/chapter_stack_and_queue/array_stack.py @@ -6,7 +6,7 @@ Author: Peng Chen (pengchzn@gmail.com) class ArrayStack: - """Stack class based on array""" + """Stack based on array implementation""" def __init__(self): """Constructor""" @@ -17,7 +17,7 @@ class ArrayStack: return len(self._stack) def is_empty(self) -> bool: - """Determine if the stack is empty""" + """Check if the stack is empty""" return self.size() == 0 def push(self, item: int): @@ -31,13 +31,13 @@ class ArrayStack: return self._stack.pop() def peek(self) -> int: - """Access stack top element""" + """Access top of the stack element""" if self.is_empty(): raise IndexError("Stack is empty") return self._stack[-1] def to_list(self) -> list[int]: - """Return array for printing""" + """Return list for printing""" return self._stack @@ -46,27 +46,27 @@ if __name__ == "__main__": # Initialize stack stack = ArrayStack() - # Element push + # Elements push onto stack stack.push(1) stack.push(3) stack.push(2) stack.push(5) stack.push(4) - print("Stack stack =", stack.to_list()) + print("stack =", stack.to_list()) - # Access stack top element + # Access top of the stack element peek: int = stack.peek() - print("Stack top element peek =", peek) + print("Top of the stack element peek =", peek) - # Element pop + # Element pop from stack pop: int = stack.pop() print("Popped element pop =", pop) - print("Stack after pop =", stack.to_list()) + print("After pop stack =", stack.to_list()) # Get the length of the stack size: int = stack.size() - print("Stack length size =", size) + print("Length of the stack size =", size) - # Determine if it's empty + # Check if it is empty is_empty: bool = stack.is_empty() print("Is the stack empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/deque.py b/en/codes/python/chapter_stack_and_queue/deque.py index 08406a96b..c6f59f2fd 100644 --- a/en/codes/python/chapter_stack_and_queue/deque.py +++ b/en/codes/python/chapter_stack_and_queue/deque.py @@ -11,32 +11,32 @@ if __name__ == "__main__": # Initialize double-ended queue deq: deque[int] = deque() - # Element enqueue - deq.append(2) # Add to rear + # Elements enqueue + deq.append(2) # Add to the rear of the queue deq.append(5) deq.append(4) - deq.appendleft(3) # Add to front + deq.appendleft(3) # Add to the front of the queue deq.appendleft(1) - print("Double-ended queue deque =", deq) + print("double-ended queue deque =", deq) - # Access element - front: int = deq[0] # Front element - print("Front element front =", front) - rear: int = deq[-1] # Rear element - print("Rear element rear =", rear) + # Access elements + front: int = deq[0] # Front of the queue element + print("Front of the queue element front =", front) + rear: int = deq[-1] # Rear of the queue element + print("Rear of the queue element rear =", rear) - # Element dequeue - pop_front: int = deq.popleft() # Front element dequeue + # Elements dequeue + pop_front: int = deq.popleft() # Front of the queue element dequeues print("Front dequeued element pop_front =", pop_front) - print("Deque after front dequeue =", deq) - pop_rear: int = deq.pop() # Rear element dequeue + print("After front dequeue deque =", deq) + pop_rear: int = deq.pop() # Rear of the queue element dequeues print("Rear dequeued element pop_rear =", pop_rear) - print("Deque after rear dequeue =", deq) + print("After rear dequeue deque =", deq) # Get the length of the double-ended queue size: int = len(deq) - print("Double-ended queue length size =", size) + print("Length of the double-ended queue size =", size) - # Determine if the double-ended queue is empty + # Check if the double-ended queue is empty is_empty: bool = len(deq) == 0 print("Is the double-ended queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py b/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py index a2cea2099..488725b76 100644 --- a/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py +++ b/en/codes/python/chapter_stack_and_queue/linkedlist_deque.py @@ -6,17 +6,17 @@ Author: krahets (krahets@163.com) class ListNode: - """Double-linked list node""" + """Doubly linked list node""" def __init__(self, val: int): """Constructor""" self.val: int = val - self.next: ListNode | None = None # Reference to successor node - self.prev: ListNode | None = None # Reference to predecessor node + self.next: ListNode | None = None # Successor node reference + self.prev: ListNode | None = None # Predecessor node reference class LinkedListDeque: - """Double-ended queue class based on double-linked list""" + """Double-ended queue based on doubly linked list implementation""" def __init__(self): """Constructor""" @@ -29,54 +29,54 @@ class LinkedListDeque: return self._size def is_empty(self) -> bool: - """Determine if the double-ended queue is empty""" + """Check if the double-ended queue is empty""" return self._size == 0 def push(self, num: int, is_front: bool): """Enqueue operation""" node = ListNode(num) - # If the list is empty, make front and rear both point to node + # If the linked list is empty, make both front and rear point to node if self.is_empty(): self._front = self._rear = node - # Front enqueue operation + # Front of the queue enqueue operation elif is_front: - # Add node to the head of the list + # Add node to the head of the linked list self._front.prev = node node.next = self._front self._front = node # Update head node - # Rear enqueue operation + # Rear of the queue enqueue operation else: - # Add node to the tail of the list + # Add node to the tail of the linked list self._rear.next = node node.prev = self._rear self._rear = node # Update tail node self._size += 1 # Update queue length def push_first(self, num: int): - """Front enqueue""" + """Front of the queue enqueue""" self.push(num, True) def push_last(self, num: int): - """Rear enqueue""" + """Rear of the queue enqueue""" self.push(num, False) def pop(self, is_front: bool) -> int: """Dequeue operation""" if self.is_empty(): raise IndexError("Double-ended queue is empty") - # Front dequeue operation + # Front of the queue dequeue operation if is_front: - val: int = self._front.val # Temporarily store the head node value - # Remove head node + val: int = self._front.val # Temporarily store head node value + # Delete head node fnext: ListNode | None = self._front.next if fnext is not None: fnext.prev = None self._front.next = None self._front = fnext # Update head node - # Rear dequeue operation + # Rear of the queue dequeue operation else: - val: int = self._rear.val # Temporarily store the tail node value - # Remove tail node + val: int = self._rear.val # Temporarily store tail node value + # Delete tail node rprev: ListNode | None = self._rear.prev if rprev is not None: rprev.next = None @@ -86,21 +86,21 @@ class LinkedListDeque: return val def pop_first(self) -> int: - """Front dequeue""" + """Front of the queue dequeue""" return self.pop(True) def pop_last(self) -> int: - """Rear dequeue""" + """Rear of the queue dequeue""" return self.pop(False) def peek_first(self) -> int: - """Access front element""" + """Access front of the queue element""" if self.is_empty(): raise IndexError("Double-ended queue is empty") return self._front.val def peek_last(self) -> int: - """Access rear element""" + """Access rear of the queue element""" if self.is_empty(): raise IndexError("Double-ended queue is empty") return self._rear.val @@ -122,30 +122,30 @@ if __name__ == "__main__": deque.push_last(3) deque.push_last(2) deque.push_last(5) - print("Double-ended queue deque =", deque.to_array()) + print("double-ended queue deque =", deque.to_array()) - # Access element + # Access elements peek_first: int = deque.peek_first() - print("Front element peek_first =", peek_first) + print("Front of the queue element peek_first =", peek_first) peek_last: int = deque.peek_last() - print("Rear element peek_last =", peek_last) + print("Rear of the queue element peek_last =", peek_last) - # Element enqueue + # Elements enqueue deque.push_last(4) - print("Element 4 rear enqueued, deque =", deque.to_array()) + print("Element 4 rear enqueue after deque =", deque.to_array()) deque.push_first(1) - print("Element 1 front enqueued, deque =", deque.to_array()) + print("Element 1 front enqueue after deque =", deque.to_array()) - # Element dequeue + # Elements dequeue pop_last: int = deque.pop_last() - print("Rear dequeued element =", pop_last, ", deque after rear dequeue =", deque.to_array()) + print("Rear dequeued element =", pop_last, ", rear dequeue after deque =", deque.to_array()) pop_first: int = deque.pop_first() - print("Front dequeued element =", pop_first, ", deque after front dequeue =", deque.to_array()) + print("Front dequeued element =", pop_first, ", front dequeue after deque =", deque.to_array()) # Get the length of the double-ended queue size: int = deque.size() - print("Double-ended queue length size =", size) + print("Length of the double-ended queue size =", size) - # Determine if the double-ended queue is empty + # Check if the double-ended queue is empty is_empty: bool = deque.is_empty() print("Is the double-ended queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/linkedlist_queue.py b/en/codes/python/chapter_stack_and_queue/linkedlist_queue.py index 1acee511e..ab5c397d6 100644 --- a/en/codes/python/chapter_stack_and_queue/linkedlist_queue.py +++ b/en/codes/python/chapter_stack_and_queue/linkedlist_queue.py @@ -12,7 +12,7 @@ from modules import ListNode class LinkedListQueue: - """Queue class based on linked list""" + """Queue based on linked list implementation""" def __init__(self): """Constructor""" @@ -25,18 +25,18 @@ class LinkedListQueue: return self._size def is_empty(self) -> bool: - """Determine if the queue is empty""" + """Check if the queue is empty""" return self._size == 0 def push(self, num: int): """Enqueue""" - # Add num behind the tail node + # Add num after the tail node node = ListNode(num) - # If the queue is empty, make the head and tail nodes both point to that node + # If the queue is empty, make both front and rear point to the node if self._front is None: self._front = node self._rear = node - # If the queue is not empty, add that node behind the tail node + # If the queue is not empty, add the node after the tail node else: self._rear.next = node self._rear = node @@ -45,19 +45,19 @@ class LinkedListQueue: def pop(self) -> int: """Dequeue""" num = self.peek() - # Remove head node + # Delete head node self._front = self._front.next self._size -= 1 return num def peek(self) -> int: - """Access front element""" + """Access front of the queue element""" if self.is_empty(): raise IndexError("Queue is empty") return self._front.val def to_list(self) -> list[int]: - """Convert to a list for printing""" + """Convert to list for printing""" queue = [] temp = self._front while temp: @@ -71,27 +71,27 @@ if __name__ == "__main__": # Initialize queue queue = LinkedListQueue() - # Element enqueue + # Elements enqueue queue.push(1) queue.push(3) queue.push(2) queue.push(5) queue.push(4) - print("Queue queue =", queue.to_list()) + print("queue =", queue.to_list()) - # Access front element + # Access front of the queue element peek: int = queue.peek() - print("Front element front =", peek) + print("Front of the queue element front =", peek) # Element dequeue pop_front: int = queue.pop() print("Dequeued element pop =", pop_front) - print("Queue after dequeue =", queue.to_list()) + print("After dequeue queue =", queue.to_list()) # Get the length of the queue size: int = queue.size() - print("Queue length size =", size) + print("Length of the queue size =", size) - # Determine if the queue is empty + # Check if the queue is empty is_empty: bool = queue.is_empty() print("Is the queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/linkedlist_stack.py b/en/codes/python/chapter_stack_and_queue/linkedlist_stack.py index 7efd39c7f..94afea86f 100644 --- a/en/codes/python/chapter_stack_and_queue/linkedlist_stack.py +++ b/en/codes/python/chapter_stack_and_queue/linkedlist_stack.py @@ -12,7 +12,7 @@ from modules import ListNode class LinkedListStack: - """Stack class based on linked list""" + """Stack based on linked list implementation""" def __init__(self): """Constructor""" @@ -24,7 +24,7 @@ class LinkedListStack: return self._size def is_empty(self) -> bool: - """Determine if the stack is empty""" + """Check if the stack is empty""" return self._size == 0 def push(self, val: int): @@ -42,13 +42,13 @@ class LinkedListStack: return num def peek(self) -> int: - """Access stack top element""" + """Access top of the stack element""" if self.is_empty(): raise IndexError("Stack is empty") return self._peek.val def to_list(self) -> list[int]: - """Convert to a list for printing""" + """Convert to list for printing""" arr = [] node = self._peek while node: @@ -63,27 +63,27 @@ if __name__ == "__main__": # Initialize stack stack = LinkedListStack() - # Element push + # Elements push onto stack stack.push(1) stack.push(3) stack.push(2) stack.push(5) stack.push(4) - print("Stack stack =", stack.to_list()) + print("stack =", stack.to_list()) - # Access stack top element + # Access top of the stack element peek: int = stack.peek() - print("Stack top element peek =", peek) + print("Top of the stack element peek =", peek) - # Element pop + # Element pop from stack pop: int = stack.pop() print("Popped element pop =", pop) - print("Stack after pop =", stack.to_list()) + print("After pop stack =", stack.to_list()) # Get the length of the stack size: int = stack.size() - print("Stack length size =", size) + print("Length of the stack size =", size) - # Determine if it's empty + # Check if it is empty is_empty: bool = stack.is_empty() print("Is the stack empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/queue.py b/en/codes/python/chapter_stack_and_queue/queue.py index 495aae127..efb350f55 100644 --- a/en/codes/python/chapter_stack_and_queue/queue.py +++ b/en/codes/python/chapter_stack_and_queue/queue.py @@ -9,31 +9,31 @@ from collections import deque """Driver Code""" if __name__ == "__main__": # Initialize queue - # In Python, we generally consider the deque class as a queue - # Although queue.Queue() is a pure queue class, it's not very user-friendly + # In Python, we generally use the double-ended queue class deque as a queue + # Although queue.Queue() is a proper queue class, it is not very convenient to use que: deque[int] = deque() - # Element enqueue + # Elements enqueue que.append(1) que.append(3) que.append(2) que.append(5) que.append(4) - print("Queue que =", que) + print("queue que =", que) - # Access front element + # Access front of the queue element front: int = que[0] - print("Front element front =", front) + print("Front of the queue element front =", front) # Element dequeue pop: int = que.popleft() print("Dequeued element pop =", pop) - print("Queue after dequeue =", que) + print("After dequeue que =", que) # Get the length of the queue size: int = len(que) - print("Queue length size =", size) + print("Length of the queue size =", size) - # Determine if the queue is empty + # Check if the queue is empty is_empty: bool = len(que) == 0 print("Is the queue empty =", is_empty) diff --git a/en/codes/python/chapter_stack_and_queue/stack.py b/en/codes/python/chapter_stack_and_queue/stack.py index 1a3447eed..c2fba9782 100644 --- a/en/codes/python/chapter_stack_and_queue/stack.py +++ b/en/codes/python/chapter_stack_and_queue/stack.py @@ -7,30 +7,30 @@ Author: Peng Chen (pengchzn@gmail.com) """Driver Code""" if __name__ == "__main__": # Initialize stack - # Python does not have a built-in stack class, but you can use a list as a stack + # Python does not have a built-in stack class, we can use list as a stack stack: list[int] = [] - # Element push + # Elements push onto stack stack.append(1) stack.append(3) stack.append(2) stack.append(5) stack.append(4) - print("Stack stack =", stack) + print("stack =", stack) - # Access stack top element + # Access top of the stack element peek: int = stack[-1] - print("Stack top element peek =", peek) + print("Top of the stack element peek =", peek) - # Element pop + # Element pop from stack pop: int = stack.pop() print("Popped element pop =", pop) - print("Stack after pop =", stack) + print("After pop stack =", stack) # Get the length of the stack size: int = len(stack) - print("Stack length size =", size) + print("Length of the stack size =", size) - # Determine if it's empty + # Check if it is empty is_empty: bool = len(stack) == 0 print("Is the stack empty =", is_empty) diff --git a/en/codes/python/chapter_tree/array_binary_tree.py b/en/codes/python/chapter_tree/array_binary_tree.py index 47c35d2e1..6e2603b66 100644 --- a/en/codes/python/chapter_tree/array_binary_tree.py +++ b/en/codes/python/chapter_tree/array_binary_tree.py @@ -12,7 +12,7 @@ from modules import TreeNode, list_to_tree, print_tree class ArrayBinaryTree: - """Array-based binary tree class""" + """Binary tree class represented by array""" def __init__(self, arr: list[int | None]): """Constructor""" @@ -23,28 +23,28 @@ class ArrayBinaryTree: return len(self._tree) def val(self, i: int) -> int | None: - """Get the value of the node at index i""" - # If the index is out of bounds, return None, representing a vacancy + """Get value of node at index i""" + # If index is out of bounds, return None, representing empty position if i < 0 or i >= self.size(): return None return self._tree[i] def left(self, i: int) -> int | None: - """Get the index of the left child of the node at index i""" + """Get index of left child node of node at index i""" return 2 * i + 1 def right(self, i: int) -> int | None: - """Get the index of the right child of the node at index i""" + """Get index of right child node of node at index i""" return 2 * i + 2 def parent(self, i: int) -> int | None: - """Get the index of the parent of the node at index i""" + """Get index of parent node of node at index i""" return (i - 1) // 2 def level_order(self) -> list[int]: """Level-order traversal""" self.res = [] - # Traverse array + # Traverse array directly for i in range(self.size()): if self.val(i) is not None: self.res.append(self.val(i)) @@ -54,32 +54,32 @@ class ArrayBinaryTree: """Depth-first traversal""" if self.val(i) is None: return - # Pre-order traversal + # Preorder traversal if order == "pre": self.res.append(self.val(i)) self.dfs(self.left(i), order) - # In-order traversal + # Inorder traversal if order == "in": self.res.append(self.val(i)) self.dfs(self.right(i), order) - # Post-order traversal + # Postorder traversal if order == "post": self.res.append(self.val(i)) def pre_order(self) -> list[int]: - """Pre-order traversal""" + """Preorder traversal""" self.res = [] self.dfs(0, order="pre") return self.res def in_order(self) -> list[int]: - """In-order traversal""" + """Inorder traversal""" self.res = [] self.dfs(0, order="in") return self.res def post_order(self) -> list[int]: - """Post-order traversal""" + """Postorder traversal""" self.res = [] self.dfs(0, order="post") return self.res @@ -88,32 +88,32 @@ class ArrayBinaryTree: """Driver Code""" if __name__ == "__main__": # Initialize binary tree - # Use a specific function to convert an array into a binary tree + # Here we use a function to generate a binary tree directly from an array arr = [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] root = list_to_tree(arr) print("\nInitialize binary tree\n") - print("Array representation of the binary tree:") + print("Array representation of binary tree:") print(arr) - print("Linked list representation of the binary tree:") + print("Linked list representation of binary tree:") print_tree(root) - # Array-based binary tree class + # Binary tree class represented by array abt = ArrayBinaryTree(arr) - # Access node + # Access nodes i = 1 l, r, p = abt.left(i), abt.right(i), abt.parent(i) - print(f"\nCurrent node index is {i}, value is {abt.val(i)}") - print(f"Its left child node index is {l}, value is {abt.val(l)}") - print(f"Its right child node index is {r}, value is {abt.val(r)}") - print(f"Its parent node index is {p}, value is {abt.val(p)}") + print(f"\nCurrent node's index is {i}, value is {abt.val(i)}") + print(f"Its left child node's index is {l}, value is {abt.val(l)}") + print(f"Its right child node's index is {r}, value is {abt.val(r)}") + print(f"Its parent node's index is {p}, value is {abt.val(p)}") # Traverse tree res = abt.level_order() print("\nLevel-order traversal is:", res) res = abt.pre_order() - print("Pre-order traversal is:", res) + print("Preorder traversal is:", res) res = abt.in_order() - print("In-order traversal is:", res) + print("Inorder traversal is:", res) res = abt.post_order() - print("Post-order traversal is:", res) + print("Postorder traversal is:", res) diff --git a/en/codes/python/chapter_tree/avl_tree.py b/en/codes/python/chapter_tree/avl_tree.py index 399515e86..f3707870a 100644 --- a/en/codes/python/chapter_tree/avl_tree.py +++ b/en/codes/python/chapter_tree/avl_tree.py @@ -46,31 +46,31 @@ class AVLTree: """Right rotation operation""" child = node.left grand_child = child.right - # Rotate node to the right around child + # Using child as pivot, rotate node to the right child.right = node node.left = grand_child # Update node height self.update_height(node) self.update_height(child) - # Return the root of the subtree after rotation + # Return root node of subtree after rotation return child def left_rotate(self, node: TreeNode | None) -> TreeNode | None: """Left rotation operation""" child = node.right grand_child = child.left - # Rotate node to the left around child + # Using child as pivot, rotate node to the left child.left = node node.right = grand_child # Update node height self.update_height(node) self.update_height(child) - # Return the root of the subtree after rotation + # Return root node of subtree after rotation return child def rotate(self, node: TreeNode | None) -> TreeNode | None: - """Perform rotation operation to restore balance to the subtree""" - # Get the balance factor of node + """Perform rotation operation to restore balance to this subtree""" + # Get balance factor of node balance_factor = self.balance_factor(node) # Left-leaning tree if balance_factor > 1: @@ -90,7 +90,7 @@ class AVLTree: # First right rotation then left rotation node.right = self.right_rotate(node.right) return self.left_rotate(node) - # Balanced tree, no rotation needed, return + # Balanced tree, no rotation needed, return directly return node def insert(self, val): @@ -107,22 +107,22 @@ class AVLTree: elif val > node.val: node.right = self.insert_helper(node.right, val) else: - # Do not insert duplicate nodes, return + # Duplicate node not inserted, return directly return node # Update node height self.update_height(node) - # 2. Perform rotation operation to restore balance to the subtree + # 2. Perform rotation operation to restore balance to this subtree return self.rotate(node) def remove(self, val: int): - """Remove node""" + """Delete node""" self._root = self.remove_helper(self._root, val) def remove_helper(self, node: TreeNode | None, val: int) -> TreeNode | None: - """Recursively remove node (helper method)""" + """Recursively delete node (helper method)""" if node is None: return None - # 1. Find and remove the node + # 1. Find node and delete if val < node.val: node.left = self.remove_helper(node.left, val) elif val > node.val: @@ -130,14 +130,14 @@ class AVLTree: else: if node.left is None or node.right is None: child = node.left or node.right - # Number of child nodes = 0, remove node and return + # Number of child nodes = 0, delete node directly and return if child is None: return None - # Number of child nodes = 1, remove node + # Number of child nodes = 1, delete node directly else: node = child else: - # Number of child nodes = 2, remove the next node in in-order traversal and replace the current node with it + # Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it temp = node.right while temp.left is not None: temp = temp.left @@ -145,13 +145,13 @@ class AVLTree: node.val = temp.val # Update node height self.update_height(node) - # 2. Perform rotation operation to restore balance to the subtree + # 2. Perform rotation operation to restore balance to this subtree return self.rotate(node) def search(self, val: int) -> TreeNode | None: """Search node""" cur = self._root - # Loop find, break after passing leaf nodes + # Loop search, exit after passing leaf node while cur is not None: # Target node is in cur's right subtree if cur.val < val: @@ -159,7 +159,7 @@ class AVLTree: # Target node is in cur's left subtree elif cur.val > val: cur = cur.left - # Found target node, break loop + # Found target node, exit loop else: break # Return target node @@ -171,30 +171,30 @@ if __name__ == "__main__": def test_insert(tree: AVLTree, val: int): tree.insert(val) - print("\nInsert node {} after, AVL tree is".format(val)) + print("\nAfter inserting node {}, AVL tree is".format(val)) print_tree(tree.get_root()) def test_remove(tree: AVLTree, val: int): tree.remove(val) - print("\nRemove node {} after, AVL tree is".format(val)) + print("\nAfter deleting node {}, AVL tree is".format(val)) print_tree(tree.get_root()) # Initialize empty AVL tree avl_tree = AVLTree() - # Insert node - # Notice how the AVL tree maintains balance after inserting nodes + # Insert nodes + # Please pay attention to how the AVL tree maintains balance after inserting nodes for val in [1, 2, 3, 4, 5, 8, 7, 9, 10, 6]: test_insert(avl_tree, val) # Insert duplicate node test_insert(avl_tree, 7) - # Remove node - # Notice how the AVL tree maintains balance after removing nodes - test_remove(avl_tree, 8) # Remove node with degree 0 - test_remove(avl_tree, 5) # Remove node with degree 1 - test_remove(avl_tree, 4) # Remove node with degree 2 + # Delete nodes + # Please pay attention to how the AVL tree maintains balance after deleting nodes + test_remove(avl_tree, 8) # Delete node with degree 0 + test_remove(avl_tree, 5) # Delete node with degree 1 + test_remove(avl_tree, 4) # Delete node with degree 2 result_node = avl_tree.search(7) print("\nFound node object is {}, node value = {}".format(result_node, result_node.val)) diff --git a/en/codes/python/chapter_tree/binary_search_tree.py b/en/codes/python/chapter_tree/binary_search_tree.py index e4a6d1f3e..f9b1da735 100644 --- a/en/codes/python/chapter_tree/binary_search_tree.py +++ b/en/codes/python/chapter_tree/binary_search_tree.py @@ -26,7 +26,7 @@ class BinarySearchTree: def search(self, num: int) -> TreeNode | None: """Search node""" cur = self._root - # Loop find, break after passing leaf nodes + # Loop search, exit after passing leaf node while cur is not None: # Target node is in cur's right subtree if cur.val < num: @@ -34,7 +34,7 @@ class BinarySearchTree: # Target node is in cur's left subtree elif cur.val > num: cur = cur.left - # Found target node, break loop + # Found target node, exit loop else: break return cur @@ -45,10 +45,10 @@ class BinarySearchTree: if self._root is None: self._root = TreeNode(num) return - # Loop find, break after passing leaf nodes + # Loop search, exit after passing leaf node cur, pre = self._root, None while cur is not None: - # Found duplicate node, thus return + # Found duplicate node, return directly if cur.val == num: return pre = cur @@ -66,47 +66,47 @@ class BinarySearchTree: pre.left = node def remove(self, num: int): - """Remove node""" - # If tree is empty, return + """Delete node""" + # If tree is empty, return directly if self._root is None: return - # Loop find, break after passing leaf nodes + # Loop search, exit after passing leaf node cur, pre = self._root, None while cur is not None: - # Found node to be removed, break loop + # Found node to delete, exit loop if cur.val == num: break pre = cur - # Node to be removed is in cur's right subtree + # Node to delete is in cur's right subtree if cur.val < num: cur = cur.right - # Node to be removed is in cur's left subtree + # Node to delete is in cur's left subtree else: cur = cur.left - # If no node to be removed, return + # If no node to delete, return directly if cur is None: return # Number of child nodes = 0 or 1 if cur.left is None or cur.right is None: - # When the number of child nodes = 0/1, child = null/that child node + # When number of child nodes = 0 / 1, child = null / that child node child = cur.left or cur.right - # Remove node cur + # Delete node cur if cur != self._root: if pre.left == cur: pre.left = child else: pre.right = child else: - # If the removed node is the root, reassign the root + # If deleted node is root node, reassign root node self._root = child # Number of child nodes = 2 else: - # Get the next node in in-order traversal of cur + # Get next node of cur in inorder traversal tmp: TreeNode = cur.right while tmp.left is not None: tmp = tmp.left - # Recursively remove node tmp + # Recursively delete node tmp self.remove(tmp.val) # Replace cur with tmp cur.val = tmp.val @@ -117,7 +117,7 @@ if __name__ == "__main__": # Initialize binary search tree bst = BinarySearchTree() nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15] - # Note that different insertion orders can result in various tree structures. This particular sequence creates a perfect binary tree + # Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree for num in nums: bst.insert(num) print("\nInitialized binary tree is\n") @@ -129,18 +129,18 @@ if __name__ == "__main__": # Insert node bst.insert(16) - print("\nAfter inserting node 16, the binary tree is\n") + print("\nAfter inserting node 16, binary tree is\n") print_tree(bst.get_root()) - # Remove node + # Delete node bst.remove(1) - print("\nAfter removing node 1, the binary tree is\n") + print("\nAfter deleting node 1, binary tree is\n") print_tree(bst.get_root()) bst.remove(2) - print("\nAfter removing node 2, the binary tree is\n") + print("\nAfter deleting node 2, binary tree is\n") print_tree(bst.get_root()) bst.remove(4) - print("\nAfter removing node 4, the binary tree is\n") + print("\nAfter deleting node 4, binary tree is\n") print_tree(bst.get_root()) diff --git a/en/codes/python/chapter_tree/binary_tree.py b/en/codes/python/chapter_tree/binary_tree.py index cdfb8416d..0bc591f62 100644 --- a/en/codes/python/chapter_tree/binary_tree.py +++ b/en/codes/python/chapter_tree/binary_tree.py @@ -14,13 +14,13 @@ from modules import TreeNode, print_tree """Driver Code""" if __name__ == "__main__": # Initialize binary tree - # Initialize node + # Initialize nodes n1 = TreeNode(val=1) n2 = TreeNode(val=2) n3 = TreeNode(val=3) n4 = TreeNode(val=4) n5 = TreeNode(val=5) - # Construct node references (pointers) + # Build references (pointers) between nodes n1.left = n2 n1.right = n3 n2.left = n4 @@ -28,14 +28,14 @@ if __name__ == "__main__": print("\nInitialize binary tree\n") print_tree(n1) - # Insert and remove nodes + # Insert and delete nodes P = TreeNode(0) # Insert node P between n1 -> n2 n1.left = P P.left = n2 print("\nAfter inserting node P\n") print_tree(n1) - # Remove node + # Delete node n1.left = n2 - print("\nAfter removing node P\n") + print("\nAfter deleting node P\n") print_tree(n1) diff --git a/en/codes/python/chapter_tree/binary_tree_bfs.py b/en/codes/python/chapter_tree/binary_tree_bfs.py index 77e1ceac7..95d596afc 100644 --- a/en/codes/python/chapter_tree/binary_tree_bfs.py +++ b/en/codes/python/chapter_tree/binary_tree_bfs.py @@ -17,26 +17,26 @@ def level_order(root: TreeNode | None) -> list[int]: # Initialize queue, add root node queue: deque[TreeNode] = deque() queue.append(root) - # Initialize a list to store the traversal sequence + # Initialize a list to save the traversal sequence res = [] while queue: - node: TreeNode = queue.popleft() # Queue dequeues + node: TreeNode = queue.popleft() # Dequeue res.append(node.val) # Save node value if node.left is not None: - queue.append(node.left) # Left child node enqueues + queue.append(node.left) # Left child node enqueue if node.right is not None: - queue.append(node.right) # Right child node enqueues + queue.append(node.right) # Right child node enqueue return res """Driver Code""" if __name__ == "__main__": # Initialize binary tree - # Use a specific function to convert an array into a binary tree + # Here we use a function to generate a binary tree directly from an array root: TreeNode = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7]) print("\nInitialize binary tree\n") print_tree(root) # Level-order traversal res: list[int] = level_order(root) - print("\nPrint sequence of nodes from level-order traversal = ", res) + print("\nLevel-order traversal node print sequence = ", res) diff --git a/en/codes/python/chapter_tree/binary_tree_dfs.py b/en/codes/python/chapter_tree/binary_tree_dfs.py index d25afe649..c20e97708 100644 --- a/en/codes/python/chapter_tree/binary_tree_dfs.py +++ b/en/codes/python/chapter_tree/binary_tree_dfs.py @@ -12,7 +12,7 @@ from modules import TreeNode, list_to_tree, print_tree def pre_order(root: TreeNode | None): - """Pre-order traversal""" + """Preorder traversal""" if root is None: return # Visit priority: root node -> left subtree -> right subtree @@ -22,7 +22,7 @@ def pre_order(root: TreeNode | None): def in_order(root: TreeNode | None): - """In-order traversal""" + """Inorder traversal""" if root is None: return # Visit priority: left subtree -> root node -> right subtree @@ -32,7 +32,7 @@ def in_order(root: TreeNode | None): def post_order(root: TreeNode | None): - """Post-order traversal""" + """Postorder traversal""" if root is None: return # Visit priority: left subtree -> right subtree -> root node @@ -44,22 +44,22 @@ def post_order(root: TreeNode | None): """Driver Code""" if __name__ == "__main__": # Initialize binary tree - # Use a specific function to convert an array into a binary tree + # Here we use a function to generate a binary tree directly from an array root = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7]) print("\nInitialize binary tree\n") print_tree(root) - # Pre-order traversal + # Preorder traversal res = [] pre_order(root) - print("\nPrint sequence of nodes from pre-order traversal = ", res) + print("\nPreorder traversal node print sequence = ", res) - # In-order traversal + # Inorder traversal res.clear() in_order(root) - print("\nPrint sequence of nodes from in-order traversal = ", res) + print("\nInorder traversal node print sequence = ", res) - # Post-order traversal + # Postorder traversal res.clear() post_order(root) - print("\nPrint sequence of nodes from post-order traversal = ", res) + print("\nPostorder traversal node print sequence = ", res) diff --git a/en/codes/python/modules/list_node.py b/en/codes/python/modules/list_node.py index 1d158f7c4..61e3a9a57 100644 --- a/en/codes/python/modules/list_node.py +++ b/en/codes/python/modules/list_node.py @@ -6,11 +6,11 @@ Author: krahets (krahets@163.com) class ListNode: - """LinkedList node class""" + """Linked list node class""" def __init__(self, val: int): self.val: int = val # Node value - self.next: ListNode | None = None # Reference to successor node + self.next: ListNode | None = None # Reference to next node def list_to_linked_list(arr: list[int]) -> ListNode | None: diff --git a/en/codes/python/modules/tree_node.py b/en/codes/python/modules/tree_node.py index 95bfef3fb..f19230482 100644 --- a/en/codes/python/modules/tree_node.py +++ b/en/codes/python/modules/tree_node.py @@ -13,14 +13,14 @@ class TreeNode: def __init__(self, val: int = 0): self.val: int = val # Node value self.height: int = 0 # Node height - self.left: TreeNode | None = None # Reference to the left child node - self.right: TreeNode | None = None # Reference to the right child node + self.left: TreeNode | None = None # Reference to left child node + self.right: TreeNode | None = None # Reference to right child node - # For serialization encoding rules, refer to: + # For the serialization encoding rules, please refer to: # https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ - # Array representation of the binary tree: + # Array representation of binary tree: # [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] - # Linked list representation of the binary tree: + # Linked list representation of binary tree: # /——— 15 # /——— 7 # /——— 3 @@ -34,13 +34,13 @@ class TreeNode: def list_to_tree_dfs(arr: list[int], i: int) -> TreeNode | None: - """Deserialize a list into a binary tree: Recursively""" - # If the index is out of array bounds, or the corresponding element is None, return None + """Deserialize a list into a binary tree: recursion""" + # If the index exceeds the array length, or the corresponding element is None, return None if i < 0 or i >= len(arr) or arr[i] is None: return None - # Construct the current node + # Build the current node root = TreeNode(arr[i]) - # Recursively construct left and right subtrees + # Recursively build the left and right subtrees root.left = list_to_tree_dfs(arr, 2 * i + 1) root.right = list_to_tree_dfs(arr, 2 * i + 2) return root @@ -52,7 +52,7 @@ def list_to_tree(arr: list[int]) -> TreeNode | None: def tree_to_list_dfs(root: TreeNode, i: int, res: list[int]) -> list[int]: - """Serialize a binary tree into a list: Recursively""" + """Serialize a binary tree into a list: recursion""" if root is None: return if i >= len(res): diff --git a/en/codes/python/modules/vertex.py b/en/codes/python/modules/vertex.py index 40602dde9..6da527db0 100644 --- a/en/codes/python/modules/vertex.py +++ b/en/codes/python/modules/vertex.py @@ -11,10 +11,10 @@ class Vertex: def vals_to_vets(vals: list[int]) -> list["Vertex"]: - """Input a list of values vals, return a list of vertices vets""" + """Input value list vals, return vertex list vets""" return [Vertex(val) for val in vals] def vets_to_vals(vets: list["Vertex"]) -> list[int]: - """Input a list of vertices vets, return a list of values vals""" + """Input vertex list vets, return value list vals""" return [vet.val for vet in vets] diff --git a/en/codes/python/test_all.py b/en/codes/python/test_all.py index c1d8c3b03..7c585de87 100644 --- a/en/codes/python/test_all.py +++ b/en/codes/python/test_all.py @@ -7,7 +7,7 @@ env["PYTHONIOENCODING"] = "utf-8" if __name__ == "__main__": # find source code files - src_paths = sorted(glob.glob("en/codes/python/chapter_*/*.py")) + src_paths = sorted(glob.glob("chapter_*/*.py")) errors = [] # run python code diff --git a/en/codes/ruby/chapter_array_and_linkedlist/array.rb b/en/codes/ruby/chapter_array_and_linkedlist/array.rb new file mode 100644 index 000000000..15bab7d77 --- /dev/null +++ b/en/codes/ruby/chapter_array_and_linkedlist/array.rb @@ -0,0 +1,108 @@ +=begin +File: array.rb +Created Time: 2024-03-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Random access element ### +def random_access(nums) + # Randomly select a number in the interval [0, nums.length) + random_index = Random.rand(0...nums.length) + + # Retrieve and return the random element + nums[random_index] +end + + +### Extend array length ### +# Note: Ruby's Array is dynamic array, can be directly expanded +# For learning purposes, this function treats Array as fixed-length array +def extend(nums, enlarge) + # Initialize an array with extended length + res = Array.new(nums.length + enlarge, 0) + + # Copy all elements from the original array to the new array + for i in 0...nums.length + res[i] = nums[i] + end + + # Return the extended new array + res +end + +### Insert element num at index in array ### +def insert(nums, num, index) + # Move all elements at and after index index backward by one position + for i in (nums.length - 1).downto(index + 1) + nums[i] = nums[i - 1] + end + + # Assign num to the element at index index + nums[index] = num +end + + +### Delete element at index ### +def remove(nums, index) + # Move all elements after index index forward by one position + for i in index...(nums.length - 1) + nums[i] = nums[i + 1] + end +end + +### Traverse array ### +def traverse(nums) + count = 0 + + # Traverse array by index + for i in 0...nums.length + count += nums[i] + end + + # Direct traversal of array elements + for num in nums + count += num + end +end + +### Find specified element in array ### +def find(nums, target) + for i in 0...nums.length + return i if nums[i] == target + end + + -1 +end + + +### Driver Code ### +if __FILE__ == $0 + # Initialize array + arr = Array.new(5, 0) + puts "Array arr = #{arr}" + nums = [1, 3, 2, 5, 4] + puts "Array nums = #{nums}" + + # Insert element + random_num = random_access(nums) + puts "Get random element #{random_num} from nums" + + # Traverse array + nums = extend(nums, 3) + puts "Extend array length to 8, get nums = #{nums}" + + # Insert element + insert(nums, 6, 3) + puts "Insert number 6 at index 3, get nums = #{nums}" + + # Remove element + remove(nums, 2) + puts "Delete element at index 2, get nums = #{nums}" + + # Traverse array + traverse(nums) + + # Find element + index = find(nums, 3) + puts "Find element 3 in nums, index = #{index}" +end diff --git a/en/codes/ruby/chapter_array_and_linkedlist/linked_list.rb b/en/codes/ruby/chapter_array_and_linkedlist/linked_list.rb new file mode 100644 index 000000000..030b72ace --- /dev/null +++ b/en/codes/ruby/chapter_array_and_linkedlist/linked_list.rb @@ -0,0 +1,83 @@ +=begin +File: linked_list.rb +Created Time: 2024-03-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/list_node' +require_relative '../utils/print_util' + +### Insert node _p after node n0 in linked list ### +# Ruby's `p` is a built-in function, `P` is a constant, so use `_p` instead +def insert(n0, _p) + n1 = n0.next + _p.next = n1 + n0.next = _p +end + +### Delete first node after node n0 in linked list ### +def remove(n0) + return if n0.next.nil? + + # n0 -> remove_node -> n1 + remove_node = n0.next + n1 = remove_node.next + n0.next = n1 +end + +### Access node at index in linked list ### +def access(head, index) + for i in 0...index + return nil if head.nil? + head = head.next + end + + head +end + +### Find first node with value target in linked list ### +def find(head, target) + index = 0 + while head + return index if head.val == target + head = head.next + index += 1 + end + + -1 +end + +### Driver Code ### +if __FILE__ == $0 + # Initialize linked list + # Initialize each node + n0 = ListNode.new(1) + n1 = ListNode.new(3) + n2 = ListNode.new(2) + n3 = ListNode.new(5) + n4 = ListNode.new(4) + # Build references between nodes + n0.next = n1 + n1.next = n2 + n2.next = n3 + n3.next = n4 + puts "Initialized linked list is" + print_linked_list(n0) + + # Insert node + insert(n0, ListNode.new(0)) + print_linked_list n0 + + # Remove node + remove(n0) + puts "Linked list after removing node is" + print_linked_list(n0) + + # Access node + node = access(n0, 3) + puts "Value of node at index 3 in linked list = #{node.val}" + + # Search node + index = find(n0, 2) + puts "Index of node with value 2 in linked list = #{index}" +end diff --git a/en/codes/ruby/chapter_array_and_linkedlist/list.rb b/en/codes/ruby/chapter_array_and_linkedlist/list.rb new file mode 100644 index 000000000..6bb01e8bf --- /dev/null +++ b/en/codes/ruby/chapter_array_and_linkedlist/list.rb @@ -0,0 +1,60 @@ +=begin +File: list.rb +Created Time: 2024-03-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Driver Code ### +if __FILE__ == $0 + # Initialize list + nums = [1, 3, 2, 5, 4] + puts "List nums = #{nums}" + + # Update element + num = nums[1] + puts "Access element at index 1, get num = #{num}" + + # Add elements at the end + nums[1] = 0 + puts "Update element at index 1 to 0, get nums = #{nums}" + + # Remove element + nums.clear + puts "After clearing list, nums = #{nums}" + + # Direct traversal of list elements + nums << 1 + nums << 3 + nums << 2 + nums << 5 + nums << 4 + puts "After adding elements, nums = #{nums}" + + # Sort list + nums.insert(3, 6) + puts "Insert element 6 at index 3, get nums = #{nums}" + + # Remove element + nums.delete_at(3) + puts "Delete element at index 3, get nums = #{nums}" + + # Traverse list by index + count = 0 + for i in 0...nums.length + count += nums[i] + end + + # Directly traverse list elements + count = 0 + nums.each do |x| + count += x + end + + # Concatenate two lists + nums1 = [6, 8, 7, 10, 9] + nums += nums1 + puts "After concatenating list nums1 to nums, get nums = #{nums}" + + nums = nums.sort { |a, b| a <=> b } + puts "After sorting list, nums = #{nums}" +end diff --git a/en/codes/ruby/chapter_array_and_linkedlist/my_list.rb b/en/codes/ruby/chapter_array_and_linkedlist/my_list.rb new file mode 100644 index 000000000..ecd0a5159 --- /dev/null +++ b/en/codes/ruby/chapter_array_and_linkedlist/my_list.rb @@ -0,0 +1,132 @@ +=begin +File: my_list.rb +Created Time: 2024-03-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### List class ### +class MyList + attr_reader :size # Get list length (current number of elements) + attr_reader :capacity # Get list capacity + + ### Constructor ### + def initialize + @capacity = 10 + @size = 0 + @extend_ratio = 2 + @arr = Array.new(capacity) + end + + ### Access element ### + def get(index) + # If the index is out of bounds, throw an exception, as below + raise IndexError, "Index out of bounds" if index < 0 || index >= size + @arr[index] + end + + ### Access element ### + def set(index, num) + raise IndexError, "Index out of bounds" if index < 0 || index >= size + @arr[index] = num + end + + ### Add element at end ### + def add(num) + # When the number of elements exceeds capacity, trigger the extension mechanism + extend_capacity if size == capacity + @arr[size] = num + + # Update the number of elements + @size += 1 + end + + ### Insert element in middle ### + def insert(index, num) + raise IndexError, "Index out of bounds" if index < 0 || index >= size + + # When the number of elements exceeds capacity, trigger the extension mechanism + extend_capacity if size == capacity + + # Move all elements after index index forward by one position + for j in (size - 1).downto(index) + @arr[j + 1] = @arr[j] + end + @arr[index] = num + + # Update the number of elements + @size += 1 + end + + ### Delete element ### + def remove(index) + raise IndexError, "Index out of bounds" if index < 0 || index >= size + num = @arr[index] + + # Move all elements after index forward by one position + for j in index...size + @arr[j] = @arr[j + 1] + end + + # Update the number of elements + @size -= 1 + + # Return the removed element + num + end + + ### Expand list capacity ### + def extend_capacity + # Create new array with length extend_ratio times original, copy original array to new array + arr = @arr.dup + Array.new(capacity * (@extend_ratio - 1)) + # Add elements at the end + @capacity = arr.length + end + + ### Convert list to array ### + def to_array + sz = size + # Elements enqueue + arr = Array.new(sz) + for i in 0...sz + arr[i] = get(i) + end + arr + end +end + +### Driver Code ### +if __FILE__ == $0 + # Initialize list + nums = MyList.new + + # Direct traversal of list elements + nums.add(1) + nums.add(3) + nums.add(2) + nums.add(5) + nums.add(4) + puts "List nums = #{nums.to_array}, capacity = #{nums.capacity}, length = #{nums.size}" + + # Sort list + nums.insert(3, 6) + puts "Insert number 6 at index 3, get nums = #{nums.to_array}" + + # Remove element + nums.remove(3) + puts "Delete element at index 3, get nums = #{nums.to_array}" + + # Update element + num = nums.get(1) + puts "Access element at index 1, get num = #{num}" + + # Add elements at the end + nums.set(1, 0) + puts "Update element at index 1 to 0, get nums = #{nums.to_array}" + + # Test capacity expansion mechanism + for i in 0...10 + # At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + nums.add(i) + end + puts "After expansion, list nums = #{nums.to_array}, capacity = #{nums.capacity}, length = #{nums.size}" +end diff --git a/en/codes/ruby/chapter_backtracking/n_queens.rb b/en/codes/ruby/chapter_backtracking/n_queens.rb new file mode 100644 index 000000000..5876751ea --- /dev/null +++ b/en/codes/ruby/chapter_backtracking/n_queens.rb @@ -0,0 +1,61 @@ +=begin +File: n_queens.rb +Created Time: 2024-05-21 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Backtracking: n queens ### +def backtrack(row, n, state, res, cols, diags1, diags2) + # When all rows are placed, record the solution + if row == n + res << state.map { |row| row.dup } + return + end + + # Traverse all columns + for col in 0...n + # Calculate the main diagonal and anti-diagonal corresponding to this cell + diag1 = row - col + n - 1 + diag2 = row + col + # Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + if !cols[col] && !diags1[diag1] && !diags2[diag2] + # Attempt: place the queen in this cell + state[row][col] = "Q" + cols[col] = diags1[diag1] = diags2[diag2] = true + # Place the next row + backtrack(row + 1, n, state, res, cols, diags1, diags2) + # Backtrack: restore this cell to an empty cell + state[row][col] = "#" + cols[col] = diags1[diag1] = diags2[diag2] = false + end + end +end + +### Solve n queens ### +def n_queens(n) + # Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + state = Array.new(n) { Array.new(n, "#") } + cols = Array.new(n, false) # Record whether there is a queen in the column + diags1 = Array.new(2 * n - 1, false) # Record whether there is a queen on the main diagonal + diags2 = Array.new(2 * n - 1, false) # Record whether there is a queen on the anti-diagonal + res = [] + backtrack(0, n, state, res, cols, diags1, diags2) + + res +end + +### Driver Code ### +if __FILE__ == $0 + n = 4 + res = n_queens(n) + + puts "Input board size is #{n}" + puts "Total queen placement solutions: #{res.length}" + + for state in res + puts "--------------------" + for row in state + p row + end + end +end diff --git a/en/codes/ruby/chapter_backtracking/permutations_i.rb b/en/codes/ruby/chapter_backtracking/permutations_i.rb new file mode 100644 index 000000000..e26a6d6d0 --- /dev/null +++ b/en/codes/ruby/chapter_backtracking/permutations_i.rb @@ -0,0 +1,46 @@ +=begin +File: permutations_i.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Backtracking: permutations I ### +def backtrack(state, choices, selected, res) + # When the state length equals the number of elements, record the solution + if state.length == choices.length + res << state.dup + return + end + + # Traverse all choices + choices.each_with_index do |choice, i| + # Pruning: do not allow repeated selection of elements + unless selected[i] + # Attempt: make choice, update state + selected[i] = true + state << choice + # Proceed to the next round of selection + backtrack(state, choices, selected, res) + # Backtrack: undo choice, restore to previous state + selected[i] = false + state.pop + end + end +end + +### Permutations I ### +def permutations_i(nums) + res = [] + backtrack([], nums, Array.new(nums.length, false), res) + res +end + +### Driver Code ### +if __FILE__ == $0 + nums = [1, 2, 3] + + res = permutations_i(nums) + + puts "Input array nums = #{nums}" + puts "All permutations res = #{res}" +end diff --git a/en/codes/ruby/chapter_backtracking/permutations_ii.rb b/en/codes/ruby/chapter_backtracking/permutations_ii.rb new file mode 100644 index 000000000..70800f7a6 --- /dev/null +++ b/en/codes/ruby/chapter_backtracking/permutations_ii.rb @@ -0,0 +1,48 @@ +=begin +File: permutations_ii.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Backtracking: permutations II ### +def backtrack(state, choices, selected, res) + # When the state length equals the number of elements, record the solution + if state.length == choices.length + res << state.dup + return + end + + # Traverse all choices + duplicated = Set.new + choices.each_with_index do |choice, i| + # Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if !selected[i] && !duplicated.include?(choice) + # Attempt: make choice, update state + duplicated.add(choice) + selected[i] = true + state << choice + # Proceed to the next round of selection + backtrack(state, choices, selected, res) + # Backtrack: undo choice, restore to previous state + selected[i] = false + state.pop + end + end +end + +### Permutations II ### +def permutations_ii(nums) + res = [] + backtrack([], nums, Array.new(nums.length, false), res) + res +end + +### Driver Code ### +if __FILE__ == $0 + nums = [1, 2, 2] + + res = permutations_ii(nums) + + puts "Input array nums = #{nums}" + puts "All permutations res = #{res}" +end diff --git a/en/codes/ruby/chapter_backtracking/preorder_traversal_i_compact.rb b/en/codes/ruby/chapter_backtracking/preorder_traversal_i_compact.rb new file mode 100644 index 000000000..1508430c2 --- /dev/null +++ b/en/codes/ruby/chapter_backtracking/preorder_traversal_i_compact.rb @@ -0,0 +1,33 @@ +=begin +File: preorder_traversal_i_compact.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Pre-order traversal: example 1 ### +def pre_order(root) + return unless root + + # Record solution + $res << root if root.val == 7 + + pre_order(root.left) + pre_order(root.right) +end + +### Driver Code ### +if __FILE__ == $0 + root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) + puts "\nInitialize binary tree" + print_tree(root) + + # Preorder traversal + $res = [] + pre_order(root) + + puts "\nOutput all nodes with value 7" + p $res.map { |node| node.val } +end diff --git a/en/codes/ruby/chapter_backtracking/preorder_traversal_ii_compact.rb b/en/codes/ruby/chapter_backtracking/preorder_traversal_ii_compact.rb new file mode 100644 index 000000000..c744ab484 --- /dev/null +++ b/en/codes/ruby/chapter_backtracking/preorder_traversal_ii_compact.rb @@ -0,0 +1,41 @@ +=begin +File: preorder_traversal_ii_compact.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Pre-order traversal: example 2 ### +def pre_order(root) + return unless root + + # Attempt + $path << root + + # Record solution + $res << $path.dup if root.val == 7 + + pre_order(root.left) + pre_order(root.right) + + # Backtrack + $path.pop +end + +### Driver Code ### +if __FILE__ == $0 + root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) + puts "\nInitialize binary tree" + print_tree(root) + + # Preorder traversal + $path, $res = [], [] + pre_order(root) + + puts "\nOutput all paths from root node to node 7" + for path in $res + p path.map { |node| node.val } + end +end diff --git a/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_compact.rb b/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_compact.rb new file mode 100644 index 000000000..cf6f7d0c3 --- /dev/null +++ b/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_compact.rb @@ -0,0 +1,42 @@ +=begin +File: preorder_traversal_iii_compact.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Pre-order traversal: example 3 ### +def pre_order(root) + # Pruning + return if !root || root.val == 3 + + # Attempt + $path.append(root) + + # Record solution + $res << $path.dup if root.val == 7 + + pre_order(root.left) + pre_order(root.right) + + # Backtrack + $path.pop +end + +### Driver Code ### +if __FILE__ == $0 + root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) + puts "\nInitialize binary tree" + print_tree(root) + + # Preorder traversal + $path, $res = [], [] + pre_order(root) + + puts "\nOutput all paths from root node to node 7, paths do not include nodes with value 3" + for path in $res + p path.map { |node| node.val } + end +end diff --git a/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_template.rb b/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_template.rb new file mode 100644 index 000000000..8022d8ca8 --- /dev/null +++ b/en/codes/ruby/chapter_backtracking/preorder_traversal_iii_template.rb @@ -0,0 +1,68 @@ +=begin +File: preorder_traversal_iii_template.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Check if current state is solution ### +def is_solution?(state) + !state.empty? && state.last.val == 7 +end + +### Record solution ### +def record_solution(state, res) + res << state.dup +end + +### Check if choice is valid in current state ### +def is_valid?(state, choice) + choice && choice.val != 3 +end + +### Update state ### +def make_choice(state, choice) + state << choice +end + +### Restore state ### +def undo_choice(state, choice) + state.pop +end + +### Backtracking: example 3 ### +def backtrack(state, choices, res) + # Check if it is a solution + record_solution(state, res) if is_solution?(state) + + # Traverse all choices + for choice in choices + # Pruning: check if the choice is valid + if is_valid?(state, choice) + # Attempt: make choice, update state + make_choice(state, choice) + # Proceed to the next round of selection + backtrack(state, [choice.left, choice.right], res) + # Backtrack: undo choice, restore to previous state + undo_choice(state, choice) + end + end +end + +### Driver Code ### +if __FILE__ == $0 + root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) + puts "\nInitialize binary tree" + print_tree(root) + + # Backtracking algorithm + res = [] + backtrack([], [root], res) + + puts "\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3" + for path in res + p path.map { |node| node.val } + end +end diff --git a/en/codes/ruby/chapter_backtracking/subset_sum_i.rb b/en/codes/ruby/chapter_backtracking/subset_sum_i.rb new file mode 100644 index 000000000..9966e851e --- /dev/null +++ b/en/codes/ruby/chapter_backtracking/subset_sum_i.rb @@ -0,0 +1,47 @@ +=begin +File: subset_sum_i.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Backtracking: subset sum I ### +def backtrack(state, target, choices, start, res) + # When the subset sum equals target, record the solution + if target.zero? + res << state.dup + return + end + # Traverse all choices + # Pruning 2: start traversing from start to avoid generating duplicate subsets + for i in start...choices.length + # Pruning 1: if the subset sum exceeds target, end the loop directly + # This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + break if target - choices[i] < 0 + # Attempt: make choice, update target, start + state << choices[i] + # Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i, res) + # Backtrack: undo choice, restore to previous state + state.pop + end +end + +### Solve subset sum I ### +def subset_sum_i(nums, target) + state = [] # State (subset) + nums.sort! # Sort nums + start = 0 # Start point for traversal + res = [] # Result list (subset list) + backtrack(state, target, nums, start, res) + res +end + +### Driver Code ### +if __FILE__ == $0 + nums = [3, 4, 5] + target = 9 + res = subset_sum_i(nums, target) + + puts "Input array = #{nums}, target = #{target}" + puts "All subsets with sum equal to #{target} res = #{res}" +end diff --git a/en/codes/ruby/chapter_backtracking/subset_sum_i_naive.rb b/en/codes/ruby/chapter_backtracking/subset_sum_i_naive.rb new file mode 100644 index 000000000..344350eb4 --- /dev/null +++ b/en/codes/ruby/chapter_backtracking/subset_sum_i_naive.rb @@ -0,0 +1,46 @@ +=begin +File: subset_sum_i_naive.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Backtracking: subset sum I ### +def backtrack(state, target, total, choices, res) + # When the subset sum equals target, record the solution + if total == target + res << state.dup + return + end + + # Traverse all choices + for i in 0...choices.length + # Pruning: if the subset sum exceeds target, skip this choice + next if total + choices[i] > target + # Attempt: make choice, update element sum total + state << choices[i] + # Proceed to the next round of selection + backtrack(state, target, total + choices[i], choices, res) + # Backtrack: undo choice, restore to previous state + state.pop + end +end + +### Solve subset sum I (with duplicate subsets) ### +def subset_sum_i_naive(nums, target) + state = [] # State (subset) + total = 0 # Subset sum + res = [] # Result list (subset list) + backtrack(state, target, total, nums, res) + res +end + +### Driver Code ### +if __FILE__ == $0 + nums = [3, 4, 5] + target = 9 + res = subset_sum_i_naive(nums, target) + + puts "Input array nums = #{nums}, target = #{target}" + puts "All subsets with sum equal to #{target} res = #{res}" + puts "Please note that this method outputs results containing duplicate sets" +end diff --git a/en/codes/ruby/chapter_backtracking/subset_sum_ii.rb b/en/codes/ruby/chapter_backtracking/subset_sum_ii.rb new file mode 100644 index 000000000..9b66cbaab --- /dev/null +++ b/en/codes/ruby/chapter_backtracking/subset_sum_ii.rb @@ -0,0 +1,51 @@ +=begin +File: subset_sum_ii.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Backtracking: subset sum II ### +def backtrack(state, target, choices, start, res) + # When the subset sum equals target, record the solution + if target.zero? + res << state.dup + return + end + + # Traverse all choices + # Pruning 2: start traversing from start to avoid generating duplicate subsets + # Pruning 3: start traversing from start to avoid repeatedly selecting the same element + for i in start...choices.length + # Pruning 1: if the subset sum exceeds target, end the loop directly + # This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + break if target - choices[i] < 0 + # Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + next if i > start && choices[i] == choices[i - 1] + # Attempt: make choice, update target, start + state << choices[i] + # Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i + 1, res) + # Backtrack: undo choice, restore to previous state + state.pop + end +end + +### Solve subset sum II ### +def subset_sum_ii(nums, target) + state = [] # State (subset) + nums.sort! # Sort nums + start = 0 # Start point for traversal + res = [] # Result list (subset list) + backtrack(state, target, nums, start, res) + res +end + +### Driver Code ### +if __FILE__ == $0 + nums = [4, 4, 5] + target = 9 + res = subset_sum_ii(nums, target) + + puts "Input array nums = #{nums}, target = #{target}" + puts "All subsets with sum equal to #{target} res = #{res}" +end diff --git a/en/codes/ruby/chapter_computational_complexity/iteration.rb b/en/codes/ruby/chapter_computational_complexity/iteration.rb new file mode 100644 index 000000000..d303eb336 --- /dev/null +++ b/en/codes/ruby/chapter_computational_complexity/iteration.rb @@ -0,0 +1,79 @@ +=begin +File: iteration.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com), Cy (9738314@gmail.com) +=end + +### for loop ### +def for_loop(n) + res = 0 + + # Sum 1, 2, ..., n-1, n + for i in 1..n + res += i + end + + res +end + +### while loop ### +def while_loop(n) + res = 0 + i = 1 # Initialize condition variable + + # Sum 1, 2, ..., n-1, n + while i <= n + res += i + i += 1 # Update condition variable + end + + res +end + +### while loop (two updates) ### +def while_loop_ii(n) + res = 0 + i = 1 # Initialize condition variable + + # Sum 1, 4, 10, ... + while i <= n + res += i + # Update condition variable + i += 1 + i *= 2 + end + + res +end + +### Nested for loop ### +def nested_for_loop(n) + res = "" + + # Loop i = 1, 2, ..., n-1, n + for i in 1..n + # Loop j = 1, 2, ..., n-1, n + for j in 1..n + res += "(#{i}, #{j}), " + end + end + + res +end + +### Driver Code ### +if __FILE__ == $0 + n = 5 + + res = for_loop(n) + puts "\nFor loop sum result res = #{res}" + + res = while_loop(n) + puts "\nWhile loop sum result res = #{res}" + + res = while_loop_ii(n) + puts "\nWhile loop (two updates) sum result res = #{res}" + + res = nested_for_loop(n) + puts "\nNested for loop traversal result #{res}" +end diff --git a/en/codes/ruby/chapter_computational_complexity/recursion.rb b/en/codes/ruby/chapter_computational_complexity/recursion.rb new file mode 100644 index 000000000..3f53cc5be --- /dev/null +++ b/en/codes/ruby/chapter_computational_complexity/recursion.rb @@ -0,0 +1,70 @@ +=begin +File: recursion.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Recursion ### +def recur(n) + # Termination condition + return 1 if n == 1 + # Recurse: recursive call + res = recur(n - 1) + # Return: return result + n + res +end + +### Use iteration to simulate recursion ### +def for_loop_recur(n) + # Use an explicit stack to simulate the system call stack + stack = [] + res = 0 + + # Recurse: recursive call + for i in n.downto(0) + # Simulate "recurse" with "push" + stack << i + end + # Return: return result + while !stack.empty? + res += stack.pop + end + + # res = 1+2+3+...+n + res +end + +### Tail recursion ### +def tail_recur(n, res) + # Termination condition + return res if n == 0 + # Tail recursive call + tail_recur(n - 1, res + n) +end + +### Fibonacci sequence: recursion ### +def fib(n) + # Termination condition f(1) = 0, f(2) = 1 + return n - 1 if n == 1 || n == 2 + # Recursive call f(n) = f(n-1) + f(n-2) + res = fib(n - 1) + fib(n - 2) + # Return result f(n) + res +end + +### Driver Code ### +if __FILE__ == $0 + n = 5 + + res = recur(n) + puts "\nRecursion sum result res = #{res}" + + res = for_loop_recur(n) + puts "\nUsing iteration to simulate recursion sum result res = #{res}" + + res = tail_recur(n, 0) + puts "\nTail recursion sum result res = #{res}" + + res = fib(n) + puts "\nThe #{n}th Fibonacci number is #{res}" +end diff --git a/en/codes/ruby/chapter_computational_complexity/space_complexity.rb b/en/codes/ruby/chapter_computational_complexity/space_complexity.rb new file mode 100644 index 000000000..c50ce0da5 --- /dev/null +++ b/en/codes/ruby/chapter_computational_complexity/space_complexity.rb @@ -0,0 +1,92 @@ +=begin +File: space_complexity.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/list_node' +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Function ### +def function + # Perform some operations + 0 +end + +### Constant time ### +def constant(n) + # Constants, variables, objects occupy O(1) space + a = 0 + nums = [0] * 10000 + node = ListNode.new + + # Variables in the loop occupy O(1) space + (0...n).each { c = 0 } + # Functions in the loop occupy O(1) space + (0...n).each { function } +end + +### Linear time ### +def linear(n) + # A list of length n occupies O(n) space + nums = Array.new(n, 0) + + # A hash table of length n occupies O(n) space + hmap = {} + for i in 0...n + hmap[i] = i.to_s + end +end + +### Linear space (recursive) ### +def linear_recur(n) + puts "Recursion n = #{n}" + return if n == 1 + linear_recur(n - 1) +end + +### Quadratic time ### +def quadratic(n) + # 2D list uses O(n^2) space + Array.new(n) { Array.new(n, 0) } +end + +### Quadratic space (recursive) ### +def quadratic_recur(n) + return 0 unless n > 0 + + # Array nums has length n, n-1, ..., 2, 1 + nums = Array.new(n, 0) + quadratic_recur(n - 1) +end + +### Exponential space (build full binary tree) ### +def build_tree(n) + return if n == 0 + + TreeNode.new.tap do |root| + root.left = build_tree(n - 1) + root.right = build_tree(n - 1) + end +end + +### Driver Code ### +if __FILE__ == $0 + n = 5 + + # Constant order + constant(n) + + # Linear order + linear(n) + linear_recur(n) + + # Exponential order + quadratic(n) + quadratic_recur(n) + + # Exponential order + root = build_tree(n) + print_tree(root) +end diff --git a/en/codes/ruby/chapter_computational_complexity/time_complexity.rb b/en/codes/ruby/chapter_computational_complexity/time_complexity.rb new file mode 100644 index 000000000..c9da271dc --- /dev/null +++ b/en/codes/ruby/chapter_computational_complexity/time_complexity.rb @@ -0,0 +1,165 @@ +=begin +File: time_complexity.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Constant time ### +def constant(n) + count = 0 + size = 100000 + + (0...size).each { count += 1 } + + count +end + +### Linear time ### +def linear(n) + count = 0 + (0...n).each { count += 1 } + count +end + +### Linear time (array traversal) ### +def array_traversal(nums) + count = 0 + + # Number of iterations is proportional to the array length + for num in nums + count += 1 + end + + count +end + +### Quadratic time ### +def quadratic(n) + count = 0 + + # Number of iterations is quadratically related to the data size n + for i in 0...n + for j in 0...n + count += 1 + end + end + + count +end + +### Quadratic time (bubble sort) ### +def bubble_sort(nums) + count = 0 # Counter + + # Outer loop: unsorted range is [0, i] + for i in (nums.length - 1).downto(0) + # Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for j in 0...i + if nums[j] > nums[j + 1] + # Swap nums[j] and nums[j + 1] + tmp = nums[j] + nums[j] = nums[j + 1] + nums[j + 1] = tmp + count += 3 # Element swap includes 3 unit operations + end + end + end + + count +end + +### Exponential time (iterative) ### +def exponential(n) + count, base = 0, 1 + + # Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + (0...n).each do + (0...base).each { count += 1 } + base *= 2 + end + + # count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + count +end + +### Exponential time (recursive) ### +def exp_recur(n) + return 1 if n == 1 + exp_recur(n - 1) + exp_recur(n - 1) + 1 +end + +### Logarithmic time (iterative) ### +def logarithmic(n) + count = 0 + + while n > 1 + n /= 2 + count += 1 + end + + count +end + +### Logarithmic time (recursive) ### +def log_recur(n) + return 0 unless n > 1 + log_recur(n / 2) + 1 +end + +### Linearithmic time ### +def linear_log_recur(n) + return 1 unless n > 1 + + count = linear_log_recur(n / 2) + linear_log_recur(n / 2) + (0...n).each { count += 1 } + + count +end + +### Factorial time (recursive) ### +def factorial_recur(n) + return 1 if n == 0 + + count = 0 + # Split from 1 into n + (0...n).each { count += factorial_recur(n - 1) } + + count +end + +### Driver Code ### +if __FILE__ == $0 + # You can modify n to run and observe the trend of the number of operations for various complexities + n = 8 + puts "Input data size n = #{n}" + + count = constant(n) + puts "Constant-time operations count = #{count}" + + count = linear(n) + puts "Linear-time operations count = #{count}" + count = array_traversal(Array.new(n, 0)) + puts "Linear-time (array traversal) operations count = #{count}" + + count = quadratic(n) + puts "Quadratic-time operations count = #{count}" + nums = Array.new(n) { |i| n - i } # [n, n-1, ..., 2, 1] + count = bubble_sort(nums) + puts "Quadratic-time (bubble sort) operations count = #{count}" + + count = exponential(n) + puts "Exponential-time (iterative) operations count = #{count}" + count = exp_recur(n) + puts "Exponential-time (recursive) operations count = #{count}" + + count = logarithmic(n) + puts "Logarithmic-time (iterative) operations count = #{count}" + count = log_recur(n) + puts "Logarithmic-time (recursive) operations count = #{count}" + + count = linear_log_recur(n) + puts "Linearithmic-time (recursive) operations count = #{count}" + + count = factorial_recur(n) + puts "Factorial-time (recursive) operations count = #{count}" +end diff --git a/en/codes/ruby/chapter_computational_complexity/worst_best_time_complexity.rb b/en/codes/ruby/chapter_computational_complexity/worst_best_time_complexity.rb new file mode 100644 index 000000000..f5d1f06cc --- /dev/null +++ b/en/codes/ruby/chapter_computational_complexity/worst_best_time_complexity.rb @@ -0,0 +1,35 @@ +=begin +File: worst_best_time_complexity.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Generate array with elements: 1, 2, ..., n, shuffled ### +def random_numbers(n) + # Generate array nums =: 1, 2, 3, ..., n + nums = Array.new(n) { |i| i + 1 } + # Randomly shuffle array elements + nums.shuffle! +end + +### Find index of number 1 in array nums ### +def find_one(nums) + for i in 0...nums.length + # When element 1 is at the head of the array, best time complexity O(1) is achieved + # When element 1 is at the tail of the array, worst time complexity O(n) is achieved + return i if nums[i] == 1 + end + + -1 +end + +### Driver Code ### +if __FILE__ == $0 + for i in 0...10 + n = 100 + nums = random_numbers(n) + index = find_one(nums) + puts "\nArray [ 1, 2, ..., n ] after shuffling = #{nums}" + puts "Index of number 1 is #{index}" + end +end diff --git a/en/codes/ruby/chapter_divide_and_conquer/binary_search_recur.rb b/en/codes/ruby/chapter_divide_and_conquer/binary_search_recur.rb new file mode 100644 index 000000000..398a752dc --- /dev/null +++ b/en/codes/ruby/chapter_divide_and_conquer/binary_search_recur.rb @@ -0,0 +1,42 @@ +=begin +File: binary_search_recur.rb +Created Time: 2024-05-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Binary search: problem f(i, j) ### +def dfs(nums, target, i, j) + # If the interval is empty, it means there is no target element, return -1 + return -1 if i > j + + # Calculate the midpoint index m + m = (i + j) / 2 + + if nums[m] < target + # Recursion subproblem f(m+1, j) + return dfs(nums, target, m + 1, j) + elsif nums[m] > target + # Recursion subproblem f(i, m-1) + return dfs(nums, target, i, m - 1) + else + # Found the target element, return its index + return m + end +end + +### Binary search ### +def binary_search(nums, target) + n = nums.length + # Solve the problem f(0, n-1) + dfs(nums, target, 0, n - 1) +end + +### Driver Code ### +if __FILE__ == $0 + target = 6 + nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] + + # Binary search (closed interval on both sides) + index = binary_search(nums, target) + puts "Index of target element 6 is #{index}" +end diff --git a/en/codes/ruby/chapter_divide_and_conquer/build_tree.rb b/en/codes/ruby/chapter_divide_and_conquer/build_tree.rb new file mode 100644 index 000000000..007a160da --- /dev/null +++ b/en/codes/ruby/chapter_divide_and_conquer/build_tree.rb @@ -0,0 +1,46 @@ +=begin +File: build_tree.rb +Created Time: 2024-05-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Build binary tree: divide and conquer ### +def dfs(preorder, inorder_map, i, l, r) + # Terminate when the subtree interval is empty + return if r - l < 0 + + # Initialize the root node + root = TreeNode.new(preorder[i]) + # Query m to divide the left and right subtrees + m = inorder_map[preorder[i]] + # Subproblem: build the left subtree + root.left = dfs(preorder, inorder_map, i + 1, l, m - 1) + # Subproblem: build the right subtree + root.right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r) + + # Return the root node + root +end + +### Build binary tree ### +def build_tree(preorder, inorder) + # Initialize hash map, storing the mapping from inorder elements to indices + inorder_map = {} + inorder.each_with_index { |val, i| inorder_map[val] = i } + dfs(preorder, inorder_map, 0, 0, inorder.length - 1) +end + +### Driver Code ### +if __FILE__ == $0 + preorder = [3, 9, 2, 1, 7] + inorder = [9, 3, 1, 2, 7] + puts "Pre-order traversal = #{preorder}" + puts "In-order traversal = #{inorder}" + + root = build_tree(preorder, inorder) + puts "The constructed binary tree is:" + print_tree(root) +end diff --git a/en/codes/ruby/chapter_divide_and_conquer/hanota.rb b/en/codes/ruby/chapter_divide_and_conquer/hanota.rb new file mode 100644 index 000000000..7fae969b1 --- /dev/null +++ b/en/codes/ruby/chapter_divide_and_conquer/hanota.rb @@ -0,0 +1,55 @@ +=begin +File: hanota.rb +Created Time: 2024-05-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Move one disk ### +def move(src, tar) + # Take out a disk from the top of src + pan = src.pop + # Place the disk on top of tar + tar << pan +end + +### Solve Tower of Hanoi f(i) ### +def dfs(i, src, buf, tar) + # If there is only one disk left in src, move it directly to tar + if i == 1 + move(src, tar) + return + end + + # Subproblem f(i-1): move the top i-1 disks from src to buf using tar + dfs(i - 1, src, tar, buf) + # Subproblem f(1): move the remaining disk from src to tar + move(src, tar) + # Subproblem f(i-1): move the top i-1 disks from buf to tar using src + dfs(i - 1, buf, src, tar) +end + +### Solve Tower of Hanoi ### +def solve_hanota(_A, _B, _C) + n = _A.length + # Move the top n disks from A to C using B + dfs(n, _A, _B, _C) +end + +### Driver Code ### +if __FILE__ == $0 + # The tail of the list is the top of the rod + A = [5, 4, 3, 2, 1] + B = [] + C = [] + puts "In initial state:" + puts "A = #{A}" + puts "B = #{B}" + puts "C = #{C}" + + solve_hanota(A, B, C) + + puts "After disk movement is complete:" + puts "A = #{A}" + puts "B = #{B}" + puts "C = #{C}" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_backtrack.rb b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_backtrack.rb new file mode 100644 index 000000000..9f9956204 --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_backtrack.rb @@ -0,0 +1,37 @@ +=begin +File: climbing_stairs_backtrack.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Backtracking ### +def backtrack(choices, state, n, res) + # When climbing to the n-th stair, add 1 to the solution count + res[0] += 1 if state == n + # Traverse all choices + for choice in choices + # Pruning: not allowed to go beyond the n-th stair + next if state + choice > n + + # Attempt: make choice, update state + backtrack(choices, state + choice, n, res) + end + # Backtrack +end + +### Climbing stairs: backtracking ### +def climbing_stairs_backtrack(n) + choices = [1, 2] # Can choose to climb up 1 or 2 stairs + state = 0 # Start climbing from the 0-th stair + res = [0] # Use res[0] to record the solution count + backtrack(choices, state, n, res) + res.first +end + +### Driver Code ### +if __FILE__ == $0 + n = 9 + + res = climbing_stairs_backtrack(n) + puts "Climbing #{n} stairs has #{res} solutions" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_constraint_dp.rb b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_constraint_dp.rb new file mode 100644 index 000000000..984751db3 --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_constraint_dp.rb @@ -0,0 +1,31 @@ +=begin +File: climbing_stairs_constraint_dp.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Climbing stairs with constraint: DP ### +def climbing_stairs_constraint_dp(n) + return 1 if n == 1 || n == 2 + + # Initialize dp table, used to store solutions to subproblems + dp = Array.new(n + 1) { Array.new(3, 0) } + # Initial state: preset the solution to the smallest subproblem + dp[1][1], dp[1][2] = 1, 0 + dp[2][1], dp[2][2] = 0, 1 + # State transition: gradually solve larger subproblems from smaller ones + for i in 3...(n + 1) + dp[i][1] = dp[i - 1][2] + dp[i][2] = dp[i - 2][1] + dp[i - 2][2] + end + + dp[n][1] + dp[n][2] +end + +### Driver Code ### +if __FILE__ == $0 + n = 9 + + res = climbing_stairs_constraint_dp(n) + puts "Climbing #{n} stairs has #{res} solutions" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs.rb b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs.rb new file mode 100644 index 000000000..e2ea8b03e --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs.rb @@ -0,0 +1,26 @@ +=begin +File: climbing_stairs_dfs.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Search ### +def dfs(i) + # Known dp[1] and dp[2], return them + return i if i == 1 || i == 2 + # dp[i] = dp[i-1] + dp[i-2] + dfs(i - 1) + dfs(i - 2) +end + +### Climbing stairs: search ### +def climbing_stairs_dfs(n) + dfs(n) +end + +### Driver Code ### +if __FILE__ == $0 + n = 9 + + res = climbing_stairs_dfs(n) + puts "Climbing #{n} stairs has #{res} solutions" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs_mem.rb b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs_mem.rb new file mode 100644 index 000000000..aa9c0fc3f --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs_mem.rb @@ -0,0 +1,33 @@ +=begin +File: climbing_stairs_dfs_mem.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Memoization search ### +def dfs(i, mem) + # Known dp[1] and dp[2], return them + return i if i == 1 || i == 2 + # If record dp[i] exists, return it directly + return mem[i] if mem[i] != -1 + + # dp[i] = dp[i-1] + dp[i-2] + count = dfs(i - 1, mem) + dfs(i - 2, mem) + # Record dp[i] + mem[i] = count +end + +### Climbing stairs: memoization search ### +def climbing_stairs_dfs_mem(n) + # mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + mem = Array.new(n + 1, -1) + dfs(n, mem) +end + +### Driver Code ### +if __FILE__ == $0 + n = 9 + + res = climbing_stairs_dfs_mem(n) + puts "Climbing #{n} stairs has #{res} solutions" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dp.rb b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dp.rb new file mode 100644 index 000000000..e759d4135 --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/climbing_stairs_dp.rb @@ -0,0 +1,40 @@ +=begin +File: climbing_stairs_dp.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Climbing stairs: dynamic programming ### +def climbing_stairs_dp(n) + return n if n == 1 || n == 2 + + # Initialize dp table, used to store solutions to subproblems + dp = Array.new(n + 1, 0) + # Initial state: preset the solution to the smallest subproblem + dp[1], dp[2] = 1, 2 + # State transition: gradually solve larger subproblems from smaller ones + (3...(n + 1)).each { |i| dp[i] = dp[i - 1] + dp[i - 2] } + + dp[n] +end + +### Climbing stairs: space-optimized DP ### +def climbing_stairs_dp_comp(n) + return n if n == 1 || n == 2 + + a, b = 1, 2 + (3...(n + 1)).each { a, b = b, a + b } + + b +end + +### Driver Code ### +if __FILE__ == $0 + n = 9 + + res = climbing_stairs_dp(n) + puts "Climbing #{n} stairs has #{res} solutions" + + res = climbing_stairs_dp_comp(n) + puts "Climbing #{n} stairs has #{res} solutions" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/coin_change.rb b/en/codes/ruby/chapter_dynamic_programming/coin_change.rb new file mode 100644 index 000000000..782a17441 --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/coin_change.rb @@ -0,0 +1,65 @@ +=begin +File: coin_change.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Coin change: dynamic programming ### +def coin_change_dp(coins, amt) + n = coins.length + _MAX = amt + 1 + # Initialize dp table + dp = Array.new(n + 1) { Array.new(amt + 1, 0) } + # State transition: first row and first column + (1...(amt + 1)).each { |a| dp[0][a] = _MAX } + # State transition: rest of the rows and columns + for i in 1...(n + 1) + for a in 1...(amt + 1) + if coins[i - 1] > a + # If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a] + else + # The smaller value between not selecting and selecting coin i + dp[i][a] = [dp[i - 1][a], dp[i][a - coins[i - 1]] + 1].min + end + end + end + dp[n][amt] != _MAX ? dp[n][amt] : -1 +end + +### Coin change: space-optimized DP ### +def coin_change_dp_comp(coins, amt) + n = coins.length + _MAX = amt + 1 + # Initialize dp table + dp = Array.new(amt + 1, _MAX) + dp[0] = 0 + # State transition + for i in 1...(n + 1) + # Traverse in forward order + for a in 1...(amt + 1) + if coins[i - 1] > a + # If exceeds target amount, don't select coin i + dp[a] = dp[a] + else + # The smaller value between not selecting and selecting coin i + dp[a] = [dp[a], dp[a - coins[i - 1]] + 1].min + end + end + end + dp[amt] != _MAX ? dp[amt] : -1 +end + +### Driver Code ### +if __FILE__ == $0 + coins = [1, 2, 5] + amt = 4 + + # Dynamic programming + res = coin_change_dp(coins, amt) + puts "Minimum coins needed to make target amount is #{res}" + + # Space-optimized dynamic programming + res = coin_change_dp_comp(coins, amt) + puts "Minimum coins needed to make target amount is #{res}" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/coin_change_ii.rb b/en/codes/ruby/chapter_dynamic_programming/coin_change_ii.rb new file mode 100644 index 000000000..77ba8d3b1 --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/coin_change_ii.rb @@ -0,0 +1,63 @@ +=begin +File: coin_change_ii.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Coin change II: dynamic programming ### +def coin_change_ii_dp(coins, amt) + n = coins.length + # Initialize dp table + dp = Array.new(n + 1) { Array.new(amt + 1, 0) } + # Initialize first column + (0...(n + 1)).each { |i| dp[i][0] = 1 } + # State transition + for i in 1...(n + 1) + for a in 1...(amt + 1) + if coins[i - 1] > a + # If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a] + else + # Sum of the two options: not selecting and selecting coin i + dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]] + end + end + end + dp[n][amt] +end + +### Coin change II: space-optimized DP ### +def coin_change_ii_dp_comp(coins, amt) + n = coins.length + # Initialize dp table + dp = Array.new(amt + 1, 0) + dp[0] = 1 + # State transition + for i in 1...(n + 1) + # Traverse in forward order + for a in 1...(amt + 1) + if coins[i - 1] > a + # If exceeds target amount, don't select coin i + dp[a] = dp[a] + else + # Sum of the two options: not selecting and selecting coin i + dp[a] = dp[a] + dp[a - coins[i - 1]] + end + end + end + dp[amt] +end + +### Driver Code ### +if __FILE__ == $0 + coins = [1, 2, 5] + amt = 5 + + # Dynamic programming + res = coin_change_ii_dp(coins, amt) + puts "Number of coin combinations to make target amount is #{res}" + + # Space-optimized dynamic programming + res = coin_change_ii_dp_comp(coins, amt) + puts "Number of coin combinations to make target amount is #{res}" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/edit_distance.rb b/en/codes/ruby/chapter_dynamic_programming/edit_distance.rb new file mode 100644 index 000000000..e216c0f8f --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/edit_distance.rb @@ -0,0 +1,115 @@ +=begin +File: edit_distance.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Edit distance: brute force search ### +def edit_distance_dfs(s, t, i, j) + # If both s and t are empty, return 0 + return 0 if i == 0 && j == 0 + # If s is empty, return length of t + return j if i == 0 + # If t is empty, return length of s + return i if j == 0 + # If two characters are equal, skip both characters + return edit_distance_dfs(s, t, i - 1, j - 1) if s[i - 1] == t[j - 1] + # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + insert = edit_distance_dfs(s, t, i, j - 1) + delete = edit_distance_dfs(s, t, i - 1, j) + replace = edit_distance_dfs(s, t, i - 1, j - 1) + # Return minimum edit steps + [insert, delete, replace].min + 1 +end + +def edit_distance_dfs_mem(s, t, mem, i, j) + # If both s and t are empty, return 0 + return 0 if i == 0 && j == 0 + # If s is empty, return length of t + return j if i == 0 + # If t is empty, return length of s + return i if j == 0 + # If there's a record, return it directly + return mem[i][j] if mem[i][j] != -1 + # If two characters are equal, skip both characters + return edit_distance_dfs_mem(s, t, mem, i - 1, j - 1) if s[i - 1] == t[j - 1] + # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + insert = edit_distance_dfs_mem(s, t, mem, i, j - 1) + delete = edit_distance_dfs_mem(s, t, mem, i - 1, j) + replace = edit_distance_dfs_mem(s, t, mem, i - 1, j - 1) + # Record and return minimum edit steps + mem[i][j] = [insert, delete, replace].min + 1 +end + +### Edit distance: dynamic programming ### +def edit_distance_dp(s, t) + n, m = s.length, t.length + dp = Array.new(n + 1) { Array.new(m + 1, 0) } + # State transition: first row and first column + (1...(n + 1)).each { |i| dp[i][0] = i } + (1...(m + 1)).each { |j| dp[0][j] = j } + # State transition: rest of the rows and columns + for i in 1...(n + 1) + for j in 1...(m +1) + if s[i - 1] == t[j - 1] + # If two characters are equal, skip both characters + dp[i][j] = dp[i - 1][j - 1] + else + # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[i][j] = [dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]].min + 1 + end + end + end + dp[n][m] +end + +### Edit distance: space-optimized DP ### +def edit_distance_dp_comp(s, t) + n, m = s.length, t.length + dp = Array.new(m + 1, 0) + # State transition: first row + (1...(m + 1)).each { |j| dp[j] = j } + # State transition: rest of the rows + for i in 1...(n + 1) + # State transition: first column + leftup = dp.first # Temporarily store dp[i-1, j-1] + dp[0] += 1 + # State transition: rest of the columns + for j in 1...(m + 1) + temp = dp[j] + if s[i - 1] == t[j - 1] + # If two characters are equal, skip both characters + dp[j] = leftup + else + # Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[j] = [dp[j - 1], dp[j], leftup].min + 1 + end + leftup = temp # Update for next round's dp[i-1, j-1] + end + end + dp[m] +end + +### Driver Code ### +if __FILE__ == $0 + s = 'bag' + t = 'pack' + n, m = s.length, t.length + + # Brute-force search + res = edit_distance_dfs(s, t, n, m) + puts "Changing #{s} to #{t} requires minimum #{res} edits" + + # Memoization search + mem = Array.new(n + 1) { Array.new(m + 1, -1) } + res = edit_distance_dfs_mem(s, t, mem, n, m) + puts "Changing #{s} to #{t} requires minimum #{res} edits" + + # Dynamic programming + res = edit_distance_dp(s, t) + puts "Changing #{s} to #{t} requires minimum #{res} edits" + + # Space-optimized dynamic programming + res = edit_distance_dp_comp(s, t) + puts "Changing #{s} to #{t} requires minimum #{res} edits" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/knapsack.rb b/en/codes/ruby/chapter_dynamic_programming/knapsack.rb new file mode 100644 index 000000000..949d41d28 --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/knapsack.rb @@ -0,0 +1,99 @@ +=begin +File: knapsack.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 0-1 knapsack: brute force search ### +def knapsack_dfs(wgt, val, i, c) + # If all items have been selected or knapsack has no remaining capacity, return value 0 + return 0 if i == 0 || c == 0 + # If exceeds knapsack capacity, can only choose not to put it in + return knapsack_dfs(wgt, val, i - 1, c) if wgt[i - 1] > c + # Calculate the maximum value of not putting in and putting in item i + no = knapsack_dfs(wgt, val, i - 1, c) + yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1] + # Return the larger value of the two options + [no, yes].max +end + +### 0-1 knapsack: memoization search ### +def knapsack_dfs_mem(wgt, val, mem, i, c) + # If all items have been selected or knapsack has no remaining capacity, return value 0 + return 0 if i == 0 || c == 0 + # If there's a record, return it directly + return mem[i][c] if mem[i][c] != -1 + # If exceeds knapsack capacity, can only choose not to put it in + return knapsack_dfs_mem(wgt, val, mem, i - 1, c) if wgt[i - 1] > c + # Calculate the maximum value of not putting in and putting in item i + no = knapsack_dfs_mem(wgt, val, mem, i - 1, c) + yes = knapsack_dfs_mem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1] + # Record and return the larger value of the two options + mem[i][c] = [no, yes].max +end + +### 0-1 knapsack: dynamic programming ### +def knapsack_dp(wgt, val, cap) + n = wgt.length + # Initialize dp table + dp = Array.new(n + 1) { Array.new(cap + 1, 0) } + # State transition + for i in 1...(n + 1) + for c in 1...(cap + 1) + if wgt[i - 1] > c + # If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c] + else + # The larger value between not selecting and selecting item i + dp[i][c] = [dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]].max + end + end + end + dp[n][cap] +end + +### 0-1 knapsack: space-optimized DP ### +def knapsack_dp_comp(wgt, val, cap) + n = wgt.length + # Initialize dp table + dp = Array.new(cap + 1, 0) + # State transition + for i in 1...(n + 1) + # Traverse in reverse order + for c in cap.downto(1) + if wgt[i - 1] > c + # If exceeds knapsack capacity, don't select item i + dp[c] = dp[c] + else + # The larger value between not selecting and selecting item i + dp[c] = [dp[c], dp[c - wgt[i - 1]] + val[i - 1]].max + end + end + end + dp[cap] +end + +### Driver Code ### +if __FILE__ == $0 + wgt = [10, 20, 30, 40, 50] + val = [50, 120, 150, 210, 240] + cap = 50 + n = wgt.length + + # Brute-force search + res = knapsack_dfs(wgt, val, n, cap) + puts "Maximum item value not exceeding knapsack capacity is #{res}" + + # Memoization search + mem = Array.new(n + 1) { Array.new(cap + 1, -1) } + res = knapsack_dfs_mem(wgt, val, mem, n, cap) + puts "Maximum item value not exceeding knapsack capacity is #{res}" + + # Dynamic programming + res = knapsack_dp(wgt, val, cap) + puts "Maximum item value not exceeding knapsack capacity is #{res}" + + # Space-optimized dynamic programming + res = knapsack_dp_comp(wgt, val, cap) + puts "Maximum item value not exceeding knapsack capacity is #{res}" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rb b/en/codes/ruby/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rb new file mode 100644 index 000000000..fe0fd6c12 --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rb @@ -0,0 +1,39 @@ +=begin +File: min_cost_climbing_stairs_dp.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Minimum cost climbing stairs: DP ### +def min_cost_climbing_stairs_dp(cost) + n = cost.length - 1 + return cost[n] if n == 1 || n == 2 + # Initialize dp table, used to store solutions to subproblems + dp = Array.new(n + 1, 0) + # Initial state: preset the solution to the smallest subproblem + dp[1], dp[2] = cost[1], cost[2] + # State transition: gradually solve larger subproblems from smaller ones + (3...(n + 1)).each { |i| dp[i] = [dp[i - 1], dp[i - 2]].min + cost[i] } + dp[n] +end + +# Minimum cost climbing stairs: Space-optimized dynamic programming +def min_cost_climbing_stairs_dp_comp(cost) + n = cost.length - 1 + return cost[n] if n == 1 || n == 2 + a, b = cost[1], cost[2] + (3...(n + 1)).each { |i| a, b = b, [a, b].min + cost[i] } + b +end + +### Driver Code ### +if __FILE__ == $0 + cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1] + puts "Input stair cost list is #{cost}" + + res = min_cost_climbing_stairs_dp(cost) + puts "Minimum cost to climb stairs is #{res}" + + res = min_cost_climbing_stairs_dp_comp(cost) + puts "Minimum cost to climb stairs is #{res}" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/min_path_sum.rb b/en/codes/ruby/chapter_dynamic_programming/min_path_sum.rb new file mode 100644 index 000000000..e256795e0 --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/min_path_sum.rb @@ -0,0 +1,93 @@ +=begin +File: min_path_sum.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Minimum path sum: brute force search ### +def min_path_sum_dfs(grid, i, j) + # If it's the top-left cell, terminate the search + return grid[i][j] if i == 0 && j == 0 + # If row or column index is out of bounds, return +∞ cost + return Float::INFINITY if i < 0 || j < 0 + # Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + up = min_path_sum_dfs(grid, i - 1, j) + left = min_path_sum_dfs(grid, i, j - 1) + # Return the minimum path cost from top-left to (i, j) + [left, up].min + grid[i][j] +end + +### Minimum path sum: memoization search ### +def min_path_sum_dfs_mem(grid, mem, i, j) + # If it's the top-left cell, terminate the search + return grid[0][0] if i == 0 && j == 0 + # If row or column index is out of bounds, return +∞ cost + return Float::INFINITY if i < 0 || j < 0 + # If there's a record, return it directly + return mem[i][j] if mem[i][j] != -1 + # Minimum path cost for left and upper cells + up = min_path_sum_dfs_mem(grid, mem, i - 1, j) + left = min_path_sum_dfs_mem(grid, mem, i, j - 1) + # Record and return the minimum path cost from top-left to (i, j) + mem[i][j] = [left, up].min + grid[i][j] +end + +### Minimum path sum: dynamic programming ### +def min_path_sum_dp(grid) + n, m = grid.length, grid.first.length + # Initialize dp table + dp = Array.new(n) { Array.new(m, 0) } + dp[0][0] = grid[0][0] + # State transition: first row + (1...m).each { |j| dp[0][j] = dp[0][j - 1] + grid[0][j] } + # State transition: first column + (1...n).each { |i| dp[i][0] = dp[i - 1][0] + grid[i][0] } + # State transition: rest of the rows and columns + for i in 1...n + for j in 1...m + dp[i][j] = [dp[i][j - 1], dp[i - 1][j]].min + grid[i][j] + end + end + dp[n -1][m -1] +end + +### Minimum path sum: space-optimized DP ### +def min_path_sum_dp_comp(grid) + n, m = grid.length, grid.first.length + # Initialize dp table + dp = Array.new(m, 0) + # State transition: first row + dp[0] = grid[0][0] + (1...m).each { |j| dp[j] = dp[j - 1] + grid[0][j] } + # State transition: rest of the rows + for i in 1...n + # State transition: first column + dp[0] = dp[0] + grid[i][0] + # State transition: rest of the columns + (1...m).each { |j| dp[j] = [dp[j - 1], dp[j]].min + grid[i][j] } + end + dp[m - 1] +end + +### Driver Code ### +if __FILE__ == $0 + grid = [[1, 3, 1, 5], [2, 2, 4, 2], [5, 3, 2, 1], [4, 3, 5, 2]] + n, m = grid.length, grid.first.length + + # Brute-force search + res = min_path_sum_dfs(grid, n - 1, m - 1) + puts "Minimum path sum from top-left to bottom-right is #{res}" + + # Memoization search + mem = Array.new(n) { Array.new(m, - 1) } + res = min_path_sum_dfs_mem(grid, mem, n - 1, m -1) + puts "Minimum path sum from top-left to bottom-right is #{res}" + + # Dynamic programming + res = min_path_sum_dp(grid) + puts "Minimum path sum from top-left to bottom-right is #{res}" + + # Space-optimized dynamic programming + res = min_path_sum_dp_comp(grid) + puts "Minimum path sum from top-left to bottom-right is #{res}" +end diff --git a/en/codes/ruby/chapter_dynamic_programming/unbounded_knapsack.rb b/en/codes/ruby/chapter_dynamic_programming/unbounded_knapsack.rb new file mode 100644 index 000000000..af9d77afc --- /dev/null +++ b/en/codes/ruby/chapter_dynamic_programming/unbounded_knapsack.rb @@ -0,0 +1,61 @@ +=begin +File: unbounded_knapsack.rb +Created Time: 2024-05-29 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Unbounded knapsack: dynamic programming ### +def unbounded_knapsack_dp(wgt, val, cap) + n = wgt.length + # Initialize dp table + dp = Array.new(n + 1) { Array.new(cap + 1, 0) } + # State transition + for i in 1...(n + 1) + for c in 1...(cap + 1) + if wgt[i - 1] > c + # If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c] + else + # The larger value between not selecting and selecting item i + dp[i][c] = [dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]].max + end + end + end + dp[n][cap] +end + +### Unbounded knapsack: space-optimized DP ### +def unbounded_knapsack_dp_comp(wgt, val, cap) + n = wgt.length + # Initialize dp table + dp = Array.new(cap + 1, 0) + # State transition + for i in 1...(n + 1) + # Traverse in forward order + for c in 1...(cap + 1) + if wgt[i -1] > c + # If exceeds knapsack capacity, don't select item i + dp[c] = dp[c] + else + # The larger value between not selecting and selecting item i + dp[c] = [dp[c], dp[c - wgt[i - 1]] + val[i - 1]].max + end + end + end + dp[cap] +end + +### Driver Code ### +if __FILE__ == $0 + wgt = [1, 2, 3] + val = [5, 11, 15] + cap = 4 + + # Dynamic programming + res = unbounded_knapsack_dp(wgt, val, cap) + puts "Maximum item value not exceeding knapsack capacity is #{res}" + + # Space-optimized dynamic programming + res = unbounded_knapsack_dp_comp(wgt, val, cap) + puts "Maximum item value not exceeding knapsack capacity is #{res}" +end diff --git a/en/codes/ruby/chapter_graph/graph_adjacency_list.rb b/en/codes/ruby/chapter_graph/graph_adjacency_list.rb new file mode 100644 index 000000000..1907942fe --- /dev/null +++ b/en/codes/ruby/chapter_graph/graph_adjacency_list.rb @@ -0,0 +1,116 @@ +=begin +File: graph_adjacency_list.rb +Created Time: 2024-04-25 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/vertex' + +### Undirected graph class based on adjacency list ### +class GraphAdjList + attr_reader :adj_list + + ### Constructor ### + def initialize(edges) + # Adjacency list, key: vertex, value: all adjacent vertices of that vertex + @adj_list = {} + # Add all vertices and edges + for edge in edges + add_vertex(edge[0]) + add_vertex(edge[1]) + add_edge(edge[0], edge[1]) + end + end + + ### Get number of vertices ### + def size + @adj_list.length + end + + ### Add edge ### + def add_edge(vet1, vet2) + raise ArgumentError if !@adj_list.include?(vet1) || !@adj_list.include?(vet2) + + @adj_list[vet1] << vet2 + @adj_list[vet2] << vet1 + end + + ### Delete edge ### + def remove_edge(vet1, vet2) + raise ArgumentError if !@adj_list.include?(vet1) || !@adj_list.include?(vet2) + + # Remove edge vet1 - vet2 + @adj_list[vet1].delete(vet2) + @adj_list[vet2].delete(vet1) + end + + ### Add vertex ### + def add_vertex(vet) + return if @adj_list.include?(vet) + + # Add a new linked list in the adjacency list + @adj_list[vet] = [] + end + + ### Delete vertex ### + def remove_vertex(vet) + raise ArgumentError unless @adj_list.include?(vet) + + # Remove the linked list corresponding to vertex vet in the adjacency list + @adj_list.delete(vet) + # Traverse the linked lists of other vertices and remove all edges containing vet + for vertex in @adj_list + @adj_list[vertex.first].delete(vet) if @adj_list[vertex.first].include?(vet) + end + end + + ### Print adjacency list ### + def __print__ + puts 'Adjacency list =' + for vertex in @adj_list + tmp = @adj_list[vertex.first].map { |v| v.val } + puts "#{vertex.first.val}: #{tmp}," + end + end +end + +### Driver Code ### +if __FILE__ == $0 + # Add edge + v = vals_to_vets([1, 3, 2, 5, 4]) + edges = [ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[2], v[3]], + [v[2], v[4]], + [v[3], v[4]], + ] + graph = GraphAdjList.new(edges) + puts "\nAfter initialization, graph is" + graph.__print__ + + # Add edge + # Vertices 1, 2 are v[0], v[2] + graph.add_edge(v[0], v[2]) + puts "\nAfter adding edge 1-2, graph is" + graph.__print__ + + # Remove edge + # Vertices 1, 3 are v[0], v[1] + graph.remove_edge(v[0], v[1]) + puts "\nAfter removing edge 1-3, graph is" + graph.__print__ + + # Add vertex + v5 = Vertex.new(6) + graph.add_vertex(v5) + puts "\nAfter adding vertex 6, graph is" + graph.__print__ + + # Remove vertex + # Vertex 3 is v[1] + graph.remove_vertex(v[1]) + puts "\nAfter removing vertex 3, graph is" + graph.__print__ +end diff --git a/en/codes/ruby/chapter_graph/graph_adjacency_matrix.rb b/en/codes/ruby/chapter_graph/graph_adjacency_matrix.rb new file mode 100644 index 000000000..56c513680 --- /dev/null +++ b/en/codes/ruby/chapter_graph/graph_adjacency_matrix.rb @@ -0,0 +1,116 @@ +=begin +File: graph_adjacency_matrix.rb +Created Time: 2024-04-25 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/print_util' + +### Undirected graph class based on adjacency matrix ### +class GraphAdjMat + def initialize(vertices, edges) + ### Constructor ### + # Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + @vertices = [] + # Adjacency matrix, where the row and column indices correspond to the "vertex index" + @adj_mat = [] + # Add vertex + vertices.each { |val| add_vertex(val) } + # Add edge + # Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + edges.each { |e| add_edge(e[0], e[1]) } + end + + ### Get number of vertices ### + def size + @vertices.length + end + + ### Add vertex ### + def add_vertex(val) + n = size + # Add the value of the new vertex to the vertex list + @vertices << val + # Add a row to the adjacency matrix + new_row = Array.new(n, 0) + @adj_mat << new_row + # Add a column to the adjacency matrix + @adj_mat.each { |row| row << 0 } + end + + ### Delete vertex ### + def remove_vertex(index) + raise IndexError if index >= size + + # Remove the vertex at index from the vertex list + @vertices.delete_at(index) + # Remove the row at index from the adjacency matrix + @adj_mat.delete_at(index) + # Remove the column at index from the adjacency matrix + @adj_mat.each { |row| row.delete_at(index) } + end + + ### Add edge ### + def add_edge(i, j) + # Parameters i, j correspond to the vertices element indices + # Handle index out of bounds and equality + if i < 0 || j < 0 || i >= size || j >= size || i == j + raise IndexError + end + # In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + @adj_mat[i][j] = 1 + @adj_mat[j][i] = 1 + end + + ### Delete edge ### + def remove_edge(i, j) + # Parameters i, j correspond to the vertices element indices + # Handle index out of bounds and equality + if i < 0 || j < 0 || i >= size || j >= size || i == j + raise IndexError + end + @adj_mat[i][j] = 0 + @adj_mat[j][i] = 0 + end + + ### Print adjacency matrix ### + def __print__ + puts "Vertex list = #{@vertices}" + puts 'Adjacency matrix =' + print_matrix(@adj_mat) + end +end + +### Driver Code ### +if __FILE__ == $0 + # Add edge + # Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + vertices = [1, 3, 2, 5, 4] + edges = [[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]] + graph = GraphAdjMat.new(vertices, edges) + puts "\nAfter initialization, graph is" + graph.__print__ + + # Add edge + # Add vertex + graph.add_edge(0, 2) + puts "\nAfter adding edge 1-2, graph is" + graph.__print__ + + # Remove edge + # Vertices 1, 3 have indices 0, 1 respectively + graph.remove_edge(0, 1) + puts "\nAfter removing edge 1-3, graph is" + graph.__print__ + + # Add vertex + graph.add_vertex(6) + puts "\nAfter adding vertex 6, graph is" + graph.__print__ + + # Remove vertex + # Vertex 3 has index 1 + graph.remove_vertex(1) + puts "\nAfter removing vertex 3, graph is" + graph.__print__ +end diff --git a/en/codes/ruby/chapter_graph/graph_bfs.rb b/en/codes/ruby/chapter_graph/graph_bfs.rb new file mode 100644 index 000000000..39efa879d --- /dev/null +++ b/en/codes/ruby/chapter_graph/graph_bfs.rb @@ -0,0 +1,61 @@ +=begin +File: graph_bfs.rb +Created Time: 2024-04-25 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require 'set' +require_relative './graph_adjacency_list' +require_relative '../utils/vertex' + +### Breadth-first traversal ### +def graph_bfs(graph, start_vet) + # Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + # Vertex traversal sequence + res = [] + # Hash set for recording vertices that have been visited + visited = Set.new([start_vet]) + # Queue used to implement BFS + que = [start_vet] + # Starting from vertex vet, loop until all vertices are visited + while que.length > 0 + vet = que.shift # Dequeue the front vertex + res << vet # Record visited vertex + # Traverse all adjacent vertices of this vertex + for adj_vet in graph.adj_list[vet] + next if visited.include?(adj_vet) # Skip vertices that have been visited + que << adj_vet # Only enqueue unvisited vertices + visited.add(adj_vet) # Mark this vertex as visited + end + end + # Return vertex traversal sequence + res +end + +### Driver Code ### +if __FILE__ == $0 + # Add edge + v = vals_to_vets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + edges = [ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[1], v[4]], + [v[2], v[5]], + [v[3], v[4]], + [v[3], v[6]], + [v[4], v[5]], + [v[4], v[7]], + [v[5], v[8]], + [v[6], v[7]], + [v[7], v[8]], + ] + graph = GraphAdjList.new(edges) + puts "\nAfter initialization, graph is" + graph.__print__ + + # Breadth-first traversal + res = graph_bfs(graph, v.first) + puts "\nBreadth-first traversal (BFS) vertex sequence is" + p vets_to_vals(res) +end diff --git a/en/codes/ruby/chapter_graph/graph_dfs.rb b/en/codes/ruby/chapter_graph/graph_dfs.rb new file mode 100644 index 000000000..066bb70cf --- /dev/null +++ b/en/codes/ruby/chapter_graph/graph_dfs.rb @@ -0,0 +1,54 @@ +=begin +File: graph_dfs.rb +Created Time: 2024-04-25 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require 'set' +require_relative './graph_adjacency_list' +require_relative '../utils/vertex' + +### Depth-first traversal helper function ### +def dfs(graph, visited, res, vet) + res << vet # Record visited vertex + visited.add(vet) # Mark this vertex as visited + # Traverse all adjacent vertices of this vertex + for adj_vet in graph.adj_list[vet] + next if visited.include?(adj_vet) # Skip vertices that have been visited + # Recursively visit adjacent vertices + dfs(graph, visited, res, adj_vet) + end +end + +### Depth-first traversal ### +def graph_dfs(graph, start_vet) + # Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex + # Vertex traversal sequence + res = [] + # Hash set for recording vertices that have been visited + visited = Set.new + dfs(graph, visited, res, start_vet) + res +end + +### Driver Code ### +if __FILE__ == $0 + # Add edge + v = vals_to_vets([0, 1, 2, 3, 4, 5, 6]) + edges = [ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[2], v[5]], + [v[4], v[5]], + [v[5], v[6]], + ] + graph = GraphAdjList.new(edges) + puts "\nAfter initialization, graph is" + graph.__print__ + + # Depth-first traversal + res = graph_dfs(graph, v[0]) + puts "\nDepth-first traversal (DFS) vertex sequence is" + p vets_to_vals(res) +end diff --git a/en/codes/ruby/chapter_greedy/coin_change_greedy.rb b/en/codes/ruby/chapter_greedy/coin_change_greedy.rb new file mode 100644 index 000000000..6aa136324 --- /dev/null +++ b/en/codes/ruby/chapter_greedy/coin_change_greedy.rb @@ -0,0 +1,50 @@ +=begin +File: coin_change_greedy.rb +Created Time: 2024-05-07 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Coin change: greedy ### +def coin_change_greedy(coins, amt) + # Assume coins list is sorted + i = coins.length - 1 + count = 0 + # Loop to make greedy choices until no remaining amount + while amt > 0 + # Find the coin that is less than and closest to the remaining amount + while i > 0 && coins[i] > amt + i -= 1 + end + # Choose coins[i] + amt -= coins[i] + count += 1 + end + # Return -1 if no solution found + amt == 0 ? count : -1 +end + +### Driver Code ### +if __FILE__ == $0 + # Greedy algorithm: Can guarantee finding the global optimal solution + coins = [1, 5, 10, 20, 50, 100] + amt = 186 + res = coin_change_greedy(coins, amt) + puts "\ncoins = #{coins}, amt = #{amt}" + puts "Minimum coins needed to make #{amt} is #{res}" + + # Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = [1, 20, 50] + amt = 60 + res = coin_change_greedy(coins, amt) + puts "\ncoins = #{coins}, amt = #{amt}" + puts "Minimum coins needed to make #{amt} is #{res}" + puts "Actually minimum needed is 3, i.e., 20 + 20 + 20" + + # Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = [1, 49, 50] + amt = 98 + res = coin_change_greedy(coins, amt) + puts "\ncoins = #{coins}, amt = #{amt}" + puts "Minimum coins needed to make #{amt} is #{res}" + puts "Actually minimum needed is 2, i.e., 49 + 49" +end diff --git a/en/codes/ruby/chapter_greedy/fractional_knapsack.rb b/en/codes/ruby/chapter_greedy/fractional_knapsack.rb new file mode 100644 index 000000000..26cba27c3 --- /dev/null +++ b/en/codes/ruby/chapter_greedy/fractional_knapsack.rb @@ -0,0 +1,51 @@ +=begin +File: fractional_knapsack.rb +Created Time: 2024-05-07 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Item ### +class Item + attr_accessor :w # Item weight + attr_accessor :v # Item value + + def initialize(w, v) + @w = w + @v = v + end +end + +### Fractional knapsack: greedy ### +def fractional_knapsack(wgt, val, cap) + # Create item list with two attributes: weight, value + items = wgt.each_with_index.map { |w, i| Item.new(w, val[i]) } + # Sort by unit value item.v / item.w from high to low + items.sort! { |a, b| (b.v.to_f / b.w) <=> (a.v.to_f / a.w) } + # Loop for greedy selection + res = 0 + for item in items + if item.w <= cap + # If remaining capacity is sufficient, put the entire current item into the knapsack + res += item.v + cap -= item.w + else + # If remaining capacity is insufficient, put part of the current item into the knapsack + res += (item.v.to_f / item.w) * cap + # No remaining capacity, so break out of the loop + break + end + end + res +end + +### Driver Code ### +if __FILE__ == $0 + wgt = [10, 20, 30, 40, 50] + val = [50, 120, 150, 210, 240] + cap = 50 + n = wgt.length + + # Greedy algorithm + res = fractional_knapsack(wgt, val, cap) + puts "Maximum item value not exceeding knapsack capacity is #{res}" +end diff --git a/en/codes/ruby/chapter_greedy/max_capacity.rb b/en/codes/ruby/chapter_greedy/max_capacity.rb new file mode 100644 index 000000000..5002eeef6 --- /dev/null +++ b/en/codes/ruby/chapter_greedy/max_capacity.rb @@ -0,0 +1,37 @@ +=begin +File: max_capacity.rb +Created Time: 2024-05-07 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Maximum capacity: greedy ### +def max_capacity(ht) + # Initialize i, j to be at both ends of the array + i, j = 0, ht.length - 1 + # Initial max capacity is 0 + res = 0 + + # Loop for greedy selection until the two boards meet + while i < j + # Update max capacity + cap = [ht[i], ht[j]].min * (j - i) + res = [res, cap].max + # Move the shorter board inward + if ht[i] < ht[j] + i += 1 + else + j -= 1 + end + end + + res +end + +### Driver Code ### +if __FILE__ == $0 + ht = [3, 8, 5, 2, 7, 7, 3, 4] + + # Greedy algorithm + res = max_capacity(ht) + puts "Maximum capacity is #{res}" +end diff --git a/en/codes/ruby/chapter_greedy/max_product_cutting.rb b/en/codes/ruby/chapter_greedy/max_product_cutting.rb new file mode 100644 index 000000000..bf5d752cd --- /dev/null +++ b/en/codes/ruby/chapter_greedy/max_product_cutting.rb @@ -0,0 +1,28 @@ +=begin +File: max_product_cutting.rb +Created Time: 2024-05-07 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Maximum cutting product: greedy ### +def max_product_cutting(n) + # When n <= 3, must cut out a 1 + return 1 * (n - 1) if n <= 3 + # Greedily cut out 3, a is the number of 3s, b is the remainder + a, b = n / 3, n % 3 + # When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + return (3.pow(a - 1) * 2 * 2).to_i if b == 1 + # When the remainder is 2, do nothing + return (3.pow(a) * 2).to_i if b == 2 + # When the remainder is 0, do nothing + 3.pow(a).to_i +end + +### Driver Code ### +if __FILE__ == $0 + n = 58 + + # Greedy algorithm + res = max_product_cutting(n) + puts "Maximum cutting product is #{res}" +end diff --git a/en/codes/ruby/chapter_hashing/array_hash_map.rb b/en/codes/ruby/chapter_hashing/array_hash_map.rb new file mode 100644 index 000000000..5ceb92edd --- /dev/null +++ b/en/codes/ruby/chapter_hashing/array_hash_map.rb @@ -0,0 +1,121 @@ +=begin +File: array_hash_map.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Key-value pair ### +class Pair + attr_accessor :key, :val + + def initialize(key, val) + @key = key + @val = val + end +end + +### Hash map based on array ### +class ArrayHashMap + ### Constructor ### + def initialize + # Initialize array with 100 buckets + @buckets = Array.new(100) + end + + ### Hash function ### + def hash_func(key) + index = key % 100 + end + + ### Query operation ### + def get(key) + index = hash_func(key) + pair = @buckets[index] + + return if pair.nil? + pair.val + end + + ### Add operation ### + def put(key, val) + pair = Pair.new(key, val) + index = hash_func(key) + @buckets[index] = pair + end + + ### Delete operation ### + def remove(key) + index = hash_func(key) + # Set to nil to delete + @buckets[index] = nil + end + + ### Get all key-value pairs ### + def entry_set + result = [] + @buckets.each { |pair| result << pair unless pair.nil? } + result + end + + ### Get all keys ### + def key_set + result = [] + @buckets.each { |pair| result << pair.key unless pair.nil? } + result + end + + ### Get all values ### + def value_set + result = [] + @buckets.each { |pair| result << pair.val unless pair.nil? } + result + end + + ### Print hash table ### + def print + @buckets.each { |pair| puts "#{pair.key} -> #{pair.val}" unless pair.nil? } + end +end + +### Driver Code ### +if __FILE__ == $0 + # Initialize hash table + hmap = ArrayHashMap.new + + # Add operation + # Add key-value pair (key, value) to the hash table + hmap.put(12836, "Xiao Ha") + hmap.put(15937, "Xiao Luo") + hmap.put(16750, "Xiao Suan") + hmap.put(13276, "Xiao Fa") + hmap.put(10583, "Xiao Ya") + puts "\nAfter adding is complete, hash table is\nKey -> Value" + hmap.print + + # Query operation + # Input key to hash table, get value + name = hmap.get(15937) + puts "\nInput student ID 15937, found name #{name}" + + # Remove operation + # Delete key-value pair (key, value) from hash table + hmap.remove(10583) + puts "\nAfter removing 10583, hash table is\nKey -> Value" + hmap.print + + # Traverse hash table + puts "\nTraverse key-value pairs Key->Value" + for pair in hmap.entry_set + puts "#{pair.key} -> #{pair.val}" + end + + puts "\nTraverse keys separately" + for key in hmap.key_set + puts key + end + + puts "\nTraverse values only Value" + for val in hmap.value_set + puts val + end +end diff --git a/en/codes/ruby/chapter_hashing/built_in_hash.rb b/en/codes/ruby/chapter_hashing/built_in_hash.rb new file mode 100644 index 000000000..f9d410b7b --- /dev/null +++ b/en/codes/ruby/chapter_hashing/built_in_hash.rb @@ -0,0 +1,34 @@ +=begin +File: built_in_hash.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/list_node' + +### Driver Code ### +if __FILE__ == $0 + num = 3 + hash_num = num.hash + puts "Hash value of integer #{num} is #{hash_num}" + + bol = true + hash_bol = bol.hash + puts "Hash value of boolean #{bol} is #{hash_bol}" + + dec = 3.14159 + hash_dec = dec.hash + puts "Hash value of decimal #{dec} is #{hash_dec}" + + str = "Hello Algo" + hash_str = str.hash + puts "Hash value of string #{str} is #{hash_str}" + + tup = [12836, 'Xiao Ha'] + hash_tup = tup.hash + puts "Hash value of tuple #{tup} is #{hash_tup}" + + obj = ListNode.new(0) + hash_obj = obj.hash + puts "Hash value of object #{obj} is #{hash_obj}" +end diff --git a/en/codes/ruby/chapter_hashing/hash_map.rb b/en/codes/ruby/chapter_hashing/hash_map.rb new file mode 100644 index 000000000..9c4086cf1 --- /dev/null +++ b/en/codes/ruby/chapter_hashing/hash_map.rb @@ -0,0 +1,44 @@ +=begin +File: hash_map.rb +Created Time: 2024-04-14 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/print_util' + +### Driver Code ### +if __FILE__ == $0 + # Initialize hash table + hmap = {} + + # Add operation + # Add key-value pair (key, value) to the hash table + hmap[12836] = "Xiao Ha" + hmap[15937] = "Xiao Luo" + hmap[16750] = "Xiao Suan" + hmap[13276] = "Xiao Fa" + hmap[10583] = "Xiao Ya" + puts "\nAfter adding is complete, hash table is\nKey -> Value" + print_hash_map(hmap) + + # Query operation + # Input key into hash table to get value + name = hmap[15937] + puts "\nInput student ID 15937, found name #{name}" + + # Remove operation + # Remove key-value pair (key, value) from hash table + hmap.delete(10583) + puts "\nAfter removing 10583, hash table is\nKey -> Value" + print_hash_map(hmap) + + # Traverse hash table + puts "\nTraverse key-value pairs Key->Value" + hmap.entries.each { |key, value| puts "#{key} -> #{value}" } + + puts "\nTraverse keys only Key" + hmap.keys.each { |key| puts key } + + puts "\nTraverse values only Value" + hmap.values.each { |val| puts val } +end diff --git a/en/codes/ruby/chapter_hashing/hash_map_chaining.rb b/en/codes/ruby/chapter_hashing/hash_map_chaining.rb new file mode 100644 index 000000000..f439e733a --- /dev/null +++ b/en/codes/ruby/chapter_hashing/hash_map_chaining.rb @@ -0,0 +1,128 @@ +=begin +File: hash_map_chaining.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative './array_hash_map' + +### Hash map with chaining ### +class HashMapChaining + ### Constructor ### + def initialize + @size = 0 # Number of key-value pairs + @capacity = 4 # Hash table capacity + @load_thres = 2.0 / 3.0 # Load factor threshold for triggering expansion + @extend_ratio = 2 # Expansion multiplier + @buckets = Array.new(@capacity) { [] } # Bucket array + end + + ### Hash function ### + def hash_func(key) + key % @capacity + end + + ### Load factor ### + def load_factor + @size / @capacity + end + + ### Query operation ### + def get(key) + index = hash_func(key) + bucket = @buckets[index] + # Traverse bucket, if key is found, return corresponding val + for pair in bucket + return pair.val if pair.key == key + end + # Return nil if key not found + nil + end + + ### Add operation ### + def put(key, val) + # When load factor exceeds threshold, perform expansion + extend if load_factor > @load_thres + index = hash_func(key) + bucket = @buckets[index] + # Traverse bucket, if specified key is encountered, update corresponding val and return + for pair in bucket + if pair.key == key + pair.val = val + return + end + end + # If key does not exist, append key-value pair to the end + pair = Pair.new(key, val) + bucket << pair + @size += 1 + end + + ### Delete operation ### + def remove(key) + index = hash_func(key) + bucket = @buckets[index] + # Traverse bucket and remove key-value pair from it + for pair in bucket + if pair.key == key + bucket.delete(pair) + @size -= 1 + break + end + end + end + + ### Expand hash table ### + def extend + # Temporarily store original hash table + buckets = @buckets + # Initialize expanded new hash table + @capacity *= @extend_ratio + @buckets = Array.new(@capacity) { [] } + @size = 0 + # Move key-value pairs from original hash table to new hash table + for bucket in buckets + for pair in bucket + put(pair.key, pair.val) + end + end + end + + ### Print hash table ### + def print + for bucket in @buckets + res = [] + for pair in bucket + res << "#{pair.key} -> #{pair.val}" + end + pp res + end + end +end + +### Driver Code ### +if __FILE__ == $0 + ### Initialize hash table + hashmap = HashMapChaining.new + + # Add operation + # Add key-value pair (key, value) to the hash table + hashmap.put(12836, "Xiao Ha") + hashmap.put(15937, "Xiao Luo") + hashmap.put(16750, "Xiao Suan") + hashmap.put(13276, "Xiao Fa") + hashmap.put(10583, "Xiao Ya") + puts "\nAfter adding, hash table is\n[Key1 -> Value1, Key2 -> Value2, ...]" + hashmap.print + + # Query operation + # Input key into hash table to get value + name = hashmap.get(13276) + puts "\nInput student ID 13276, found name #{name}" + + # Remove operation + # Remove key-value pair (key, value) from hash table + hashmap.remove(12836) + puts "\nAfter deleting 12836, hash table is\n[Key1 -> Value1, Key2 -> Value2, ...]" + hashmap.print +end diff --git a/en/codes/ruby/chapter_hashing/hash_map_open_addressing.rb b/en/codes/ruby/chapter_hashing/hash_map_open_addressing.rb new file mode 100644 index 000000000..078776632 --- /dev/null +++ b/en/codes/ruby/chapter_hashing/hash_map_open_addressing.rb @@ -0,0 +1,147 @@ +=begin +File: hash_map_open_addressing.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative './array_hash_map' + +### Hash map with open addressing ### +class HashMapOpenAddressing + TOMBSTONE = Pair.new(-1, '-1') # Removal marker + + ### Constructor ### + def initialize + @size = 0 # Number of key-value pairs + @capacity = 4 # Hash table capacity + @load_thres = 2.0 / 3.0 # Load factor threshold for triggering expansion + @extend_ratio = 2 # Expansion multiplier + @buckets = Array.new(@capacity) # Bucket array + end + + ### Hash function ### + def hash_func(key) + key % @capacity + end + + ### Load factor ### + def load_factor + @size / @capacity + end + + ### Search bucket index for key ### + def find_bucket(key) + index = hash_func(key) + first_tombstone = -1 + # Linear probing, break when encountering an empty bucket + while !@buckets[index].nil? + # If key is encountered, return the corresponding bucket index + if @buckets[index].key == key + # If a removal marker was encountered before, move the key-value pair to that index + if first_tombstone != -1 + @buckets[first_tombstone] = @buckets[index] + @buckets[index] = TOMBSTONE + return first_tombstone # Return the moved bucket index + end + return index # Return bucket index + end + # Record the first removal marker encountered + first_tombstone = index if first_tombstone == -1 && @buckets[index] == TOMBSTONE + # Calculate bucket index, wrap around to the head if past the tail + index = (index + 1) % @capacity + end + # If key does not exist, return the index for insertion + first_tombstone == -1 ? index : first_tombstone + end + + ### Query operation ### + def get(key) + # Search for bucket index corresponding to key + index = find_bucket(key) + # If key-value pair is found, return corresponding val + return @buckets[index].val unless [nil, TOMBSTONE].include?(@buckets[index]) + # Return nil if key-value pair does not exist + nil + end + + ### Add operation ### + def put(key, val) + # When load factor exceeds threshold, perform expansion + extend if load_factor > @load_thres + # Search for bucket index corresponding to key + index = find_bucket(key) + # If key-value pair found, overwrite val and return + unless [nil, TOMBSTONE].include?(@buckets[index]) + @buckets[index].val = val + return + end + # If key-value pair does not exist, add the key-value pair + @buckets[index] = Pair.new(key, val) + @size += 1 + end + + ### Delete operation ### + def remove(key) + # Search for bucket index corresponding to key + index = find_bucket(key) + # If key-value pair is found, overwrite it with removal marker + unless [nil, TOMBSTONE].include?(@buckets[index]) + @buckets[index] = TOMBSTONE + @size -= 1 + end + end + + ### Expand hash table ### + def extend + # Temporarily store the original hash table + buckets_tmp = @buckets + # Initialize expanded new hash table + @capacity *= @extend_ratio + @buckets = Array.new(@capacity) + @size = 0 + # Move key-value pairs from original hash table to new hash table + for pair in buckets_tmp + put(pair.key, pair.val) unless [nil, TOMBSTONE].include?(pair) + end + end + + ### Print hash table ### + def print + for pair in @buckets + if pair.nil? + puts "Nil" + elsif pair == TOMBSTONE + puts "TOMBSTONE" + else + puts "#{pair.key} -> #{pair.val}" + end + end + end +end + +### Driver Code ### +if __FILE__ == $0 + # Initialize hash table + hashmap = HashMapOpenAddressing.new + + # Add operation + # Add key-value pair (key, val) to the hash table + hashmap.put(12836, "Xiao Ha") + hashmap.put(15937, "Xiao Luo") + hashmap.put(16750, "Xiao Suan") + hashmap.put(13276, "Xiao Fa") + hashmap.put(10583, "Xiao Ya") + puts "\nAfter adding is complete, hash table is\nKey -> Value" + hashmap.print + + # Query operation + # Input key into hash table to get value val + name = hashmap.get(13276) + puts "\nInput student ID 13276, found name #{name}" + + # Remove operation + # Remove key-value pair (key, val) from hash table + hashmap.remove(16750) + puts "\nAfter removing 16750, hash table is\nKey -> Value" + hashmap.print +end diff --git a/en/codes/ruby/chapter_hashing/simple_hash.rb b/en/codes/ruby/chapter_hashing/simple_hash.rb new file mode 100644 index 000000000..c3bd3d789 --- /dev/null +++ b/en/codes/ruby/chapter_hashing/simple_hash.rb @@ -0,0 +1,62 @@ +=begin +File: simple_hash.rb +Created Time: 2024-04-14 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Additive hash ### +def add_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash += c.ord } + + hash % modulus +end + +### Multiplicative hash ### +def mul_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash = 31 * hash + c.ord } + + hash % modulus +end + +### XOR hash ### +def xor_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash ^= c.ord } + + hash % modulus +end + +### Rotational hash ### +def rot_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash = (hash << 4) ^ (hash >> 28) ^ c.ord } + + hash % modulus +end + +### Driver Code ### +if __FILE__ == $0 + key = "Hello Algo" + + hash = add_hash(key) + puts "Additive hash value is #{hash}" + + hash = mul_hash(key) + puts "Multiplicative hash value is #{hash}" + + hash = xor_hash(key) + puts "XOR hash value is #{hash}" + + hash = rot_hash(key) + puts "Rotational hash value is #{hash}" +end diff --git a/en/codes/ruby/chapter_heap/my_heap.rb b/en/codes/ruby/chapter_heap/my_heap.rb new file mode 100644 index 000000000..ef029db86 --- /dev/null +++ b/en/codes/ruby/chapter_heap/my_heap.rb @@ -0,0 +1,147 @@ +=begin +File: my_heap.rb +Created Time: 2024-04-19 +Author: Blue Bean (lonnnnnnner@gmail.com) +=end + +require_relative '../utils/print_util' + +### Max heap ### +class MaxHeap + attr_reader :max_heap + + ### Constructor, build heap from input list ### + def initialize(nums) + # Add list elements to heap as is + @max_heap = nums + # Heapify all nodes except leaf nodes + parent(size - 1).downto(0) do |i| + sift_down(i) + end + end + + ### Get left child index ### + def left(i) + 2 * i + 1 + end + + ### Get right child index ### + def right(i) + 2 * i + 2 + end + + ### Get parent node index ### + def parent(i) + (i - 1) / 2 # Floor division + end + + ### Swap elements ### + def swap(i, j) + @max_heap[i], @max_heap[j] = @max_heap[j], @max_heap[i] + end + + ### Get heap size ### + def size + @max_heap.length + end + + ### Check if heap is empty ### + def is_empty? + size == 0 + end + + ### Access heap top element ### + def peek + @max_heap[0] + end + + ### Push element to heap ### + def push(val) + # Add node + @max_heap << val + # Heapify from bottom to top + sift_up(size - 1) + end + + ### Heapify from node i, bottom to top ### + def sift_up(i) + loop do + # Get parent node of node i + p = parent(i) + # When "crossing root node" or "node needs no repair", end heapify + break if p < 0 || @max_heap[i] <= @max_heap[p] + # Swap two nodes + swap(i, p) + # Loop upward heapify + i = p + end + end + + ### Pop element from heap ### + def pop + # Handle empty case + raise IndexError, "Heap is empty" if is_empty? + # Delete node + swap(0, size - 1) + # Remove node + val = @max_heap.pop + # Return top element + sift_down(0) + # Return heap top element + val + end + + ### Heapify from node i, top to bottom ### + def sift_down(i) + loop do + # If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + l, r, ma = left(i), right(i), i + ma = l if l < size && @max_heap[l] > @max_heap[ma] + ma = r if r < size && @max_heap[r] > @max_heap[ma] + + # Swap two nodes + break if ma == i + + # Swap two nodes + swap(i, ma) + # Loop downwards heapification + i = ma + end + end + + ### Print heap (binary tree) ### + def __print__ + print_heap(@max_heap) + end +end + +### Driver Code ### +if __FILE__ == $0 + # Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + max_heap = MaxHeap.new([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]) + puts "\nAfter inputting list and building heap" + max_heap.__print__ + + # Check if heap is empty + peek = max_heap.peek + puts "\nHeap top element is #{peek}" + + # Element enters heap + val = 7 + max_heap.push(val) + puts "\nAfter element #{val} pushes to heap" + max_heap.__print__ + + # Time complexity is O(n), not O(nlogn) + peek = max_heap.pop + puts "\nAfter heap top element #{peek} pops from heap" + max_heap.__print__ + + # Get heap size + size = max_heap.size + puts "\nHeap size is #{size}" + + # Check if heap is empty + is_empty = max_heap.is_empty? + puts "\nIs heap empty #{is_empty}" +end diff --git a/en/codes/ruby/chapter_heap/top_k.rb b/en/codes/ruby/chapter_heap/top_k.rb new file mode 100644 index 000000000..5578dbb35 --- /dev/null +++ b/en/codes/ruby/chapter_heap/top_k.rb @@ -0,0 +1,64 @@ +=begin +File: top_k.rb +Created Time: 2024-04-19 +Author: Blue Bean (lonnnnnnner@gmail.com) +=end + +require_relative "./my_heap" + +### Push element to heap ### +def push_min_heap(heap, val) + # Negate element + heap.push(-val) +end + +### Pop element from heap ### +def pop_min_heap(heap) + # Negate element + -heap.pop +end + +### Access heap top element ### +def peek_min_heap(heap) + # Negate element + -heap.peek +end + +### Get elements from heap ### +def get_min_heap(heap) + # Negate all elements in heap + heap.max_heap.map { |x| -x } +end + +### Find largest k elements in array using heap ### +def top_k_heap(nums, k) + # Python's heapq module implements min heap by default + # Note: We negate all heap elements to simulate min heap using max heap + max_heap = MaxHeap.new([]) + + # Enter the first k elements of array into heap + for i in 0...k + push_min_heap(max_heap, nums[i]) + end + + # Starting from the (k+1)th element, maintain heap length as k + for i in k...nums.length + # If current element is greater than top element, top element exits heap, current element enters heap + if nums[i] > peek_min_heap(max_heap) + pop_min_heap(max_heap) + push_min_heap(max_heap, nums[i]) + end + end + + get_min_heap(max_heap) +end + +### Driver Code ### +if __FILE__ == $0 + nums = [1, 7, 6, 3, 2] + k = 3 + + res = top_k_heap(nums, k) + puts "The largest #{k} elements are" + print_heap(res) +end diff --git a/en/codes/ruby/chapter_searching/binary_search.rb b/en/codes/ruby/chapter_searching/binary_search.rb new file mode 100644 index 000000000..072f2a8ff --- /dev/null +++ b/en/codes/ruby/chapter_searching/binary_search.rb @@ -0,0 +1,63 @@ +=begin +File: binary_search.rb +Created Time: 2024-04-09 +Author: Blue Bean (lonnnnnnner@gmail.com) +=end + +### Binary search (closed interval) ### +def binary_search(nums, target) + # Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + i, j = 0, nums.length - 1 + + # Loop, exit when the search interval is empty (empty when i > j) + while i <= j + # In theory, Ruby numbers can be infinitely large (limited by memory), no need to consider overflow + m = (i + j) / 2 # Calculate the midpoint index m + + if nums[m] < target + i = m + 1 # This means target is in the interval [m+1, j] + elsif nums[m] > target + j = m - 1 # This means target is in the interval [i, m-1] + else + return m # Found the target element, return its index + end + end + + -1 # Target element not found, return -1 +end + +### Binary search (left-closed right-open interval) ### +def binary_search_lcro(nums, target) + # Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + i, j = 0, nums.length + + # Loop, exit when the search interval is empty (empty when i = j) + while i < j + # Calculate the midpoint index m + m = (i + j) / 2 + + if nums[m] < target + i = m + 1 # This means target is in the interval [m+1, j) + elsif nums[m] > target + j = m - 1 # This means target is in the interval [i, m) + else + return m # Found the target element, return its index + end + end + + -1 # Target element not found, return -1 +end + +### Driver Code ### +if __FILE__ == $0 + target = 6 + nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] + + # Binary search (closed interval on both sides) + index = binary_search(nums, target) + puts "Index of target element 6 is #{index}" + + # Binary search (left-closed right-open interval) + index = binary_search_lcro(nums, target) + puts "Index of target element 6 is #{index}" +end diff --git a/en/codes/ruby/chapter_searching/binary_search_edge.rb b/en/codes/ruby/chapter_searching/binary_search_edge.rb new file mode 100644 index 000000000..9dadd0368 --- /dev/null +++ b/en/codes/ruby/chapter_searching/binary_search_edge.rb @@ -0,0 +1,47 @@ +=begin +File: binary_search_edge.rb +Created Time: 2024-04-09 +Author: Blue Bean (lonnnnnnner@gmail.com) +=end + +require_relative './binary_search_insertion' + +### Binary search leftmost target ### +def binary_search_left_edge(nums, target) + # Equivalent to finding the insertion point of target + i = binary_search_insertion(nums, target) + + # Target not found, return -1 + return -1 if i == nums.length || nums[i] != target + + i # Found target, return index i +end + +### Binary search rightmost target ### +def binary_search_right_edge(nums, target) + # Convert to finding the leftmost target + 1 + i = binary_search_insertion(nums, target + 1) + + # j points to the rightmost target, i points to the first element greater than target + j = i - 1 + + # Target not found, return -1 + return -1 if j == -1 || nums[j] != target + + j # Found target, return index j +end + +### Driver Code ### +if __FILE__ == $0 + # Array with duplicate elements + nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] + puts "\nArray nums = #{nums}" + + # Binary search left and right boundaries + for target in [6, 7] + index = binary_search_left_edge(nums, target) + puts "Leftmost element #{target} index is #{index}" + index = binary_search_right_edge(nums, target) + puts "Rightmost element #{target} index is #{index}" + end +end diff --git a/en/codes/ruby/chapter_searching/binary_search_insertion.rb b/en/codes/ruby/chapter_searching/binary_search_insertion.rb new file mode 100644 index 000000000..bcf47a4d4 --- /dev/null +++ b/en/codes/ruby/chapter_searching/binary_search_insertion.rb @@ -0,0 +1,68 @@ +=begin +File: binary_search_insertion.rb +Created Time: 2024-04-09 +Author: Blue Bean (lonnnnnnner@gmail.com) +=end + +### Binary search insertion point (no duplicates) ### +def binary_search_insertion_simple(nums, target) + # Initialize closed interval [0, n-1] + i, j = 0, nums.length - 1 + + while i <= j + # Calculate the midpoint index m + m = (i + j) / 2 + + if nums[m] < target + i = m + 1 # target is in the interval [m+1, j] + elsif nums[m] > target + j = m - 1 # target is in the interval [i, m-1] + else + return m # Found target, return insertion point m + end + end + + i # Target not found, return insertion point i +end + +### Binary search insertion point (with duplicates) ### +def binary_search_insertion(nums, target) + # Initialize closed interval [0, n-1] + i, j = 0, nums.length - 1 + + while i <= j + # Calculate the midpoint index m + m = (i + j) / 2 + + if nums[m] < target + i = m + 1 # target is in the interval [m+1, j] + elsif nums[m] > target + j = m - 1 # target is in the interval [i, m-1] + else + j = m - 1 # The first element less than target is in the interval [i, m-1] + end + end + + i # Return insertion point i +end + +### Driver Code ### +if __FILE__ == $0 + # Array without duplicate elements + nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] + puts "\nArray nums = #{nums}" + # Binary search for insertion point + for target in [6, 9] + index = binary_search_insertion_simple(nums, target) + puts "Insertion point index for element #{target} is #{index}" + end + + # Array with duplicate elements + nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] + puts "\nArray nums = #{nums}" + # Binary search for insertion point + for target in [2, 6, 20] + index = binary_search_insertion(nums, target) + puts "Insertion point index for element #{target} is #{index}" + end +end diff --git a/en/codes/ruby/chapter_searching/hashing_search.rb b/en/codes/ruby/chapter_searching/hashing_search.rb new file mode 100644 index 000000000..c8f4b60af --- /dev/null +++ b/en/codes/ruby/chapter_searching/hashing_search.rb @@ -0,0 +1,47 @@ +=begin +File: hashing_search.rb +Created Time: 2024-04-09 +Author: Blue Bean (lonnnnnnner@gmail.com) +=end + +require_relative '../utils/list_node' + +### Hash search (array) ### +def hashing_search_array(hmap, target) + # Hash table's key: target element, value: index + # If this key does not exist in the hash table, return -1 + hmap[target] || -1 +end + +### Hash search (linked list) ### +def hashing_search_linkedlist(hmap, target) + # Hash table's key: target element, value: node object + # If this key does not exist in the hash table, return None + hmap[target] || nil +end + +### Driver Code ### +if __FILE__ == $0 + target = 3 + + # Hash search (array) + nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] + # Initialize hash table + map0 = {} + for i in 0...nums.length + map0[nums[i]] = i # key: element, value: index + end + index = hashing_search_array(map0, target) + puts "Index of target element 3 = #{index}" + + # Hash search (linked list) + head = arr_to_linked_list(nums) + # Initialize hash table + map1 = {} + while head + map1[head.val] = head + head = head.next + end + node = hashing_search_linkedlist(map1, target) + puts "Node object for target value 3 is #{node}" +end diff --git a/en/codes/ruby/chapter_searching/linear_search.rb b/en/codes/ruby/chapter_searching/linear_search.rb new file mode 100644 index 000000000..6f585a7e8 --- /dev/null +++ b/en/codes/ruby/chapter_searching/linear_search.rb @@ -0,0 +1,44 @@ +=begin +File: linear_search.rb +Created Time: 2024-04-09 +Author: Blue Bean (lonnnnnnner@gmail.com) +=end + +require_relative '../utils/list_node' + +### Linear search (array) ### +def linear_search_array(nums, target) + # Traverse array + for i in 0...nums.length + return i if nums[i] == target # Found the target element, return its index + end + + -1 # Target element not found, return -1 +end + +### Linear search (linked list) ### +def linear_search_linkedlist(head, target) + # Traverse the linked list + while head + return head if head.val == target # Found the target node, return it + + head = head.next + end + + nil # Target node not found, return None +end + +### Driver Code ### +if __FILE__ == $0 + target = 3 + + # Perform linear search in array + nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] + index = linear_search_array(nums, target) + puts "Index of target element 3 = #{index}" + + # Perform linear search in linked list + head = arr_to_linked_list(nums) + node = linear_search_linkedlist(head, target) + puts "Node object for target value 3 is #{node}" +end diff --git a/en/codes/ruby/chapter_searching/two_sum.rb b/en/codes/ruby/chapter_searching/two_sum.rb new file mode 100644 index 000000000..2b0d30703 --- /dev/null +++ b/en/codes/ruby/chapter_searching/two_sum.rb @@ -0,0 +1,46 @@ +=begin +File: two_sum.rb +Created Time: 2024-04-09 +Author: Blue Bean (lonnnnnnner@gmail.com) +=end + +### Method 1: Brute force enumeration ### +def two_sum_brute_force(nums, target) + # Two nested loops, time complexity is O(n^2) + for i in 0...(nums.length - 1) + for j in (i + 1)...nums.length + return [i, j] if nums[i] + nums[j] == target + end + end + + [] +end + +### Method 2: Auxiliary hash table ### +def two_sum_hash_table(nums, target) + # Auxiliary hash table, space complexity is O(n) + dic = {} + # Single loop, time complexity is O(n) + for i in 0...nums.length + return [dic[target - nums[i]], i] if dic.has_key?(target - nums[i]) + + dic[nums[i]] = i + end + + [] +end + +### Driver Code ### +if __FILE__ == $0 + # ======= Test Case ======= + nums = [2, 7, 11, 15] + target = 13 + + # ====== Driver Code ====== + # Method 1 + res = two_sum_brute_force(nums, target) + puts "Method 1 res = #{res}" + # Method 2 + res = two_sum_hash_table(nums, target) + puts "Method 2 res = #{res}" +end diff --git a/en/codes/ruby/chapter_sorting/bubble_sort.rb b/en/codes/ruby/chapter_sorting/bubble_sort.rb new file mode 100644 index 000000000..03b2ddc5e --- /dev/null +++ b/en/codes/ruby/chapter_sorting/bubble_sort.rb @@ -0,0 +1,51 @@ +=begin +File: bubble_sort.rb +Created Time: 2024-05-02 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Bubble sort ### +def bubble_sort(nums) + n = nums.length + # Outer loop: unsorted range is [0, i] + for i in (n - 1).downto(1) + # Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for j in 0...i + if nums[j] > nums[j + 1] + # Swap nums[j] and nums[j + 1] + nums[j], nums[j + 1] = nums[j + 1], nums[j] + end + end + end +end + +### Bubble sort (flag optimization) ### +def bubble_sort_with_flag(nums) + n = nums.length + # Outer loop: unsorted range is [0, i] + for i in (n - 1).downto(1) + flag = false # Initialize flag + + # Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for j in 0...i + if nums[j] > nums[j + 1] + # Swap nums[j] and nums[j + 1] + nums[j], nums[j + 1] = nums[j + 1], nums[j] + flag = true # Record element swap + end + end + + break unless flag # No elements were swapped in this round of "bubbling", exit directly + end +end + +### Driver Code ### +if __FILE__ == $0 + nums = [4, 1, 3, 1, 5, 2] + bubble_sort(nums) + puts "After bubble sort, nums = #{nums}" + + nums1 = [4, 1, 3, 1, 5, 2] + bubble_sort_with_flag(nums1) + puts "After bubble sort, nums = #{nums1}" +end diff --git a/en/codes/ruby/chapter_sorting/bucket_sort.rb b/en/codes/ruby/chapter_sorting/bucket_sort.rb new file mode 100644 index 000000000..47569176c --- /dev/null +++ b/en/codes/ruby/chapter_sorting/bucket_sort.rb @@ -0,0 +1,43 @@ +=begin +File: bucket_sort.rb +Created Time: 2024-04-17 +Author: Martin Xu (martin.xus@gmail.com) +=end + +### Bucket sort ### +def bucket_sort(nums) + # Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + k = nums.length / 2 + buckets = Array.new(k) { [] } + + # 1. Distribute array elements into various buckets + nums.each do |num| + # Input data range is [0, 1), use num * k to map to index range [0, k-1] + i = (num * k).to_i + # Add num to bucket i + buckets[i] << num + end + + # 2. Sort each bucket + buckets.each do |bucket| + # Use built-in sorting function, can also replace with other sorting algorithms + bucket.sort! + end + + # 3. Traverse buckets to merge results + i = 0 + buckets.each do |bucket| + bucket.each do |num| + nums[i] = num + i += 1 + end + end +end + +### Driver Code ### +if __FILE__ == $0 + # Assume input data is floating point, interval [0, 1) + nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37] + bucket_sort(nums) + puts "After bucket sort, nums = #{nums}" +end diff --git a/en/codes/ruby/chapter_sorting/counting_sort.rb b/en/codes/ruby/chapter_sorting/counting_sort.rb new file mode 100644 index 000000000..27d0edf3a --- /dev/null +++ b/en/codes/ruby/chapter_sorting/counting_sort.rb @@ -0,0 +1,62 @@ +=begin +File: counting_sort.rb +Created Time: 2024-05-02 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Counting sort ### +def counting_sort_naive(nums) + # Simple implementation, cannot be used for sorting objects + # 1. Count the maximum element m in the array + m = 0 + nums.each { |num| m = [m, num].max } + # 2. Count the occurrence of each number + # counter[num] represents the occurrence of num + counter = Array.new(m + 1, 0) + nums.each { |num| counter[num] += 1 } + # 3. Traverse counter, filling each element back into the original array nums + i = 0 + for num in 0...(m + 1) + (0...counter[num]).each do + nums[i] = num + i += 1 + end + end +end + +### Counting sort ### +def counting_sort(nums) + # Complete implementation, can sort objects and is a stable sort + # 1. Count the maximum element m in the array + m = nums.max + # 2. Count the occurrence of each number + # counter[num] represents the occurrence of num + counter = Array.new(m + 1, 0) + nums.each { |num| counter[num] += 1 } + # 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + # counter[num]-1 is the last index where num appears in res + (0...m).each { |i| counter[i + 1] += counter[i] } + # 4. Traverse nums in reverse, fill elements into result array res + # Initialize the array res to record results + n = nums.length + res = Array.new(n, 0) + (n - 1).downto(0).each do |i| + num = nums[i] + res[counter[num] - 1] = num # Place num at the corresponding index + counter[num] -= 1 # Decrement the prefix sum by 1, getting the next index to place num + end + # Use result array res to overwrite the original array nums + (0...n).each { |i| nums[i] = res[i] } +end + +### Driver Code ### +if __FILE__ == $0 + nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] + + counting_sort_naive(nums) + puts "After counting sort (cannot sort objects), nums = #{nums}" + + nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] + counting_sort(nums1) + puts "After counting sort, nums1 = #{nums1}" +end diff --git a/en/codes/ruby/chapter_sorting/heap_sort.rb b/en/codes/ruby/chapter_sorting/heap_sort.rb new file mode 100644 index 000000000..63e36a22e --- /dev/null +++ b/en/codes/ruby/chapter_sorting/heap_sort.rb @@ -0,0 +1,45 @@ +=begin +File: heap_sort.rb +Created Time: 2024-04-10 +Author: junminhong (junminhong1110@gmail.com) +=end + +### Heap length is n, heapify from node i, top to bottom ### +def sift_down(nums, n, i) + while true + # If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + l = 2 * i + 1 + r = 2 * i + 2 + ma = i + ma = l if l < n && nums[l] > nums[ma] + ma = r if r < n && nums[r] > nums[ma] + # Swap two nodes + break if ma == i + # Swap two nodes + nums[i], nums[ma] = nums[ma], nums[i] + # Loop downwards heapification + i = ma + end +end + +### Heap sort ### +def heap_sort(nums) + # Build heap operation: heapify all nodes except leaves + (nums.length / 2 - 1).downto(0) do |i| + sift_down(nums, nums.length, i) + end + # Extract the largest element from the heap and repeat for n-1 rounds + (nums.length - 1).downto(1) do |i| + # Delete node + nums[0], nums[i] = nums[i], nums[0] + # Start heapifying the root node, from top to bottom + sift_down(nums, i, 0) + end +end + +### Driver Code ### +if __FILE__ == $0 + nums = [4, 1, 3, 1, 5, 2] + heap_sort(nums) + puts "After heap sort, nums = #{nums.inspect}" +end diff --git a/en/codes/ruby/chapter_sorting/insertion_sort.rb b/en/codes/ruby/chapter_sorting/insertion_sort.rb new file mode 100644 index 000000000..d9e205e56 --- /dev/null +++ b/en/codes/ruby/chapter_sorting/insertion_sort.rb @@ -0,0 +1,26 @@ +=begin +File: insertion_sort.rb +Created Time: 2024-04-02 +Author: Cy (3739004@gmail.com), Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Insertion sort ### +def insertion_sort(nums) + n = nums.length + # Outer loop: sorted interval is [0, i-1] + for i in 1...n + base = nums[i] + j = i - 1 + # Inner loop: insert base into the correct position within the sorted interval [0, i-1] + while j >= 0 && nums[j] > base + nums[j + 1] = nums[j] # Move nums[j] to the right by one position + j -= 1 + end + nums[j + 1] = base # Assign base to the correct position + end +end + +### Driver Code ### +nums = [4, 1, 3, 1, 5, 2] +insertion_sort(nums) +puts "After insertion sort, nums = #{nums}" diff --git a/en/codes/ruby/chapter_sorting/merge_sort.rb b/en/codes/ruby/chapter_sorting/merge_sort.rb new file mode 100644 index 000000000..8682c1d79 --- /dev/null +++ b/en/codes/ruby/chapter_sorting/merge_sort.rb @@ -0,0 +1,60 @@ +=begin +File: merge_sort.rb +Created Time: 2024-04-10 +Author: junminhong (junminhong1110@gmail.com) +=end + +### Merge left and right subarrays ### +def merge(nums, left, mid, right) + # Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + # Create temporary array tmp to store merged result + tmp = Array.new(right - left + 1, 0) + # Initialize the start indices of the left and right subarrays + i, j, k = left, mid + 1, 0 + # While both subarrays still have elements, compare and copy the smaller element into the temporary array + while i <= mid && j <= right + if nums[i] <= nums[j] + tmp[k] = nums[i] + i += 1 + else + tmp[k] = nums[j] + j += 1 + end + k += 1 + end + # Copy the remaining elements of the left and right subarrays into the temporary array + while i <= mid + tmp[k] = nums[i] + i += 1 + k += 1 + end + while j <= right + tmp[k] = nums[j] + j += 1 + k += 1 + end + # Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + (0...tmp.length).each do |k| + nums[left + k] = tmp[k] + end +end + +### Merge sort ### +def merge_sort(nums, left, right) + # Termination condition + # Terminate recursion when subarray length is 1 + return if left >= right + # Divide and conquer stage + mid = left + (right - left) / 2 # Calculate midpoint + merge_sort(nums, left, mid) # Recursively process the left subarray + merge_sort(nums, mid + 1, right) # Recursively process the right subarray + # Merge stage + merge(nums, left, mid, right) +end + +### Driver Code ### +if __FILE__ == $0 + nums = [7, 3, 2, 6, 0, 1, 5, 4] + merge_sort(nums, 0, nums.length - 1) + puts "After merge sort, nums = #{nums.inspect}" +end diff --git a/en/codes/ruby/chapter_sorting/quick_sort.rb b/en/codes/ruby/chapter_sorting/quick_sort.rb new file mode 100644 index 000000000..b01341565 --- /dev/null +++ b/en/codes/ruby/chapter_sorting/quick_sort.rb @@ -0,0 +1,153 @@ +=begin +File: quick_sort.rb +Created Time: 2024-04-01 +Author: Cy (3739004@gmail.com), Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Quick sort class ### +class QuickSort + class << self + ### Sentinel partition ### + def partition(nums, left, right) + # Use nums[left] as the pivot + i, j = left, right + while i < j + while i < j && nums[j] >= nums[left] + j -= 1 # Search from right to left for the first element smaller than the pivot + end + while i < j && nums[i] <= nums[left] + i += 1 # Search from left to right for the first element greater than the pivot + end + # Swap elements + nums[i], nums[j] = nums[j], nums[i] + end + # Swap the pivot to the boundary between the two subarrays + nums[i], nums[left] = nums[left], nums[i] + i # Return the index of the pivot + end + + ### Quick sort class ### + def quick_sort(nums, left, right) + # Recurse when subarray length is not 1 + if left < right + # Sentinel partition + pivot = partition(nums, left, right) + # Recursively process the left subarray and right subarray + quick_sort(nums, left, pivot - 1) + quick_sort(nums, pivot + 1, right) + end + nums + end + end +end + +### Quick sort class (median optimization) ### +class QuickSortMedian + class << self + ### Select median of three candidate elements ### + def median_three(nums, left, mid, right) + # Select the median of three candidate elements + _l, _m, _r = nums[left], nums[mid], nums[right] + # m is between l and r + return mid if (_l <= _m && _m <= _r) || (_r <= _m && _m <= _l) + # l is between m and r + return left if (_m <= _l && _l <= _r) || (_r <= _l && _l <= _m) + return right + end + + ### Sentinel partition (median of three) ### + def partition(nums, left, right) + ### Use nums[left] as pivot + med = median_three(nums, left, (left + right) / 2, right) + # Swap median to leftmost position of array + nums[left], nums[med] = nums[med], nums[left] + i, j = left, right + while i < j + while i < j && nums[j] >= nums[left] + j -= 1 # Search from right to left for the first element smaller than the pivot + end + while i < j && nums[i] <= nums[left] + i += 1 # Search from left to right for the first element greater than the pivot + end + # Swap elements + nums[i], nums[j] = nums[j], nums[i] + end + # Swap the pivot to the boundary between the two subarrays + nums[i], nums[left] = nums[left], nums[i] + i # Return the index of the pivot + end + + ### Quick sort ### + def quick_sort(nums, left, right) + # Recurse when subarray length is not 1 + if left < right + # Sentinel partition + pivot = partition(nums, left, right) + # Recursively process the left subarray and right subarray + quick_sort(nums, left, pivot - 1) + quick_sort(nums, pivot + 1, right) + end + nums + end + end +end + +### Quick sort class (recursion depth optimization) ### +class QuickSortTailCall + class << self + ### Sentinel partition ### + def partition(nums, left, right) + # Use nums[left] as pivot + i = left + j = right + while i < j + while i < j && nums[j] >= nums[left] + j -= 1 # Search from right to left for the first element smaller than the pivot + end + while i < j && nums[i] <= nums[left] + i += 1 # Search from left to right for the first element greater than the pivot + end + # Swap elements + nums[i], nums[j] = nums[j], nums[i] + end + # Swap the pivot to the boundary between the two subarrays + nums[i], nums[left] = nums[left], nums[i] + i # Return the index of the pivot + end + + ### Quick sort (recursion depth optimization) ### + def quick_sort(nums, left, right) + # Recurse when subarray length is not 1 + while left < right + # Sentinel partition + pivot = partition(nums, left, right) + # Perform quick sort on the shorter of the two subarrays + if pivot - left < right - pivot + quick_sort(nums, left, pivot - 1) + left = pivot + 1 # Remaining unsorted interval is [pivot + 1, right] + else + quick_sort(nums, pivot + 1, right) + right = pivot - 1 # Remaining unsorted interval is [left, pivot - 1] + end + end + end + end +end + +### Driver Code ### +if __FILE__ == $0 + # Quick sort + nums = [2, 4, 1, 0, 3, 5] + QuickSort.quick_sort(nums, 0, nums.length - 1) + puts "After quick sort, nums = #{nums}" + + # Quick sort (recursion depth optimization) + nums1 = [2, 4, 1, 0, 3, 5] + QuickSortMedian.quick_sort(nums1, 0, nums1.length - 1) + puts "After quick sort (median pivot optimization), nums1 = #{nums1}" + + # Quick sort (recursion depth optimization) + nums2 = [2, 4, 1, 0, 3, 5] + QuickSortTailCall.quick_sort(nums2, 0, nums2.length - 1) + puts "After quick sort (recursion depth optimization), nums2 = #{nums2}" +end diff --git a/en/codes/ruby/chapter_sorting/radix_sort.rb b/en/codes/ruby/chapter_sorting/radix_sort.rb new file mode 100644 index 000000000..56e5289a2 --- /dev/null +++ b/en/codes/ruby/chapter_sorting/radix_sort.rb @@ -0,0 +1,70 @@ +=begin +File: radix_sort.rb +Created Time: 2024-05-03 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Get k-th digit of element num, where exp = 10^(k-1) ### +def digit(num, exp) + # Passing exp instead of k avoids expensive exponentiation calculations + (num / exp) % 10 +end + +### Counting sort (sort by k-th digit of nums) ### +def counting_sort_digit(nums, exp) + # Decimal digit range is 0~9, therefore need a bucket array of length 10 + counter = Array.new(10, 0) + n = nums.length + # Count the occurrence of digits 0~9 + for i in 0...n + d = digit(nums[i], exp) # Get the k-th digit of nums[i], noted as d + counter[d] += 1 # Count the occurrence of digit d + end + # Calculate prefix sum, converting "occurrence count" into "array index" + (1...10).each { |i| counter[i] += counter[i - 1] } + # Traverse in reverse, based on bucket statistics, place each element into res + res = Array.new(n, 0) + for i in (n - 1).downto(0) + d = digit(nums[i], exp) + j = counter[d] - 1 # Get the index j for d in the array + res[j] = nums[i] # Place the current element at index j + counter[d] -= 1 # Decrease the count of d by 1 + end + # Use result to overwrite the original array nums + (0...n).each { |i| nums[i] = res[i] } +end + +### Radix sort ### +def radix_sort(nums) + # Get the maximum element of the array, used to determine the maximum number of digits + m = nums.max + # Traverse from the lowest to the highest digit + exp = 1 + while exp <= m + # Perform counting sort on the k-th digit of array elements + # k = 1 -> exp = 1 + # k = 2 -> exp = 10 + # i.e., exp = 10^(k-1) + counting_sort_digit(nums, exp) + exp *= 10 + end +end + +### Driver Code ### +if __FILE__ == $0 + # Radix sort + nums = [ + 10546151, + 35663510, + 42865989, + 34862445, + 81883077, + 88906420, + 72429244, + 30524779, + 82060337, + 63832996, + ] + radix_sort(nums) + puts "After radix sort, nums = #{nums}" +end diff --git a/en/codes/ruby/chapter_sorting/selection_sort.rb b/en/codes/ruby/chapter_sorting/selection_sort.rb new file mode 100644 index 000000000..8c1ab0475 --- /dev/null +++ b/en/codes/ruby/chapter_sorting/selection_sort.rb @@ -0,0 +1,29 @@ +=begin +File: selection_sort.rb +Created Time: 2024-05-03 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Selection sort ### +def selection_sort(nums) + n = nums.length + # Outer loop: unsorted interval is [i, n-1] + for i in 0...(n - 1) + # Inner loop: find the smallest element within the unsorted interval + k = i + for j in (i + 1)...n + if nums[j] < nums[k] + k = j # Record the index of the smallest element + end + end + # Swap the smallest element with the first element of the unsorted interval + nums[i], nums[k] = nums[k], nums[i] + end +end + +### Driver Code ### +if __FILE__ == $0 + nums = [4, 1, 3, 1, 5, 2] + selection_sort(nums) + puts "After selection sort, nums = #{nums}" +end diff --git a/en/codes/ruby/chapter_stack_and_queue/array_deque.rb b/en/codes/ruby/chapter_stack_and_queue/array_deque.rb new file mode 100644 index 000000000..7e505b36e --- /dev/null +++ b/en/codes/ruby/chapter_stack_and_queue/array_deque.rb @@ -0,0 +1,145 @@ +=begin +File: array_deque.rb +Created Time: 2024-04-05 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Deque based on circular array ### +class ArrayDeque + ### Get deque length ### + attr_reader :size + + ### Constructor ### + def initialize(capacity) + @nums = Array.new(capacity, 0) + @front = 0 + @size = 0 + end + + ### Get deque capacity ### + def capacity + @nums.length + end + + ### Check if deque is empty ### + def is_empty? + size.zero? + end + + ### Enqueue at front ### + def push_first(num) + if size == capacity + puts 'Double-ended queue is full' + return + end + + # Use modulo operation to wrap front around to the tail after passing the head of the array + # Add num to the front of the queue + @front = index(@front - 1) + # Add num to front of queue + @nums[@front] = num + @size += 1 + end + + ### Enqueue at rear ### + def push_last(num) + if size == capacity + puts 'Double-ended queue is full' + return + end + + # Use modulo operation to wrap rear around to the head after passing the tail of the array + rear = index(@front + size) + # Front pointer moves one position backward + @nums[rear] = num + @size += 1 + end + + ### Dequeue from front ### + def pop_first + num = peek_first + # Move front pointer backward by one position + @front = index(@front + 1) + @size -= 1 + num + end + + ### Dequeue from rear ### + def pop_last + num = peek_last + @size -= 1 + num + end + + ### Access front element ### + def peek_first + raise IndexError, 'Deque is empty' if is_empty? + + @nums[@front] + end + + ### Access rear element ### + def peek_last + raise IndexError, 'Deque is empty' if is_empty? + + # Initialize double-ended queue + last = index(@front + size - 1) + @nums[last] + end + + ### Return array for printing ### + def to_array + # Elements enqueue + res = [] + for i in 0...size + res << @nums[index(@front + i)] + end + res + end + + private + + ### Calculate circular array index ### + def index(i) + # Use modulo operation to wrap the array head and tail together + # When i passes the tail of the array, return to the head + # When i passes the head of the array, return to the tail + (i + capacity) % capacity + end +end + +### Driver Code ### +if __FILE__ == $0 + # Get the length of the double-ended queue + deque = ArrayDeque.new(10) + deque.push_last(3) + deque.push_last(2) + deque.push_last(5) + puts "Deque deque = #{deque.to_array}" + + # Update element + peek_first = deque.peek_first + puts "Front element peek_first = #{peek_first}" + peek_last = deque.peek_last + puts "Rear element peek_last = #{peek_last}" + + # Elements enqueue + deque.push_last(4) + puts "After element 4 enqueues at rear, deque = #{deque.to_array}" + deque.push_first(1) + puts "After element 1 enqueues at rear, deque = #{deque.to_array}" + + # Element dequeue + pop_last = deque.pop_last + puts "Dequeue rear element = #{pop_last}, after dequeue deque = #{deque.to_array}" + pop_first = deque.pop_first + puts "Dequeue front element = #{pop_first}, after dequeue deque = #{deque.to_array}" + + # Get the length of the double-ended queue + size = deque.size + puts "Deque length size = #{size}" + + # Check if the double-ended queue is empty + is_empty = deque.is_empty? + puts "Is deque empty = #{is_empty}" +end diff --git a/en/codes/ruby/chapter_stack_and_queue/array_queue.rb b/en/codes/ruby/chapter_stack_and_queue/array_queue.rb new file mode 100644 index 000000000..1303a5f8b --- /dev/null +++ b/en/codes/ruby/chapter_stack_and_queue/array_queue.rb @@ -0,0 +1,107 @@ +=begin +File: array_queue.rb +Created Time: 2024-04-05 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Queue based on circular array ### +class ArrayQueue + ### Get queue length ### + attr_reader :size + + ### Constructor ### + def initialize(size) + @nums = Array.new(size, 0) # Array for storing queue elements + @front = 0 # Front pointer, points to the front of the queue element + @size = 0 # Queue length + end + + ### Get queue capacity ### + def capacity + @nums.length + end + + ### Check if queue is empty ### + def is_empty? + size.zero? + end + + ### Enqueue ### + def push(num) + raise IndexError, 'Queue is full' if size == capacity + + # Use modulo operation to wrap rear around to the head after passing the tail of the array + # Add num to the rear of the queue + rear = (@front + size) % capacity + # Front pointer moves one position backward + @nums[rear] = num + @size += 1 + end + + ### Dequeue ### + def pop + num = peek + # Move front pointer backward by one position, if it passes the tail, return to array head + @front = (@front + 1) % capacity + @size -= 1 + num + end + + ### Access front element ### + def peek + raise IndexError, 'Queue is empty' if is_empty? + + @nums[@front] + end + + ### Return list for printing ### + def to_array + res = Array.new(size, 0) + j = @front + + for i in 0...size + res[i] = @nums[j % capacity] + j += 1 + end + + res + end +end + +### Driver Code ### +if __FILE__ == $0 + # Access front of the queue element + queue = ArrayQueue.new(10) + + # Elements enqueue + queue.push(1) + queue.push(3) + queue.push(2) + queue.push(5) + queue.push(4) + puts "Queue queue = #{queue.to_array}" + + # Return list for printing + peek = queue.peek + puts "Front element peek = #{peek}" + + # Element dequeue + pop = queue.pop + puts "Dequeue element pop = #{pop}" + puts "After dequeue, queue = #{queue.to_array}" + + # Get the length of the queue + size = queue.size + puts "Queue length size = #{size}" + + # Check if the queue is empty + is_empty = queue.is_empty? + puts "Is queue empty = #{is_empty}" + + # Test circular array + for i in 0...10 + queue.push(i) + queue.pop + puts "After round #{i} of enqueue + dequeue, queue = #{queue.to_array}" + end +end diff --git a/en/codes/ruby/chapter_stack_and_queue/array_stack.rb b/en/codes/ruby/chapter_stack_and_queue/array_stack.rb new file mode 100644 index 000000000..cb4adf3f0 --- /dev/null +++ b/en/codes/ruby/chapter_stack_and_queue/array_stack.rb @@ -0,0 +1,78 @@ +=begin +File: array_stack.rb +Created Time: 2024-04-06 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Stack based on array ### +class ArrayStack + ### Constructor ### + def initialize + @stack = [] + end + + ### Get stack length ### + def size + @stack.length + end + + ### Check if stack is empty ### + def is_empty? + @stack.empty? + end + + ### Push ### + def push(item) + @stack << item + end + + ### Pop ### + def pop + raise IndexError, 'Stack is empty' if is_empty? + + @stack.pop + end + + ### Access top element ### + def peek + raise IndexError, 'Stack is empty' if is_empty? + + @stack.last + end + + ### Return list for printing ### + def to_array + @stack + end +end + +### Driver Code ### +if __FILE__ == $0 + # Access top of the stack element + stack = ArrayStack.new + + # Elements push onto stack + stack.push(1) + stack.push(3) + stack.push(2) + stack.push(5) + stack.push(4) + puts "Stack stack = #{stack.to_array}" + + # Return list for printing + peek = stack.peek + puts "Top element peek = #{peek}" + + # Element pop from stack + pop = stack.pop + puts "Pop element pop = #{pop}" + puts "After pop, stack = #{stack.to_array}" + + # Get the length of the stack + size = stack.size + puts "Stack length size = #{size}" + + # Check if empty + is_empty = stack.is_empty? + puts "Is stack empty = #{is_empty}" +end diff --git a/en/codes/ruby/chapter_stack_and_queue/deque.rb b/en/codes/ruby/chapter_stack_and_queue/deque.rb new file mode 100644 index 000000000..016b94f08 --- /dev/null +++ b/en/codes/ruby/chapter_stack_and_queue/deque.rb @@ -0,0 +1,42 @@ +=begin +File: deque.rb +Created Time: 2024-04-06 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Driver Code ### +if __FILE__ == $0 + # Get the length of the double-ended queue + # Ruby has no built-in deque, can only use Array as deque + deque = [] + + # Element enqueues + deque << 2 + deque << 5 + deque << 4 + # Note: due to array, Array#unshift method has O(n) time complexity + deque.unshift(3) + deque.unshift(1) + puts "Deque deque = #{deque}" + + # Update element + peek_first = deque.first + puts "Front element peek_first = #{peek_first}" + peek_last = deque.last + puts "Rear element peek_last = #{peek_last}" + + # Element dequeue + # Note: due to array, Array#shift method has O(n) time complexity + pop_front = deque.shift + puts "Dequeue front element pop_front = #{pop_front}, after dequeue deque = #{deque}" + pop_back = deque.pop + puts "Dequeue rear element pop_back = #{pop_back}, after dequeue deque = #{deque}" + + # Get the length of the double-ended queue + size = deque.length + puts "Deque length size = #{size}" + + # Check if the double-ended queue is empty + is_empty = size.zero? + puts "Is deque empty = #{is_empty}" +end diff --git a/en/codes/ruby/chapter_stack_and_queue/linkedlist_deque.rb b/en/codes/ruby/chapter_stack_and_queue/linkedlist_deque.rb new file mode 100644 index 000000000..c397fa976 --- /dev/null +++ b/en/codes/ruby/chapter_stack_and_queue/linkedlist_deque.rb @@ -0,0 +1,168 @@ +=begin +File: linkedlist_deque.rb +Created Time: 2024-04-06 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Doubly linked list node +class ListNode + attr_accessor :val + attr_accessor :next # Successor node reference + attr_accessor :prev # Predecessor node reference + + ### Constructor ### + def initialize(val) + @val = val + end +end + +### Deque based on doubly linked list ### +class LinkedListDeque + ### Get deque length ### + attr_reader :size + + ### Constructor ### + def initialize + @front = nil # Head node front + @rear = nil # Tail node rear + @size = 0 # Length of the double-ended queue + end + + ### Check if deque is empty ### + def is_empty? + size.zero? + end + + ### Enqueue operation ### + def push(num, is_front) + node = ListNode.new(num) + # If list is empty, set both front and rear to node + if is_empty? + @front = @rear = node + # Front of the queue enqueue operation + elsif is_front + # Add node to the head of the linked list + @front.prev = node + node.next = @front + @front = node # Update head node + # Rear of the queue enqueue operation + else + # Add node to the tail of the linked list + @rear.next = node + node.prev = @rear + @rear = node # Update tail node + end + @size += 1 # Update queue length + end + + ### Enqueue at front ### + def push_first(num) + push(num, true) + end + + ### Enqueue at rear ### + def push_last(num) + push(num, false) + end + + ### Dequeue operation ### + def pop(is_front) + raise IndexError, 'Deque is empty' if is_empty? + + # Temporarily store head node value + if is_front + val = @front.val # Delete head node + # Delete head node + fnext = @front.next + unless fnext.nil? + fnext.prev = nil + @front.next = nil + end + @front = fnext # Update head node + # Temporarily store tail node value + else + val = @rear.val # Delete tail node + # Update tail node + rprev = @rear.prev + unless rprev.nil? + rprev.next = nil + @rear.prev = nil + end + @rear = rprev # Update tail node + end + @size -= 1 # Update queue length + + val + end + + ### Dequeue from front ### + def pop_first + pop(true) + end + + ### Dequeue from front ### + def pop_last + pop(false) + end + + ### Access front element ### + def peek_first + raise IndexError, 'Deque is empty' if is_empty? + + @front.val + end + + ### Access rear element ### + def peek_last + raise IndexError, 'Deque is empty' if is_empty? + + @rear.val + end + + ### Return array for printing ### + def to_array + node = @front + res = Array.new(size, 0) + for i in 0...size + res[i] = node.val + node = node.next + end + res + end +end + +### Driver Code ### +if __FILE__ == $0 + # Get the length of the double-ended queue + deque = LinkedListDeque.new + deque.push_last(3) + deque.push_last(2) + deque.push_last(5) + puts "Deque deque = #{deque.to_array}" + + # Update element + peek_first = deque.peek_first + puts "Front element peek_first = #{peek_first}" + peek_last = deque.peek_last + puts "Rear element peek_last = #{peek_last}" + + # Elements enqueue + deque.push_last(4) + puts "After element 4 enqueues at rear, deque = #{deque.to_array}" + deque.push_first(1) + puts "After element 1 enqueues at front, deque = #{deque.to_array}" + + # Element dequeue + pop_last = deque.pop_last + puts "Dequeue rear element = #{pop_last}, after dequeue deque = #{deque.to_array}" + pop_first = deque.pop_first + puts "Dequeue front element = #{pop_first}, after dequeue deque = #{deque.to_array}" + + # Get the length of the double-ended queue + size = deque.size + puts "Deque length size = #{size}" + + # Check if the double-ended queue is empty + is_empty = deque.is_empty? + puts "Is deque empty = #{is_empty}" +end diff --git a/en/codes/ruby/chapter_stack_and_queue/linkedlist_queue.rb b/en/codes/ruby/chapter_stack_and_queue/linkedlist_queue.rb new file mode 100644 index 000000000..c140455de --- /dev/null +++ b/en/codes/ruby/chapter_stack_and_queue/linkedlist_queue.rb @@ -0,0 +1,101 @@ +=begin +File: linkedlist_queue.rb +Created Time: 2024-04-06 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/list_node' + +### Queue based on linked list ### +class LinkedListQueue + ### Get queue length ### + attr_reader :size + + ### Constructor ### + def initialize + @front = nil # Head node front + @rear = nil # Tail node rear + @size = 0 + end + + ### Check if queue is empty ### + def is_empty? + @front.nil? + end + + ### Enqueue ### + def push(num) + # Add num after the tail node + node = ListNode.new(num) + + # If queue is empty, set both front and rear to this node + if @front.nil? + @front = node + @rear = node + # If queue is not empty, add this node after rear + else + @rear.next = node + @rear = node + end + + @size += 1 + end + + ### Dequeue ### + def pop + num = peek + # Delete head node + @front = @front.next + @size -= 1 + num + end + + ### Access front element ### + def peek + raise IndexError, 'Queue is empty' if is_empty? + + @front.val + end + + ### Convert linked list to Array and return ### + def to_array + queue = [] + temp = @front + while temp + queue << temp.val + temp = temp.next + end + queue + end +end + +### Driver Code ### +if __FILE__ == $0 + # Access front of the queue element + queue = LinkedListQueue.new + + # Element enqueues + queue.push(1) + queue.push(3) + queue.push(2) + queue.push(5) + queue.push(4) + puts "Queue queue = #{queue.to_array}" + + # Return list for printing + peek = queue.peek + puts "Front element = #{peek}" + + # Element dequeue + pop_front = queue.pop + puts "Dequeue element pop = #{pop_front}" + puts "After dequeue, queue = #{queue.to_array}" + + # Get the length of the queue + size = queue.size + puts "Queue length size = #{size}" + + # Check if the queue is empty + is_empty = queue.is_empty? + puts "Is queue empty = #{is_empty}" +end diff --git a/en/codes/ruby/chapter_stack_and_queue/linkedlist_stack.rb b/en/codes/ruby/chapter_stack_and_queue/linkedlist_stack.rb new file mode 100644 index 000000000..2831e9a9c --- /dev/null +++ b/en/codes/ruby/chapter_stack_and_queue/linkedlist_stack.rb @@ -0,0 +1,87 @@ +=begin +File: linkedlist_stack.rb +Created Time: 2024-04-06 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/list_node' + +### Stack based on linked list ### +class LinkedListStack + attr_reader :size + + ### Constructor ### + def initialize + @size = 0 + end + + ### Check if stack is empty ### + def is_empty? + @peek.nil? + end + + ### Push ### + def push(val) + node = ListNode.new(val) + node.next = @peek + @peek = node + @size += 1 + end + + ### Pop ### + def pop + num = peek + @peek = @peek.next + @size -= 1 + num + end + + ### Access top element ### + def peek + raise IndexError, 'Stack is empty' if is_empty? + + @peek.val + end + + ### Convert linked list to Array and return ### + def to_array + arr = [] + node = @peek + while node + arr << node.val + node = node.next + end + arr.reverse + end +end + +### Driver Code ### +if __FILE__ == $0 + # Access top of the stack element + stack = LinkedListStack.new + + # Elements push onto stack + stack.push(1) + stack.push(3) + stack.push(2) + stack.push(5) + stack.push(4) + puts "Stack stack = #{stack.to_array}" + + # Return list for printing + peek = stack.peek + puts "Top element peek = #{peek}" + + # Element pop from stack + pop = stack.pop + puts "Pop element pop = #{pop}" + puts "After pop, stack = #{stack.to_array}" + + # Get the length of the stack + size = stack.size + puts "Stack length size = #{size}" + + # Check if empty + is_empty = stack.is_empty? + puts "Is stack empty = #{is_empty}" +end diff --git a/en/codes/ruby/chapter_stack_and_queue/queue.rb b/en/codes/ruby/chapter_stack_and_queue/queue.rb new file mode 100644 index 000000000..3fc5f1fc3 --- /dev/null +++ b/en/codes/ruby/chapter_stack_and_queue/queue.rb @@ -0,0 +1,38 @@ +=begin +File: queue.rb +Created Time: 2024-04-06 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Driver Code ### +if __FILE__ == $0 + # Access front of the queue element + # Ruby's built-in queue (Thread::Queue) has no peek and traversal methods, can use Array as queue + queue = [] + + # Elements enqueue + queue.push(1) + queue.push(3) + queue.push(2) + queue.push(5) + queue.push(4) + puts "Queue queue = #{queue}" + + # Access queue elements + peek = queue.first + puts "Front element peek = #{peek}" + + # Element dequeue + # Note: due to array, Array#shift method has O(n) time complexity + pop = queue.shift + puts "Dequeue element pop = #{pop}" + puts "After dequeue, queue = #{queue}" + + # Get the length of the queue + size = queue.length + puts "Queue length size = #{size}" + + # Check if the queue is empty + is_empty = queue.empty? + puts "Is queue empty = #{is_empty}" +end diff --git a/en/codes/ruby/chapter_stack_and_queue/stack.rb b/en/codes/ruby/chapter_stack_and_queue/stack.rb new file mode 100644 index 000000000..8f4e0b7d4 --- /dev/null +++ b/en/codes/ruby/chapter_stack_and_queue/stack.rb @@ -0,0 +1,37 @@ +=begin +File: stack.rb +Created Time: 2024-04-06 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Driver Code ### +if __FILE__ == $0 + # Access top of the stack element + # Ruby has no built-in stack class, can use Array as stack + stack = [] + + # Elements push onto stack + stack << 1 + stack << 3 + stack << 2 + stack << 5 + stack << 4 + puts "Stack stack = #{stack}" + + # Return list for printing + peek = stack.last + puts "Top element peek = #{peek}" + + # Element pop from stack + pop = stack.pop + puts "Pop element pop = #{pop}" + puts "After pop, stack = #{stack}" + + # Get the length of the stack + size = stack.length + puts "Stack length size = #{size}" + + # Check if empty + is_empty = stack.empty? + puts "Is stack empty = #{is_empty}" +end diff --git a/en/codes/ruby/chapter_tree/array_binary_tree.rb b/en/codes/ruby/chapter_tree/array_binary_tree.rb new file mode 100644 index 000000000..6a5515147 --- /dev/null +++ b/en/codes/ruby/chapter_tree/array_binary_tree.rb @@ -0,0 +1,124 @@ +=begin +File: array_binary_tree.rb +Created Time: 2024-04-17 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Array representation of binary tree class ### +class ArrayBinaryTree + ### Constructor ### + def initialize(arr) + @tree = arr.to_a + end + + ### List capacity ### + def size + @tree.length + end + + ### Get value of node at index i ### + def val(i) + # Return nil if index out of bounds, representing empty position + return if i < 0 || i >= size + + @tree[i] + end + + ### Get left child index of node at index i ### + def left(i) + 2 * i + 1 + end + + ### Get right child index of node at index i ### + def right(i) + 2 * i + 2 + end + + ### Get parent node index of node at index i ### + def parent(i) + (i - 1) / 2 + end + + ### Level-order traversal ### + def level_order + @res = [] + + # Traverse array directly + for i in 0...size + @res << val(i) unless val(i).nil? + end + + @res + end + + ### Depth-first traversal ### + def dfs(i, order) + return if val(i).nil? + # Preorder traversal + @res << val(i) if order == :pre + dfs(left(i), order) + # Inorder traversal + @res << val(i) if order == :in + dfs(right(i), order) + # Postorder traversal + @res << val(i) if order == :post + end + + ### Pre-order traversal ### + def pre_order + @res = [] + dfs(0, :pre) + @res + end + + ### In-order traversal ### + def in_order + @res = [] + dfs(0, :in) + @res + end + + ### Post-order traversal ### + def post_order + @res = [] + dfs(0, :post) + @res + end +end + +### Driver Code ### +if __FILE__ == $0 + # Initialize binary tree + # Here we use a function to generate a binary tree directly from an array + arr = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] + root = arr_to_tree(arr) + puts "\nInitialize binary tree\n\n" + puts 'Array representation of binary tree:' + pp arr + puts 'Linked list representation of binary tree:' + print_tree(root) + + # Binary tree class represented by array + abt = ArrayBinaryTree.new(arr) + + # Access node + i = 1 + l, r, _p = abt.left(i), abt.right(i), abt.parent(i) + puts "\nCurrent node index is #{i}, value is #{abt.val(i).inspect}" + puts "Left child index is #{l}, value is #{abt.val(l).inspect}" + puts "Right child index is #{r}, value is #{abt.val(r).inspect}" + puts "Parent node index is #{_p}, value is #{abt.val(_p).inspect}" + + # Traverse tree + res = abt.level_order + puts "\nLevel-order traversal is: #{res}" + res = abt.pre_order + puts "Pre-order traversal is: #{res}" + res = abt.in_order + puts "In-order traversal is: #{res}" + res = abt.post_order + puts "Post-order traversal is: #{res}" +end diff --git a/en/codes/ruby/chapter_tree/avl_tree.rb b/en/codes/ruby/chapter_tree/avl_tree.rb new file mode 100644 index 000000000..e6c6f0544 --- /dev/null +++ b/en/codes/ruby/chapter_tree/avl_tree.rb @@ -0,0 +1,216 @@ +=begin +File: avl_tree.rb +Created Time: 2024-04-17 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### AVL tree ### +class AVLTree + ### Constructor ### + def initialize + @root = nil + end + + ### Get binary tree root node ### + def get_root + @root + end + + ### Get node height ### + def height(node) + # Empty node height is -1, leaf node height is 0 + return node.height unless node.nil? + + -1 + end + + ### Update node height ### + def update_height(node) + # Node height equals the height of the tallest subtree + 1 + node.height = [height(node.left), height(node.right)].max + 1 + end + + ### Get balance factor ### + def balance_factor(node) + # Empty node balance factor is 0 + return 0 if node.nil? + + # Node balance factor = left subtree height - right subtree height + height(node.left) - height(node.right) + end + + ### Right rotation ### + def right_rotate(node) + child = node.left + grand_child = child.right + # Using child as pivot, rotate node to the right + child.right = node + node.left = grand_child + # Update node height + update_height(node) + update_height(child) + # Return root node of subtree after rotation + child + end + + ### Left rotation ### + def left_rotate(node) + child = node.right + grand_child = child.left + # Using child as pivot, rotate node to the left + child.left = node + node.right = grand_child + # Update node height + update_height(node) + update_height(child) + # Return root node of subtree after rotation + child + end + + ### Perform rotation to rebalance subtree ### + def rotate(node) + # Get balance factor of node + balance_factor = balance_factor(node) + # Left-heavy tree + if balance_factor > 1 + if balance_factor(node.left) >= 0 + # Right rotation + return right_rotate(node) + else + # First left rotation then right rotation + node.left = left_rotate(node.left) + return right_rotate(node) + end + # Right-heavy tree + elsif balance_factor < -1 + if balance_factor(node.right) <= 0 + # Left rotation + return left_rotate(node) + else + # First right rotation then left rotation + node.right = right_rotate(node.right) + return left_rotate(node) + end + end + # Balanced tree, no rotation needed, return directly + node + end + + ### Insert node ### + def insert(val) + @root = insert_helper(@root, val) + end + + ### Recursively insert node (helper method) ### + def insert_helper(node, val) + return TreeNode.new(val) if node.nil? + # 1. Find insertion position and insert node + if val < node.val + node.left = insert_helper(node.left, val) + elsif val > node.val + node.right = insert_helper(node.right, val) + else + # Duplicate node not inserted, return directly + return node + end + # Update node height + update_height(node) + # 2. Perform rotation operation to restore balance to this subtree + rotate(node) + end + + ### Delete node ### + def remove(val) + @root = remove_helper(@root, val) + end + + ### Recursively delete node (helper method) ### + def remove_helper(node, val) + return if node.nil? + # 1. Find node and delete + if val < node.val + node.left = remove_helper(node.left, val) + elsif val > node.val + node.right = remove_helper(node.right, val) + else + if node.left.nil? || node.right.nil? + child = node.left || node.right + # Number of child nodes = 0, delete node directly and return + return if child.nil? + # Number of child nodes = 1, delete node directly + node = child + else + # Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + temp = node.right + while !temp.left.nil? + temp = temp.left + end + node.right = remove_helper(node.right, temp.val) + node.val = temp.val + end + end + # Update node height + update_height(node) + # 2. Perform rotation operation to restore balance to this subtree + rotate(node) + end + + ### Search node ### + def search(val) + cur = @root + # Loop search, exit after passing leaf node + while !cur.nil? + # Target node is in cur's right subtree + if cur.val < val + cur = cur.right + # Target node is in cur's left subtree + elsif cur.val > val + cur = cur.left + # Found target node, exit loop + else + break + end + end + # Return target node + cur + end +end + +### Driver Code ### +if __FILE__ == $0 + def test_insert(tree, val) + tree.insert(val) + puts "\nAfter inserting node #{val}, AVL tree is" + print_tree(tree.get_root) + end + + def test_remove(tree, val) + tree.remove(val) + puts "\nAfter deleting node #{val}, AVL tree is" + print_tree(tree.get_root) + end + + # Please pay attention to how the AVL tree maintains balance after inserting nodes + avl_tree = AVLTree.new + + # Insert node + # Delete nodes + for val in [1, 2, 3, 4, 5, 8, 7, 9, 10, 6] + test_insert(avl_tree, val) + end + + # Please pay attention to how the AVL tree maintains balance after deleting nodes + test_insert(avl_tree, 7) + + # Remove node + # Delete node with degree 1 + test_remove(avl_tree, 8) # Delete node with degree 2 + test_remove(avl_tree, 5) # Remove node with degree 1 + test_remove(avl_tree, 4) # Remove node with degree 2 + + result_node = avl_tree.search(7) + puts "\nFound node object #{result_node}, node value = #{result_node.val}" +end diff --git a/en/codes/ruby/chapter_tree/binary_search_tree.rb b/en/codes/ruby/chapter_tree/binary_search_tree.rb new file mode 100644 index 000000000..5717fcb2d --- /dev/null +++ b/en/codes/ruby/chapter_tree/binary_search_tree.rb @@ -0,0 +1,161 @@ +=begin +File: binary_search_tree.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Binary search tree ### +class BinarySearchTree + ### Constructor ### + def initialize + # Initialize empty tree + @root = nil + end + + ### Get binary tree root node ### + def get_root + @root + end + + ### Search node ### + def search(num) + cur = @root + + # Loop search, exit after passing leaf node + while !cur.nil? + # Target node is in cur's right subtree + if cur.val < num + cur = cur.right + # Target node is in cur's left subtree + elsif cur.val > num + cur = cur.left + # Found target node, exit loop + else + break + end + end + + cur + end + + ### Insert node ### + def insert(num) + # If tree is empty, initialize root node + if @root.nil? + @root = TreeNode.new(num) + return + end + + # Loop search, exit after passing leaf node + cur, pre = @root, nil + while !cur.nil? + # Found duplicate node, return directly + return if cur.val == num + + pre = cur + # Insertion position is in cur's right subtree + if cur.val < num + cur = cur.right + # Insertion position is in cur's left subtree + else + cur = cur.left + end + end + + # Insert node + node = TreeNode.new(num) + if pre.val < num + pre.right = node + else + pre.left = node + end + end + + ### Delete node ### + def remove(num) + # If tree is empty, return directly + return if @root.nil? + + # Loop search, exit after passing leaf node + cur, pre = @root, nil + while !cur.nil? + # Found node to delete, exit loop + break if cur.val == num + + pre = cur + # Node to delete is in cur's right subtree + if cur.val < num + cur = cur.right + # Node to delete is in cur's left subtree + else + cur = cur.left + end + end + # If no node to delete, return directly + return if cur.nil? + + # Number of child nodes = 0 or 1 + if cur.left.nil? || cur.right.nil? + # When number of child nodes = 0 / 1, child = null / that child node + child = cur.left || cur.right + # Delete node cur + if cur != @root + if pre.left == cur + pre.left = child + else + pre.right = child + end + else + # If deleted node is root node, reassign root node + @root = child + end + # Number of child nodes = 2 + else + # Get next node of cur in inorder traversal + tmp = cur.right + while !tmp.left.nil? + tmp = tmp.left + end + # Recursively delete node tmp + remove(tmp.val) + # Replace cur with tmp + cur.val = tmp.val + end + end +end + +### Driver Code ### +if __FILE__ == $0 + # Initialize binary search tree + bst = BinarySearchTree.new + nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15] + # Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + nums.each { |num| bst.insert(num) } + puts "\nInitialized binary tree is\n" + print_tree(bst.get_root) + + # Search node + node = bst.search(7) + puts "\nFound node object: #{node}, node value = #{node.val}" + + # Insert node + bst.insert(16) + puts "\nAfter inserting node 16, binary tree is\n" + print_tree(bst.get_root) + + # Remove node + bst.remove(1) + puts "\nAfter removing node 1, binary tree is\n" + print_tree(bst.get_root) + + bst.remove(2) + puts "\nAfter removing node 2, binary tree is\n" + print_tree(bst.get_root) + + bst.remove(4) + puts "\nAfter removing node 4, binary tree is\n" + print_tree(bst.get_root) +end diff --git a/en/codes/ruby/chapter_tree/binary_tree.rb b/en/codes/ruby/chapter_tree/binary_tree.rb new file mode 100644 index 000000000..80c3b1470 --- /dev/null +++ b/en/codes/ruby/chapter_tree/binary_tree.rb @@ -0,0 +1,38 @@ +=begin +File: binary_tree.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Driver Code ### +if __FILE__ == $0 + # Initialize binary tree + # Initialize nodes + n1 = TreeNode.new(1) + n2 = TreeNode.new(2) + n3 = TreeNode.new(3) + n4 = TreeNode.new(4) + n5 = TreeNode.new(5) + # Build references (pointers) between nodes + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 + puts "\nInitialize binary tree\n\n" + print_tree(n1) + + # Insert node P between n1 -> n2 + _p = TreeNode.new(0) + # Insert node _p between n1 -> n2 + n1.left = _p + _p.left = n2 + puts "\nAfter inserting node _p\n\n" + print_tree(n1) + # Remove node + n1.left = n2 + puts "\nAfter deleting node _p\n\n" + print_tree(n1) +end diff --git a/en/codes/ruby/chapter_tree/binary_tree_bfs.rb b/en/codes/ruby/chapter_tree/binary_tree_bfs.rb new file mode 100644 index 000000000..00312e864 --- /dev/null +++ b/en/codes/ruby/chapter_tree/binary_tree_bfs.rb @@ -0,0 +1,36 @@ +=begin +File: binary_tree_bfs.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Level-order traversal ### +def level_order(root) + # Initialize queue, add root node + queue = [root] + # Initialize a list to save the traversal sequence + res = [] + while !queue.empty? + node = queue.shift # Dequeue + res << node.val # Save node value + queue << node.left unless node.left.nil? # Left child node enqueue + queue << node.right unless node.right.nil? # Right child node enqueue + end + res +end + +### Driver Code ### +if __FILE__ == $0 + # Initialize binary tree + # Here we use a function to generate a binary tree directly from an array + root = arr_to_tree([1, 2, 3, 4, 5, 6, 7]) + puts "\nInitialize binary tree\n\n" + print_tree(root) + + # Level-order traversal + res = level_order(root) + puts "\nLevel-order traversal node sequence = #{res}" +end diff --git a/en/codes/ruby/chapter_tree/binary_tree_dfs.rb b/en/codes/ruby/chapter_tree/binary_tree_dfs.rb new file mode 100644 index 000000000..3e480f1c5 --- /dev/null +++ b/en/codes/ruby/chapter_tree/binary_tree_dfs.rb @@ -0,0 +1,62 @@ +=begin +File: binary_tree_dfs.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Pre-order traversal ### +def pre_order(root) + return if root.nil? + + # Visit priority: root node -> left subtree -> right subtree + $res << root.val + pre_order(root.left) + pre_order(root.right) +end + +### In-order traversal ### +def in_order(root) + return if root.nil? + + # Visit priority: left subtree -> root node -> right subtree + in_order(root.left) + $res << root.val + in_order(root.right) +end + +### Post-order traversal ### +def post_order(root) + return if root.nil? + + # Visit priority: left subtree -> right subtree -> root node + post_order(root.left) + post_order(root.right) + $res << root.val +end + +### Driver Code ### +if __FILE__ == $0 + # Initialize binary tree + # Here we use a function to generate a binary tree directly from an array + root = arr_to_tree([1, 2, 3, 4, 5, 6, 7]) + puts "\nInitialize binary tree\n\n" + print_tree(root) + + # Preorder traversal + $res = [] + pre_order(root) + puts "\nPre-order traversal node sequence = #{$res}" + + # Inorder traversal + $res.clear + in_order(root) + puts "\nIn-order traversal node sequence = #{$res}" + + # Postorder traversal + $res.clear + post_order(root) + puts "\nPost-order traversal node sequence = #{$res}" +end diff --git a/en/codes/ruby/test_all.rb b/en/codes/ruby/test_all.rb new file mode 100644 index 000000000..a4d417800 --- /dev/null +++ b/en/codes/ruby/test_all.rb @@ -0,0 +1,23 @@ +require 'open3' + +start_time = Time.now +ruby_code_dir = File.dirname(__FILE__) +files = Dir.glob("#{ruby_code_dir}/chapter_*/*.rb") + +errors = [] + +files.each do |file| + stdout, stderr, status = Open3.capture3("ruby #{file}") + errors << stderr unless status.success? +end + +puts "\x1b[34mTested #{files.count} files\x1b[m" + +unless errors.empty? + puts "\x1b[33mFound exception in #{errors.length} files\x1b[m" + raise errors.join("\n\n") +else + puts "\x1b[32mPASS\x1b[m" +end + +puts "Testing finishes after #{((Time.now - start_time) * 1000).round} ms" diff --git a/en/codes/ruby/utils/list_node.rb b/en/codes/ruby/utils/list_node.rb new file mode 100644 index 000000000..9912998bd --- /dev/null +++ b/en/codes/ruby/utils/list_node.rb @@ -0,0 +1,38 @@ +=begin +File: list_node.rb +Created Time: 2024-03-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Linked list node class ### +class ListNode + attr_accessor :val # Node value + attr_accessor :next # Reference to next node + + def initialize(val=0, next_node=nil) + @val = val + @next = next_node + end +end + +### Deserialize list to linked list ### +def arr_to_linked_list(arr) + head = current = ListNode.new(arr[0]) + + for i in 1...arr.length + current.next = ListNode.new(arr[i]) + current = current.next + end + + head +end + +### Serialize linked list to list ### +def linked_list_to_arr(head) + arr = [] + + while head + arr << head.val + head = head.next + end +end diff --git a/en/codes/ruby/utils/print_util.rb b/en/codes/ruby/utils/print_util.rb new file mode 100644 index 000000000..ab3f8f657 --- /dev/null +++ b/en/codes/ruby/utils/print_util.rb @@ -0,0 +1,80 @@ +=begin +File: print_util.rb +Created Time: 2024-03-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative "./tree_node" + +### Print matrix ### +def print_matrix(mat) + s = [] + mat.each { |arr| s << " #{arr.to_s}" } + puts "[\n#{s.join(",\n")}\n]" +end + +### Print linked list ### +def print_linked_list(head) + list = [] + while head + list << head.val + head = head.next + end + puts "#{list.join(" -> ")}" +end + +class Trunk + attr_accessor :prev, :str + + def initialize(prev, str) + @prev = prev + @str = str + end +end + +def show_trunk(p) + return if p.nil? + + show_trunk(p.prev) + print p.str +end + +### Print binary tree ### +# This tree printer is borrowed from TECHIE DELIGHT +# https://www.techiedelight.com/c-program-print-binary-tree/ +def print_tree(root, prev=nil, is_right=false) + return if root.nil? + + prev_str = " " + trunk = Trunk.new(prev, prev_str) + print_tree(root.right, trunk, true) + + if prev.nil? + trunk.str = "———" + elsif is_right + trunk.str = "/———" + prev_str = " |" + else + trunk.str = "\\———" + prev.str = prev_str + end + + show_trunk(trunk) + puts " #{root.val}" + prev.str = prev_str if prev + trunk.str = " |" + print_tree(root.left, trunk, false) +end + +### Print hash table ### +def print_hash_map(hmap) + hmap.entries.each { |key, value| puts "#{key} -> #{value}" } +end + +### Print heap ### +def print_heap(heap) + puts "Array representation of heap: #{heap}" + puts "Heap tree representation:" + root = arr_to_tree(heap) + print_tree(root) +end diff --git a/en/codes/ruby/utils/tree_node.rb b/en/codes/ruby/utils/tree_node.rb new file mode 100644 index 000000000..862415325 --- /dev/null +++ b/en/codes/ruby/utils/tree_node.rb @@ -0,0 +1,53 @@ +=begin +File: tree_node.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Binary tree node class ### +class TreeNode + attr_accessor :val # Node value + attr_accessor :height # Node height + attr_accessor :left # Reference to left child node + attr_accessor :right # Reference to right child node + + def initialize(val=0) + @val = val + @height = 0 + end +end + +### Deserialize list to binary tree: recursion ### +def arr_to_tree_dfs(arr, i) + # Return nil if index exceeds array length or element is nil + return if i < 0 || i >= arr.length || arr[i].nil? + # Build the current node + root = TreeNode.new(arr[i]) + # Recursively build the left and right subtrees + root.left = arr_to_tree_dfs(arr, 2 * i + 1) + root.right = arr_to_tree_dfs(arr, 2 * i + 2) + root +end + +### Deserialize list to binary tree ### +def arr_to_tree(arr) + arr_to_tree_dfs(arr, 0) +end + +### Serialize binary tree to list: recursion ### +def tree_to_arr_dfs(root, i, res) + return if root.nil? + + res += Array.new(i - res.length + 1) if i >= res.length + res[i] = root.val + + tree_to_arr_dfs(root.left, 2 * i + 1, res) + tree_to_arr_dfs(root.right, 2 * i + 2, res) +end + +### Serialize binary tree to list ### +def tree_to_arr(root) + res = [] + tree_to_arr_dfs(root, 0, res) + res +end diff --git a/en/codes/ruby/utils/vertex.rb b/en/codes/ruby/utils/vertex.rb new file mode 100644 index 000000000..c340f5ea6 --- /dev/null +++ b/en/codes/ruby/utils/vertex.rb @@ -0,0 +1,24 @@ +=begin +File: vertex.rb +Created Time: 2024-04-25 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### Vertex class ### +class Vertex + attr_accessor :val + + def initialize(val) + @val = val + end +end + +### Input value list vals, return vertex list vets ### +def vals_to_vets(vals) + Array.new(vals.length) { |i| Vertex.new(vals[i]) } +end + +### Input vertex list vets, return value list vals ### +def vets_to_vals(vets) + Array.new(vets.length) { |i| vets[i].val } +end diff --git a/en/codes/rust/.gitignore b/en/codes/rust/.gitignore new file mode 100644 index 000000000..447098846 --- /dev/null +++ b/en/codes/rust/.gitignore @@ -0,0 +1,2 @@ +target/ +Cargo.lock \ No newline at end of file diff --git a/en/codes/rust/Cargo.toml b/en/codes/rust/Cargo.toml new file mode 100644 index 000000000..160f9134c --- /dev/null +++ b/en/codes/rust/Cargo.toml @@ -0,0 +1,413 @@ +[package] +name = "hello-algo-rust" +version = "0.1.0" +edition = "2021" +publish = false + +# Run Command: cargo run --bin time_complexity +[[bin]] +name = "time_complexity" +path = "chapter_computational_complexity/time_complexity.rs" + +# Run Command: cargo run --bin worst_best_time_complexity +[[bin]] +name = "worst_best_time_complexity" +path = "chapter_computational_complexity/worst_best_time_complexity.rs" + +# Run Command: cargo run --bin space_complexity +[[bin]] +name = "space_complexity" +path = "chapter_computational_complexity/space_complexity.rs" + +# Run Command: cargo run --bin iteration +[[bin]] +name = "iteration" +path = "chapter_computational_complexity/iteration.rs" + +# Run Command: cargo run --bin recursion +[[bin]] +name = "recursion" +path = "chapter_computational_complexity/recursion.rs" + +# Run Command: cargo run --bin two_sum +[[bin]] +name = "two_sum" +path = "chapter_searching/two_sum.rs" + +# Run Command: cargo run --bin array +[[bin]] +name = "array" +path = "chapter_array_and_linkedlist/array.rs" + +# Run Command: cargo run --bin linked_list +[[bin]] +name = "linked_list" +path = "chapter_array_and_linkedlist/linked_list.rs" + +# Run Command: cargo run --bin list +[[bin]] +name = "list" +path = "chapter_array_and_linkedlist/list.rs" + +# Run Command: cargo run --bin my_list +[[bin]] +name = "my_list" +path = "chapter_array_and_linkedlist/my_list.rs" + +# Run Command: cargo run --bin stack +[[bin]] +name = "stack" +path = "chapter_stack_and_queue/stack.rs" + +# Run Command: cargo run --bin linkedlist_stack +[[bin]] +name = "linkedlist_stack" +path = "chapter_stack_and_queue/linkedlist_stack.rs" + +# Run Command: cargo run --bin queue +[[bin]] +name = "queue" +path = "chapter_stack_and_queue/queue.rs" + +# Run Command: cargo run --bin linkedlist_queue +[[bin]] +name = "linkedlist_queue" +path = "chapter_stack_and_queue/linkedlist_queue.rs" + +# Run Command: cargo run --bin deque +[[bin]] +name = "deque" +path = "chapter_stack_and_queue/deque.rs" + +# Run Command: cargo run --bin array_deque +[[bin]] +name = "array_deque" +path = "chapter_stack_and_queue/array_deque.rs" + +# Run Command: cargo run --bin linkedlist_deque +[[bin]] +name = "linkedlist_deque" +path = "chapter_stack_and_queue/linkedlist_deque.rs" + +# Run Command: cargo run --bin simple_hash +[[bin]] +name = "simple_hash" +path = "chapter_hashing/simple_hash.rs" + +# Run Command: cargo run --bin hash_map +[[bin]] +name = "hash_map" +path = "chapter_hashing/hash_map.rs" + +# Run Command: cargo run --bin array_hash_map +[[bin]] +name = "array_hash_map" +path = "chapter_hashing/array_hash_map.rs" + +# Run Command: cargo run --bin build_in_hash +[[bin]] +name = "build_in_hash" +path = "chapter_hashing/build_in_hash.rs" + +# Run Command: cargo run --bin hash_map_chaining +[[bin]] +name = "hash_map_chaining" +path = "chapter_hashing/hash_map_chaining.rs" + +# Run Command: cargo run --bin hash_map_open_addressing +[[bin]] +name = "hash_map_open_addressing" +path = "chapter_hashing/hash_map_open_addressing.rs" + +# Run Command: cargo run --bin binary_search +[[bin]] +name = "binary_search" +path = "chapter_searching/binary_search.rs" + +# Run Command: cargo run --bin binary_search_edge +[[bin]] +name = "binary_search_edge" +path = "chapter_searching/binary_search_edge.rs" + +# Run Command: cargo run --bin binary_search_insertion +[[bin]] +name = "binary_search_insertion" +path = "chapter_searching/binary_search_insertion.rs" + +# Run Command: cargo run --bin bubble_sort +[[bin]] +name = "bubble_sort" +path = "chapter_sorting/bubble_sort.rs" + +# Run Command: cargo run --bin insertion_sort +[[bin]] +name = "insertion_sort" +path = "chapter_sorting/insertion_sort.rs" + +# Run Command: cargo run --bin quick_sort +[[bin]] +name = "quick_sort" +path = "chapter_sorting/quick_sort.rs" + +# Run Command: cargo run --bin merge_sort +[[bin]] +name = "merge_sort" +path = "chapter_sorting/merge_sort.rs" + +# Run Command: cargo run --bin selection_sort +[[bin]] +name = "selection_sort" +path = "chapter_sorting/selection_sort.rs" + +# Run Command: cargo run --bin bucket_sort +[[bin]] +name = "bucket_sort" +path = "chapter_sorting/bucket_sort.rs" + +# Run Command: cargo run --bin heap_sort +[[bin]] +name = "heap_sort" +path = "chapter_sorting/heap_sort.rs" + +# Run Command: cargo run --bin counting_sort +[[bin]] +name = "counting_sort" +path = "chapter_sorting/counting_sort.rs" + +# Run Command: cargo run --bin radix_sort +[[bin]] +name = "radix_sort" +path = "chapter_sorting/radix_sort.rs" + +# Run Command: cargo run --bin array_stack +[[bin]] +name = "array_stack" +path = "chapter_stack_and_queue/array_stack.rs" + +# Run Command: cargo run --bin array_queue +[[bin]] +name = "array_queue" +path = "chapter_stack_and_queue/array_queue.rs" + +# Run Command: cargo run --bin array_binary_tree +[[bin]] +name = "array_binary_tree" +path = "chapter_tree/array_binary_tree.rs" + +# Run Command: cargo run --bin avl_tree +[[bin]] +name = "avl_tree" +path = "chapter_tree/avl_tree.rs" + +# Run Command: cargo run --bin binary_search_tree +[[bin]] +name = "binary_search_tree" +path = "chapter_tree/binary_search_tree.rs" + +# Run Command: cargo run --bin binary_tree_bfs +[[bin]] +name = "binary_tree_bfs" +path = "chapter_tree/binary_tree_bfs.rs" + +# Run Command: cargo run --bin binary_tree_dfs +[[bin]] +name = "binary_tree_dfs" +path = "chapter_tree/binary_tree_dfs.rs" + +# Run Command: cargo run --bin binary_tree +[[bin]] +name = "binary_tree" +path = "chapter_tree/binary_tree.rs" + +# Run Command: cargo run --bin heap +[[bin]] +name = "heap" +path = "chapter_heap/heap.rs" + +# Run Command: cargo run --bin my_heap +[[bin]] +name = "my_heap" +path = "chapter_heap/my_heap.rs" + +# Run Command: cargo run --bin top_k +[[bin]] +name = "top_k" +path = "chapter_heap/top_k.rs" + +# Run Command: cargo run --bin graph_adjacency_list +[[bin]] +name = "graph_adjacency_list" +path = "chapter_graph/graph_adjacency_list.rs" + +# Run Command: cargo run --bin graph_adjacency_matrix +[[bin]] +name = "graph_adjacency_matrix" +path = "chapter_graph/graph_adjacency_matrix.rs" + +# Run Command: cargo run --bin graph_bfs +[[bin]] +name = "graph_bfs" +path = "chapter_graph/graph_bfs.rs" + +# Run Command: cargo run --bin graph_dfs +[[bin]] +name = "graph_dfs" +path = "chapter_graph/graph_dfs.rs" + +# Run Command: cargo run --bin linear_search +[[bin]] +name = "linear_search" +path = "chapter_searching/linear_search.rs" + +# Run Command: cargo run --bin hashing_search +[[bin]] +name = "hashing_search" +path = "chapter_searching/hashing_search.rs" + +# Run Command: cargo run --bin climbing_stairs_dfs +[[bin]] +name = "climbing_stairs_dfs" +path = "chapter_dynamic_programming/climbing_stairs_dfs.rs" + +# Run Command: cargo run --bin climbing_stairs_dfs_mem +[[bin]] +name = "climbing_stairs_dfs_mem" +path = "chapter_dynamic_programming/climbing_stairs_dfs_mem.rs" + +# Run Command: cargo run --bin climbing_stairs_dp +[[bin]] +name = "climbing_stairs_dp" +path = "chapter_dynamic_programming/climbing_stairs_dp.rs" + +# Run Command: cargo run --bin min_cost_climbing_stairs_dp +[[bin]] +name = "min_cost_climbing_stairs_dp" +path = "chapter_dynamic_programming/min_cost_climbing_stairs_dp.rs" + +# Run Command: cargo run --bin climbing_stairs_constraint_dp +[[bin]] +name = "climbing_stairs_constraint_dp" +path = "chapter_dynamic_programming/climbing_stairs_constraint_dp.rs" + +# Run Command: cargo run --bin climbing_stairs_backtrack +[[bin]] +name = "climbing_stairs_backtrack" +path = "chapter_dynamic_programming/climbing_stairs_backtrack.rs" + +# Run Command: cargo run --bin subset_sum_i_naive +[[bin]] +name = "subset_sum_i_naive" +path = "chapter_backtracking/subset_sum_i_naive.rs" + +# Run Command: cargo run --bin subset_sum_i +[[bin]] +name = "subset_sum_i" +path = "chapter_backtracking/subset_sum_i.rs" + +# Run Command: cargo run --bin subset_sum_ii +[[bin]] +name = "subset_sum_ii" +path = "chapter_backtracking/subset_sum_ii.rs" + +# Run Command: cargo run --bin coin_change +[[bin]] +name = "coin_change" +path = "chapter_dynamic_programming/coin_change.rs" + +# Run Command: cargo run --bin coin_change_ii +[[bin]] +name = "coin_change_ii" +path = "chapter_dynamic_programming/coin_change_ii.rs" + +# Run Command: cargo run --bin unbounded_knapsack +[[bin]] +name = "unbounded_knapsack" +path = "chapter_dynamic_programming/unbounded_knapsack.rs" + +# Run Command: cargo run --bin knapsack +[[bin]] +name = "knapsack" +path = "chapter_dynamic_programming/knapsack.rs" + +# Run Command: cargo run --bin min_path_sum +[[bin]] +name = "min_path_sum" +path = "chapter_dynamic_programming/min_path_sum.rs" + +# Run Command: cargo run --bin edit_distance +[[bin]] +name = "edit_distance" +path = "chapter_dynamic_programming/edit_distance.rs" + +# Run Command: cargo run --bin n_queens +[[bin]] +name = "n_queens" +path = "chapter_backtracking/n_queens.rs" + +# Run Command: cargo run --bin permutations_i +[[bin]] +name = "permutations_i" +path = "chapter_backtracking/permutations_i.rs" + +# Run Command: cargo run --bin permutations_ii +[[bin]] +name = "permutations_ii" +path = "chapter_backtracking/permutations_ii.rs" + +# Run Command: cargo run --bin preorder_traversal_i_compact +[[bin]] +name = "preorder_traversal_i_compact" +path = "chapter_backtracking/preorder_traversal_i_compact.rs" + +# Run Command: cargo run --bin preorder_traversal_ii_compact +[[bin]] +name = "preorder_traversal_ii_compact" +path = "chapter_backtracking/preorder_traversal_ii_compact.rs" + +# Run Command: cargo run --bin preorder_traversal_iii_compact +[[bin]] +name = "preorder_traversal_iii_compact" +path = "chapter_backtracking/preorder_traversal_iii_compact.rs" + +# Run Command: cargo run --bin preorder_traversal_iii_template +[[bin]] +name = "preorder_traversal_iii_template" +path = "chapter_backtracking/preorder_traversal_iii_template.rs" + +# Run Command: cargo run --bin binary_search_recur +[[bin]] +name = "binary_search_recur" +path = "chapter_divide_and_conquer/binary_search_recur.rs" + +# Run Command: cargo run --bin hanota +[[bin]] +name = "hanota" +path = "chapter_divide_and_conquer/hanota.rs" + +# Run Command: cargo run --bin build_tree +[[bin]] +name = "build_tree" +path = "chapter_divide_and_conquer/build_tree.rs" + +# Run Command: cargo run --bin coin_change_greedy +[[bin]] +name = "coin_change_greedy" +path = "chapter_greedy/coin_change_greedy.rs" + +# Run Command: cargo run --bin fractional_knapsack +[[bin]] +name = "fractional_knapsack" +path = "chapter_greedy/fractional_knapsack.rs" + +# Run Command: cargo run --bin max_capacity +[[bin]] +name = "max_capacity" +path = "chapter_greedy/max_capacity.rs" + +# Run Command: cargo run --bin max_product_cutting +[[bin]] +name = "max_product_cutting" +path = "chapter_greedy/max_product_cutting.rs" + +[dependencies] +rand = "0.8.5" diff --git a/en/codes/rust/chapter_array_and_linkedlist/array.rs b/en/codes/rust/chapter_array_and_linkedlist/array.rs new file mode 100644 index 000000000..288aadd6b --- /dev/null +++ b/en/codes/rust/chapter_array_and_linkedlist/array.rs @@ -0,0 +1,111 @@ +/* + * File: array.rs + * Created Time: 2023-01-15 + * Author: xBLACICEx (xBLACKICEx@outlook.com), codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::print_util; +use rand::Rng; + +/* Random access to element */ +fn random_access(nums: &[i32]) -> i32 { + // Randomly select a number in interval [0, nums.len()) + let random_index = rand::thread_rng().gen_range(0..nums.len()); + // Retrieve and return the random element + let random_num = nums[random_index]; + random_num +} + +/* Extend array length */ +fn extend(nums: &[i32], enlarge: usize) -> Vec { + // Initialize an array with extended length + let mut res: Vec = vec![0; nums.len() + enlarge]; + // Copy all elements from original array to new + res[0..nums.len()].copy_from_slice(nums); + + // Return the extended new array + res +} + +/* Insert element num at index index in the array */ +fn insert(nums: &mut [i32], num: i32, index: usize) { + // Move all elements at and after index index backward by one position + for i in (index + 1..nums.len()).rev() { + nums[i] = nums[i - 1]; + } + // Assign num to the element at index index + nums[index] = num; +} + +/* Remove the element at index index */ +fn remove(nums: &mut [i32], index: usize) { + // Move all elements after index index forward by one position + for i in index..nums.len() - 1 { + nums[i] = nums[i + 1]; + } +} + +/* Traverse array */ +fn traverse(nums: &[i32]) { + let mut _count = 0; + // Traverse array by index + for i in 0..nums.len() { + _count += nums[i]; + } + // Direct traversal of array elements + _count = 0; + for &num in nums { + _count += num; + } +} + +/* Find the specified element in the array */ +fn find(nums: &[i32], target: i32) -> Option { + for i in 0..nums.len() { + if nums[i] == target { + return Some(i); + } + } + None +} + +/* Driver Code */ +fn main() { + /* Initialize array */ + let arr: [i32; 5] = [0; 5]; + print!("Array arr = "); + print_util::print_array(&arr); + // In Rust, specifying length ([i32; 5]) is an array, without length (&[i32]) is a slice + // Since Rust arrays are designed to have compile-time determined length, only constants can specify length + // Vector is the type Rust generally uses as a dynamic array + // To facilitate implementing the extend() method, the following treats vector as array + let nums: Vec = vec![1, 3, 2, 5, 4]; + print!("\nArray nums = "); + print_util::print_array(&nums); + + // Insert element + let random_num = random_access(&nums); + println!("\nGet random element {} from nums", random_num); + + // Traverse array + let mut nums: Vec = extend(&nums, 3); + print!("Extend array length to 8, resulting in nums = "); + print_util::print_array(&nums); + + // Insert element + insert(&mut nums, 6, 3); + print!("\nInsert number 6 at index 3, get nums = "); + print_util::print_array(&nums); + + // Remove element + remove(&mut nums, 2); + print!("\nDelete element at index 2, get nums = "); + print_util::print_array(&nums); + + // Traverse array + traverse(&nums); + + // Find element + let index = find(&nums, 3).unwrap(); + println!("\nFind element 3 in nums, index = {}", index); +} diff --git a/en/codes/rust/chapter_array_and_linkedlist/linked_list.rs b/en/codes/rust/chapter_array_and_linkedlist/linked_list.rs new file mode 100644 index 000000000..b4809bd7e --- /dev/null +++ b/en/codes/rust/chapter_array_and_linkedlist/linked_list.rs @@ -0,0 +1,100 @@ +/* + * File: linked_list.rs + * Created Time: 2023-03-05 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::{print_util, ListNode}; +use std::cell::RefCell; +use std::rc::Rc; + +/* Insert node P after node n0 in the linked list */ +#[allow(non_snake_case)] +pub fn insert(n0: &Rc>>, P: Rc>>) { + let n1 = n0.borrow_mut().next.take(); + P.borrow_mut().next = n1; + n0.borrow_mut().next = Some(P); +} + +/* Remove the first node after node n0 in the linked list */ +#[allow(non_snake_case)] +pub fn remove(n0: &Rc>>) { + // n0 -> P -> n1 + let P = n0.borrow_mut().next.take(); + if let Some(node) = P { + let n1 = node.borrow_mut().next.take(); + n0.borrow_mut().next = n1; + } +} + +/* Access the node at index index in the linked list */ +pub fn access(head: Rc>>, index: i32) -> Option>>> { + fn dfs( + head: Option<&Rc>>>, + index: i32, + ) -> Option>>> { + if index <= 0 { + return head.cloned(); + } + + if let Some(node) = head { + dfs(node.borrow().next.as_ref(), index - 1) + } else { + None + } + } + + dfs(Some(head).as_ref(), index) +} + +/* Find the first node with value target in the linked list */ +pub fn find(head: Rc>>, target: T) -> i32 { + fn find(head: Option<&Rc>>>, target: T, idx: i32) -> i32 { + if let Some(node) = head { + if node.borrow().val == target { + return idx; + } + return find(node.borrow().next.as_ref(), target, idx + 1); + } else { + -1 + } + } + + find(Some(head).as_ref(), target, 0) +} + +/* Driver Code */ +fn main() { + /* Initialize linked list */ + // Initialize each node + let n0 = ListNode::new(1); + let n1 = ListNode::new(3); + let n2 = ListNode::new(2); + let n3 = ListNode::new(5); + let n4 = ListNode::new(4); + // Build references between nodes + n0.borrow_mut().next = Some(n1.clone()); + n1.borrow_mut().next = Some(n2.clone()); + n2.borrow_mut().next = Some(n3.clone()); + n3.borrow_mut().next = Some(n4.clone()); + print!("Initialized linked list is "); + print_util::print_linked_list(&n0); + + /* Insert node */ + insert(&n0, ListNode::new(0)); + print!("After inserting node, linked list is "); + print_util::print_linked_list(&n0); + + /* Remove node */ + remove(&n0); + print!("After deleting node, linked list is "); + print_util::print_linked_list(&n0); + + /* Access node */ + let node = access(n0.clone(), 3); + println!("Value of node at index 3 in linked list = {}", node.unwrap().borrow().val); + + /* Search node */ + let index = find(n0.clone(), 2); + println!("Index of node with value 2 in linked list = {}", index); +} diff --git a/en/codes/rust/chapter_array_and_linkedlist/list.rs b/en/codes/rust/chapter_array_and_linkedlist/list.rs new file mode 100644 index 000000000..3a367c78c --- /dev/null +++ b/en/codes/rust/chapter_array_and_linkedlist/list.rs @@ -0,0 +1,71 @@ +/* + * File: list.rs + * Created Time: 2023-01-18 + * Author: xBLACICEx (xBLACKICEx@outlook.com), codingonion (coderonion@gmail.com) + */ +use hello_algo_rust::include::print_util; + +/* Driver Code */ +fn main() { + // Initialize list + let mut nums: Vec = vec![1, 3, 2, 5, 4]; + print!("List nums = "); + print_util::print_array(&nums); + + // Update element + let num = nums[1]; + println!("\nAccess element at index 1, get num = {num}"); + + // Add elements at the end + nums[1] = 0; + print!("Update element at index 1 to 0, resulting in nums = "); + print_util::print_array(&nums); + + // Remove element + nums.clear(); + print!("\nAfter clearing list, nums = "); + print_util::print_array(&nums); + + // Direct traversal of list elements + nums.push(1); + nums.push(3); + nums.push(2); + nums.push(5); + nums.push(4); + print!("\nAfter adding elements, nums = "); + print_util::print_array(&nums); + + // Sort list + nums.insert(3, 6); + print!("\nInsert number 6 at index 3, get nums = "); + print_util::print_array(&nums); + + // Remove element + nums.remove(3); + print!("\nDelete element at index 3, get nums = "); + print_util::print_array(&nums); + + // Traverse list by index + let mut _count = 0; + for i in 0..nums.len() { + _count += nums[i]; + } + // Directly traverse list elements + _count = 0; + for x in &nums { + _count += x; + } + + // Concatenate two lists + let mut nums1 = vec![6, 8, 7, 10, 9]; + nums.append(&mut nums1); // After append (move), nums1 is empty! + + // nums.extend(&nums1); // extend (borrow) allows nums1 to continue being used + print!("\nAfter concatenating list nums1 to nums, get nums = "); + print_util::print_array(&nums); + + // Sort list + nums.sort(); + print!("\nAfter sorting list, nums = "); + print_util::print_array(&nums); +} diff --git a/en/codes/rust/chapter_array_and_linkedlist/my_list.rs b/en/codes/rust/chapter_array_and_linkedlist/my_list.rs new file mode 100644 index 000000000..33d35c088 --- /dev/null +++ b/en/codes/rust/chapter_array_and_linkedlist/my_list.rs @@ -0,0 +1,164 @@ +/* + * File: my_list.rs + * Created Time: 2023-03-11 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::print_util; + +/* List class */ +#[allow(dead_code)] +struct MyList { + arr: Vec, // Array (stores list elements) + capacity: usize, // List capacity + size: usize, // List length (current number of elements) + extend_ratio: usize, // Multiple by which the list capacity is extended each time +} + +#[allow(unused, unused_comparisons)] +impl MyList { + /* Constructor */ + pub fn new(capacity: usize) -> Self { + let mut vec = vec![0; capacity]; + Self { + arr: vec, + capacity, + size: 0, + extend_ratio: 2, + } + } + + /* Get list length (current number of elements) */ + pub fn size(&self) -> usize { + return self.size; + } + + /* Get list capacity */ + pub fn capacity(&self) -> usize { + return self.capacity; + } + + /* Update element */ + pub fn get(&self, index: usize) -> i32 { + // If the index is out of bounds, throw an exception, as below + if index >= self.size { + panic!("Index out of bounds") + }; + return self.arr[index]; + } + + /* Add elements at the end */ + pub fn set(&mut self, index: usize, num: i32) { + if index >= self.size { + panic!("Index out of bounds") + }; + self.arr[index] = num; + } + + /* Direct traversal of list elements */ + pub fn add(&mut self, num: i32) { + // When the number of elements exceeds capacity, trigger the extension mechanism + if self.size == self.capacity() { + self.extend_capacity(); + } + self.arr[self.size] = num; + // Update the number of elements + self.size += 1; + } + + /* Sort list */ + pub fn insert(&mut self, index: usize, num: i32) { + if index >= self.size() { + panic!("Index out of bounds") + }; + // When the number of elements exceeds capacity, trigger the extension mechanism + if self.size == self.capacity() { + self.extend_capacity(); + } + // Move all elements after index index forward by one position + for j in (index..self.size).rev() { + self.arr[j + 1] = self.arr[j]; + } + self.arr[index] = num; + // Update the number of elements + self.size += 1; + } + + /* Remove element */ + pub fn remove(&mut self, index: usize) -> i32 { + if index >= self.size() { + panic!("Index out of bounds") + }; + let num = self.arr[index]; + // Create a new array with length _extend_ratio times the original array, and copy the original array to the new array + for j in index..self.size - 1 { + self.arr[j] = self.arr[j + 1]; + } + // Update the number of elements + self.size -= 1; + // Return the removed element + return num; + } + + /* Driver Code */ + pub fn extend_capacity(&mut self) { + // Create new array with length extend_ratio times original, copy original array to new array + let new_capacity = self.capacity * self.extend_ratio; + self.arr.resize(new_capacity, 0); + // Add elements at the end + self.capacity = new_capacity; + } + + /* Convert list to array */ + pub fn to_array(&self) -> Vec { + // Elements enqueue + let mut arr = Vec::new(); + for i in 0..self.size { + arr.push(self.get(i)); + } + arr + } +} + +/* Driver Code */ +fn main() { + /* Initialize list */ + let mut nums = MyList::new(10); + /* Direct traversal of list elements */ + nums.add(1); + nums.add(3); + nums.add(2); + nums.add(5); + nums.add(4); + print!("List nums = "); + print_util::print_array(&nums.to_array()); + print!(", capacity = {}, length = {}", nums.capacity(), nums.size()); + + /* Sort list */ + nums.insert(3, 6); + print!("\nInsert number 6 at index 3, get nums = "); + print_util::print_array(&nums.to_array()); + + /* Remove element */ + nums.remove(3); + print!("\nDelete element at index 3, get nums = "); + print_util::print_array(&nums.to_array()); + + /* Update element */ + let num = nums.get(1); + println!("\nAccess element at index 1, get num = {num}"); + + /* Add elements at the end */ + nums.set(1, 0); + print!("Update element at index 1 to 0, resulting in nums = "); + print_util::print_array(&nums.to_array()); + + /* Test capacity expansion mechanism */ + for i in 0..10 { + // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + nums.add(i); + } + print!("\nAfter expanding list, nums = "); + print_util::print_array(&nums.to_array()); + print!(", capacity = {}, length = {}", nums.capacity(), nums.size()); +} diff --git a/en/codes/rust/chapter_backtracking/n_queens.rs b/en/codes/rust/chapter_backtracking/n_queens.rs new file mode 100644 index 000000000..26d489ab2 --- /dev/null +++ b/en/codes/rust/chapter_backtracking/n_queens.rs @@ -0,0 +1,76 @@ +/* + * File: n_queens.rs + * Created Time: 2023-07-15 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Backtracking algorithm: N queens */ +fn backtrack( + row: usize, + n: usize, + state: &mut Vec>, + res: &mut Vec>>, + cols: &mut [bool], + diags1: &mut [bool], + diags2: &mut [bool], +) { + // When all rows are placed, record the solution + if row == n { + res.push(state.clone()); + return; + } + // Traverse all columns + for col in 0..n { + // Calculate the main diagonal and anti-diagonal corresponding to this cell + let diag1 = row + n - 1 - col; + let diag2 = row + col; + // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + if !cols[col] && !diags1[diag1] && !diags2[diag2] { + // Attempt: place the queen in this cell + state[row][col] = "Q".into(); + (cols[col], diags1[diag1], diags2[diag2]) = (true, true, true); + // Place the next row + backtrack(row + 1, n, state, res, cols, diags1, diags2); + // Backtrack: restore this cell to an empty cell + state[row][col] = "#".into(); + (cols[col], diags1[diag1], diags2[diag2]) = (false, false, false); + } + } +} + +/* Solve N queens */ +fn n_queens(n: usize) -> Vec>> { + // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + let mut state: Vec> = vec![vec!["#".to_string(); n]; n]; + let mut cols = vec![false; n]; // Record whether there is a queen in the column + let mut diags1 = vec![false; 2 * n - 1]; // Record whether there is a queen on the main diagonal + let mut diags2 = vec![false; 2 * n - 1]; // Record whether there is a queen on the anti-diagonal + let mut res: Vec>> = Vec::new(); + + backtrack( + 0, + n, + &mut state, + &mut res, + &mut cols, + &mut diags1, + &mut diags2, + ); + + res +} + +/* Driver Code */ +pub fn main() { + let n: usize = 4; + let res = n_queens(n); + + println!("Input board size is {n}"); + println!("Total queen placement solutions: {}", res.len()); + for state in res.iter() { + println!("--------------------"); + for row in state.iter() { + println!("{:?}", row); + } + } +} diff --git a/en/codes/rust/chapter_backtracking/permutations_i.rs b/en/codes/rust/chapter_backtracking/permutations_i.rs new file mode 100644 index 000000000..5525d2dad --- /dev/null +++ b/en/codes/rust/chapter_backtracking/permutations_i.rs @@ -0,0 +1,46 @@ +/* + * File: permutations_i.rs + * Created Time: 2023-07-15 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Backtracking algorithm: Permutations I */ +fn backtrack(mut state: Vec, choices: &[i32], selected: &mut [bool], res: &mut Vec>) { + // When the state length equals the number of elements, record the solution + if state.len() == choices.len() { + res.push(state); + return; + } + // Traverse all choices + for i in 0..choices.len() { + let choice = choices[i]; + // Pruning: do not allow repeated selection of elements + if !selected[i] { + // Attempt: make choice, update state + selected[i] = true; + state.push(choice); + // Proceed to the next round of selection + backtrack(state.clone(), choices, selected, res); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + state.pop(); + } + } +} + +/* Permutations I */ +fn permutations_i(nums: &mut [i32]) -> Vec> { + let mut res = Vec::new(); // State (subset) + backtrack(Vec::new(), nums, &mut vec![false; nums.len()], &mut res); + res +} + +/* Driver Code */ +pub fn main() { + let mut nums = [1, 2, 3]; + + let res = permutations_i(&mut nums); + + println!("Input array nums = {:?}", &nums); + println!("All permutations res = {:?}", &res); +} diff --git a/en/codes/rust/chapter_backtracking/permutations_ii.rs b/en/codes/rust/chapter_backtracking/permutations_ii.rs new file mode 100644 index 000000000..7cf2cd470 --- /dev/null +++ b/en/codes/rust/chapter_backtracking/permutations_ii.rs @@ -0,0 +1,50 @@ +/* + * File: permutations_ii.rs + * Created Time: 2023-07-15 + * Author: codingonion (coderonion@gmail.com) + */ + +use std::collections::HashSet; + +/* Backtracking algorithm: Permutations II */ +fn backtrack(mut state: Vec, choices: &[i32], selected: &mut [bool], res: &mut Vec>) { + // When the state length equals the number of elements, record the solution + if state.len() == choices.len() { + res.push(state); + return; + } + // Traverse all choices + let mut duplicated = HashSet::::new(); + for i in 0..choices.len() { + let choice = choices[i]; + // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if !selected[i] && !duplicated.contains(&choice) { + // Attempt: make choice, update state + duplicated.insert(choice); // Record the selected element value + selected[i] = true; + state.push(choice); + // Proceed to the next round of selection + backtrack(state.clone(), choices, selected, res); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + state.pop(); + } + } +} + +/* Permutations II */ +fn permutations_ii(nums: &mut [i32]) -> Vec> { + let mut res = Vec::new(); + backtrack(Vec::new(), nums, &mut vec![false; nums.len()], &mut res); + res +} + +/* Driver Code */ +pub fn main() { + let mut nums = [1, 2, 2]; + + let res = permutations_ii(&mut nums); + + println!("Input array nums = {:?}", &nums); + println!("All permutations res = {:?}", &res); +} diff --git a/en/codes/rust/chapter_backtracking/preorder_traversal_i_compact.rs b/en/codes/rust/chapter_backtracking/preorder_traversal_i_compact.rs new file mode 100644 index 000000000..1b08d7017 --- /dev/null +++ b/en/codes/rust/chapter_backtracking/preorder_traversal_i_compact.rs @@ -0,0 +1,41 @@ +/* + * File: preorder_traversal_i_compact.rs + * Created Time: 2023-07-15 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode}; +use std::{cell::RefCell, rc::Rc}; + +/* Preorder traversal: Example 1 */ +fn pre_order(res: &mut Vec>>, root: Option<&Rc>>) { + if root.is_none() { + return; + } + if let Some(node) = root { + if node.borrow().val == 7 { + // Record solution + res.push(node.clone()); + } + pre_order(res, node.borrow().left.as_ref()); + pre_order(res, node.borrow().right.as_ref()); + } +} + +/* Driver Code */ +pub fn main() { + let root = vec_to_tree([1, 7, 3, 4, 5, 6, 7].map(|x| Some(x)).to_vec()); + println!("Initialize binary tree"); + print_util::print_tree(root.as_ref().unwrap()); + + // Preorder traversal + let mut res = Vec::new(); + pre_order(&mut res, root.as_ref()); + + println!("\nOutput all nodes with value 7"); + let mut vals = Vec::new(); + for node in res { + vals.push(node.borrow().val) + } + println!("{:?}", vals); +} diff --git a/en/codes/rust/chapter_backtracking/preorder_traversal_ii_compact.rs b/en/codes/rust/chapter_backtracking/preorder_traversal_ii_compact.rs new file mode 100644 index 000000000..61fb43afd --- /dev/null +++ b/en/codes/rust/chapter_backtracking/preorder_traversal_ii_compact.rs @@ -0,0 +1,52 @@ +/* + * File: preorder_traversal_ii_compact.rs + * Created Time: 2023-07-15 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode}; +use std::{cell::RefCell, rc::Rc}; + +/* Preorder traversal: Example 2 */ +fn pre_order( + res: &mut Vec>>>, + path: &mut Vec>>, + root: Option<&Rc>>, +) { + if root.is_none() { + return; + } + if let Some(node) = root { + // Attempt + path.push(node.clone()); + if node.borrow().val == 7 { + // Record solution + res.push(path.clone()); + } + pre_order(res, path, node.borrow().left.as_ref()); + pre_order(res, path, node.borrow().right.as_ref()); + // Backtrack + path.pop(); + } +} + +/* Driver Code */ +pub fn main() { + let root = vec_to_tree([1, 7, 3, 4, 5, 6, 7].map(|x| Some(x)).to_vec()); + println!("Initialize binary tree"); + print_util::print_tree(root.as_ref().unwrap()); + + // Preorder traversal + let mut path = Vec::new(); + let mut res = Vec::new(); + pre_order(&mut res, &mut path, root.as_ref()); + + println!("\nOutput all paths from root node to node 7"); + for path in res { + let mut vals = Vec::new(); + for node in path { + vals.push(node.borrow().val) + } + println!("{:?}", vals); + } +} diff --git a/en/codes/rust/chapter_backtracking/preorder_traversal_iii_compact.rs b/en/codes/rust/chapter_backtracking/preorder_traversal_iii_compact.rs new file mode 100644 index 000000000..faa0340db --- /dev/null +++ b/en/codes/rust/chapter_backtracking/preorder_traversal_iii_compact.rs @@ -0,0 +1,53 @@ +/* + * File: preorder_traversal_iii_compact.rs + * Created Time: 2023-07-15 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode}; +use std::{cell::RefCell, rc::Rc}; + +/* Preorder traversal: Example 3 */ +fn pre_order( + res: &mut Vec>>>, + path: &mut Vec>>, + root: Option<&Rc>>, +) { + // Pruning + if root.is_none() || root.as_ref().unwrap().borrow().val == 3 { + return; + } + if let Some(node) = root { + // Attempt + path.push(node.clone()); + if node.borrow().val == 7 { + // Record solution + res.push(path.clone()); + } + pre_order(res, path, node.borrow().left.as_ref()); + pre_order(res, path, node.borrow().right.as_ref()); + // Backtrack + path.pop(); + } +} + +/* Driver Code */ +pub fn main() { + let root = vec_to_tree([1, 7, 3, 4, 5, 6, 7].map(|x| Some(x)).to_vec()); + println!("Initialize binary tree"); + print_util::print_tree(root.as_ref().unwrap()); + + // Preorder traversal + let mut path = Vec::new(); + let mut res = Vec::new(); + pre_order(&mut res, &mut path, root.as_ref()); + + println!("\nOutput all paths from root node to node 7, paths do not include nodes with value 3"); + for path in res { + let mut vals = Vec::new(); + for node in path { + vals.push(node.borrow().val) + } + println!("{:?}", vals); + } +} diff --git a/en/codes/rust/chapter_backtracking/preorder_traversal_iii_template.rs b/en/codes/rust/chapter_backtracking/preorder_traversal_iii_template.rs new file mode 100644 index 000000000..fd27e6a1d --- /dev/null +++ b/en/codes/rust/chapter_backtracking/preorder_traversal_iii_template.rs @@ -0,0 +1,88 @@ +/* + * File: preorder_traversal_iii_template.rs + * Created Time: 2023-07-15 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode}; +use std::{cell::RefCell, rc::Rc}; + +/* Check if the current state is a solution */ +fn is_solution(state: &mut Vec>>) -> bool { + return !state.is_empty() && state.last().unwrap().borrow().val == 7; +} + +/* Record solution */ +fn record_solution( + state: &mut Vec>>, + res: &mut Vec>>>, +) { + res.push(state.clone()); +} + +/* Check if the choice is valid under the current state */ +fn is_valid(_: &mut Vec>>, choice: Option<&Rc>>) -> bool { + return choice.is_some() && choice.unwrap().borrow().val != 3; +} + +/* Update state */ +fn make_choice(state: &mut Vec>>, choice: Rc>) { + state.push(choice); +} + +/* Restore state */ +fn undo_choice(state: &mut Vec>>, _: Rc>) { + state.pop(); +} + +/* Backtracking algorithm: Example 3 */ +fn backtrack( + state: &mut Vec>>, + choices: &Vec>>>, + res: &mut Vec>>>, +) { + // Check if it is a solution + if is_solution(state) { + // Record solution + record_solution(state, res); + } + // Traverse all choices + for &choice in choices.iter() { + // Pruning: check if the choice is valid + if is_valid(state, choice) { + // Attempt: make choice, update state + make_choice(state, choice.unwrap().clone()); + // Proceed to the next round of selection + backtrack( + state, + &vec![ + choice.unwrap().borrow().left.as_ref(), + choice.unwrap().borrow().right.as_ref(), + ], + res, + ); + // Backtrack: undo choice, restore to previous state + undo_choice(state, choice.unwrap().clone()); + } + } +} + +/* Driver Code */ +pub fn main() { + let root = vec_to_tree([1, 7, 3, 4, 5, 6, 7].map(|x| Some(x)).to_vec()); + println!("Initialize binary tree"); + print_util::print_tree(root.as_ref().unwrap()); + + // Backtracking algorithm + let mut res = Vec::new(); + backtrack(&mut Vec::new(), &mut vec![root.as_ref()], &mut res); + + println!("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3"); + for path in res { + let mut vals = Vec::new(); + for node in path { + vals.push(node.borrow().val) + } + println!("{:?}", vals); + } +} diff --git a/en/codes/rust/chapter_backtracking/subset_sum_i.rs b/en/codes/rust/chapter_backtracking/subset_sum_i.rs new file mode 100644 index 000000000..ce00e53a9 --- /dev/null +++ b/en/codes/rust/chapter_backtracking/subset_sum_i.rs @@ -0,0 +1,56 @@ +/* + * File: subset_sum_i.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Backtracking algorithm: Subset sum I */ +fn backtrack( + state: &mut Vec, + target: i32, + choices: &[i32], + start: usize, + res: &mut Vec>, +) { + // When the subset sum equals target, record the solution + if target == 0 { + res.push(state.clone()); + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + for i in start..choices.len() { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if target - choices[i] < 0 { + break; + } + // Attempt: make choice, update target, start + state.push(choices[i]); + // Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i, res); + // Backtrack: undo choice, restore to previous state + state.pop(); + } +} + +/* Solve subset sum I */ +fn subset_sum_i(nums: &mut [i32], target: i32) -> Vec> { + let mut state = Vec::new(); // State (subset) + nums.sort(); // Sort nums + let start = 0; // Start point for traversal + let mut res = Vec::new(); // Result list (subset list) + backtrack(&mut state, target, nums, start, &mut res); + res +} + +/* Driver Code */ +pub fn main() { + let mut nums = [3, 4, 5]; + let target = 9; + + let res = subset_sum_i(&mut nums, target); + + println!("Input array nums = {:?}, target = {}", &nums, target); + println!("All subsets with sum equal to {} res = {:?}", target, &res); +} diff --git a/en/codes/rust/chapter_backtracking/subset_sum_i_naive.rs b/en/codes/rust/chapter_backtracking/subset_sum_i_naive.rs new file mode 100644 index 000000000..3f397b0a4 --- /dev/null +++ b/en/codes/rust/chapter_backtracking/subset_sum_i_naive.rs @@ -0,0 +1,54 @@ +/* + * File: subset_sum_i_naive.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Backtracking algorithm: Subset sum I */ +fn backtrack( + state: &mut Vec, + target: i32, + total: i32, + choices: &[i32], + res: &mut Vec>, +) { + // When the subset sum equals target, record the solution + if total == target { + res.push(state.clone()); + return; + } + // Traverse all choices + for i in 0..choices.len() { + // Pruning: if the subset sum exceeds target, skip this choice + if total + choices[i] > target { + continue; + } + // Attempt: make choice, update element sum total + state.push(choices[i]); + // Proceed to the next round of selection + backtrack(state, target, total + choices[i], choices, res); + // Backtrack: undo choice, restore to previous state + state.pop(); + } +} + +/* Solve subset sum I (including duplicate subsets) */ +fn subset_sum_i_naive(nums: &[i32], target: i32) -> Vec> { + let mut state = Vec::new(); // State (subset) + let total = 0; // Subset sum + let mut res = Vec::new(); // Result list (subset list) + backtrack(&mut state, target, total, nums, &mut res); + res +} + +/* Driver Code */ +pub fn main() { + let nums = [3, 4, 5]; + let target = 9; + + let res = subset_sum_i_naive(&nums, target); + + println!("Input array nums = {:?}, target = {}", &nums, target); + println!("All subsets with sum equal to {} res = {:?}", target, &res); + println!("Please note that this method outputs results containing duplicate sets"); +} diff --git a/en/codes/rust/chapter_backtracking/subset_sum_ii.rs b/en/codes/rust/chapter_backtracking/subset_sum_ii.rs new file mode 100644 index 000000000..ad217a170 --- /dev/null +++ b/en/codes/rust/chapter_backtracking/subset_sum_ii.rs @@ -0,0 +1,61 @@ +/* + * File: subset_sum_ii.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Backtracking algorithm: Subset sum II */ +fn backtrack( + state: &mut Vec, + target: i32, + choices: &[i32], + start: usize, + res: &mut Vec>, +) { + // When the subset sum equals target, record the solution + if target == 0 { + res.push(state.clone()); + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + for i in start..choices.len() { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if target - choices[i] < 0 { + break; + } + // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + if i > start && choices[i] == choices[i - 1] { + continue; + } + // Attempt: make choice, update target, start + state.push(choices[i]); + // Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i + 1, res); + // Backtrack: undo choice, restore to previous state + state.pop(); + } +} + +/* Solve subset sum II */ +fn subset_sum_ii(nums: &mut [i32], target: i32) -> Vec> { + let mut state = Vec::new(); // State (subset) + nums.sort(); // Sort nums + let start = 0; // Start point for traversal + let mut res = Vec::new(); // Result list (subset list) + backtrack(&mut state, target, nums, start, &mut res); + res +} + +/* Driver Code */ +pub fn main() { + let mut nums = [4, 4, 5]; + let target = 9; + + let res = subset_sum_ii(&mut nums, target); + + println!("Input array nums = {:?}, target = {}", &nums, target); + println!("All subsets with sum equal to {} res = {:?}", target, &res); +} diff --git a/en/codes/rust/chapter_computational_complexity/iteration.rs b/en/codes/rust/chapter_computational_complexity/iteration.rs new file mode 100644 index 000000000..dedd9e20e --- /dev/null +++ b/en/codes/rust/chapter_computational_complexity/iteration.rs @@ -0,0 +1,74 @@ +/* + * File: iteration.rs + * Created Time: 2023-09-02 + * Author: night-cruise (2586447362@qq.com) + */ + +/* for loop */ +fn for_loop(n: i32) -> i32 { + let mut res = 0; + // Sum 1, 2, ..., n-1, n + for i in 1..=n { + res += i; + } + res +} + +/* while loop */ +fn while_loop(n: i32) -> i32 { + let mut res = 0; + let mut i = 1; // Initialize condition variable + + // Sum 1, 2, ..., n-1, n + while i <= n { + res += i; + i += 1; // Update condition variable + } + res +} + +/* while loop (two updates) */ +fn while_loop_ii(n: i32) -> i32 { + let mut res = 0; + let mut i = 1; // Initialize condition variable + + // Sum 1, 4, 10, ... + while i <= n { + res += i; + // Update condition variable + i += 1; + i *= 2; + } + res +} + +/* Nested for loop */ +fn nested_for_loop(n: i32) -> String { + let mut res = vec![]; + // Loop i = 1, 2, ..., n-1, n + for i in 1..=n { + // Loop j = 1, 2, ..., n-1, n + for j in 1..=n { + res.push(format!("({}, {}), ", i, j)); + } + } + res.join("") +} + +/* Driver Code */ +fn main() { + let n = 5; + let mut res; + + res = for_loop(n); + println!("\nFor loop sum result res = {res}"); + + res = while_loop(n); + println!("\nWhile loop sum result res = {res}"); + + res = while_loop_ii(n); + println!("\nWhile loop (two updates) sum result res = {}", res); + + let res = nested_for_loop(n); + println!("\nNested for loop traversal result {res}"); +} diff --git a/en/codes/rust/chapter_computational_complexity/recursion.rs b/en/codes/rust/chapter_computational_complexity/recursion.rs new file mode 100644 index 000000000..5d45c8e7a --- /dev/null +++ b/en/codes/rust/chapter_computational_complexity/recursion.rs @@ -0,0 +1,76 @@ +/* + * File: recursion.rs + * Created Time: 2023-09-02 + * Author: night-cruise (2586447362@qq.com) + */ + +/* Recursion */ +fn recur(n: i32) -> i32 { + // Termination condition + if n == 1 { + return 1; + } + // Recurse: recursive call + let res = recur(n - 1); + // Return: return result + n + res +} + +/* Simulate recursion using iteration */ +fn for_loop_recur(n: i32) -> i32 { + // Use an explicit stack to simulate the system call stack + let mut stack = Vec::new(); + let mut res = 0; + // Recurse: recursive call + for i in (1..=n).rev() { + // Simulate "recurse" with "push" + stack.push(i); + } + // Return: return result + while !stack.is_empty() { + // Simulate "return" with "pop" + res += stack.pop().unwrap(); + } + // res = 1+2+3+...+n + res +} + +/* Tail recursion */ +fn tail_recur(n: i32, res: i32) -> i32 { + // Termination condition + if n == 0 { + return res; + } + // Tail recursive call + tail_recur(n - 1, res + n) +} + +/* Fibonacci sequence: recursion */ +fn fib(n: i32) -> i32 { + // Termination condition f(1) = 0, f(2) = 1 + if n == 1 || n == 2 { + return n - 1; + } + // Recursive call f(n) = f(n-1) + f(n-2) + let res = fib(n - 1) + fib(n - 2); + // Return result + res +} + +/* Driver Code */ +fn main() { + let n = 5; + let mut res; + + res = recur(n); + println!("\nRecursion sum result res = {res}"); + + res = for_loop_recur(n); + println!("\nUsing iteration to simulate recursion sum result res = {res}"); + + res = tail_recur(n, 0); + println!("\nTail recursion sum result res = {res}"); + + res = fib(n); + println!("\nThe {n}th Fibonacci number is {res}"); +} diff --git a/en/codes/rust/chapter_computational_complexity/space_complexity.rs b/en/codes/rust/chapter_computational_complexity/space_complexity.rs new file mode 100644 index 000000000..efee1f1e2 --- /dev/null +++ b/en/codes/rust/chapter_computational_complexity/space_complexity.rs @@ -0,0 +1,114 @@ +/* + * File: space_complexity.rs + * Created Time: 2023-03-11 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::{print_util, ListNode, TreeNode}; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +/* Function */ +fn function() -> i32 { + // Perform some operations + return 0; +} + +/* Constant order */ +#[allow(unused)] +fn constant(n: i32) { + // Constants, variables, objects occupy O(1) space + const A: i32 = 0; + let b = 0; + let nums = vec![0; 10000]; + let node = ListNode::new(0); + // Variables in the loop occupy O(1) space + for i in 0..n { + let c = 0; + } + // Functions in the loop occupy O(1) space + for i in 0..n { + function(); + } +} + +/* Linear order */ +#[allow(unused)] +fn linear(n: i32) { + // Array of length n uses O(n) space + let mut nums = vec![0; n as usize]; + // A list of length n occupies O(n) space + let mut nodes = Vec::new(); + for i in 0..n { + nodes.push(ListNode::new(i)) + } + // A hash table of length n occupies O(n) space + let mut map = HashMap::new(); + for i in 0..n { + map.insert(i, i.to_string()); + } +} + +/* Linear order (recursive implementation) */ +fn linear_recur(n: i32) { + println!("Recursion n = {}", n); + if n == 1 { + return; + }; + linear_recur(n - 1); +} + +/* Exponential order */ +#[allow(unused)] +fn quadratic(n: i32) { + // Matrix uses O(n^2) space + let num_matrix = vec![vec![0; n as usize]; n as usize]; + // 2D list uses O(n^2) space + let mut num_list = Vec::new(); + for i in 0..n { + let mut tmp = Vec::new(); + for j in 0..n { + tmp.push(0); + } + num_list.push(tmp); + } +} + +/* Quadratic order (recursive implementation) */ +fn quadratic_recur(n: i32) -> i32 { + if n <= 0 { + return 0; + }; + // Array nums has length n, n-1, ..., 2, 1 + let nums = vec![0; n as usize]; + println!("In recursion n = {}, nums length = {}", n, nums.len()); + return quadratic_recur(n - 1); +} + +/* Driver Code */ +fn build_tree(n: i32) -> Option>> { + if n == 0 { + return None; + }; + let root = TreeNode::new(0); + root.borrow_mut().left = build_tree(n - 1); + root.borrow_mut().right = build_tree(n - 1); + return Some(root); +} + +/* Driver Code */ +fn main() { + let n = 5; + // Constant order + constant(n); + // Linear order + linear(n); + linear_recur(n); + // Exponential order + quadratic(n); + quadratic_recur(n); + // Exponential order + let root = build_tree(n); + print_util::print_tree(&root.unwrap()); +} diff --git a/en/codes/rust/chapter_computational_complexity/time_complexity.rs b/en/codes/rust/chapter_computational_complexity/time_complexity.rs new file mode 100644 index 000000000..6f355cc23 --- /dev/null +++ b/en/codes/rust/chapter_computational_complexity/time_complexity.rs @@ -0,0 +1,170 @@ +/* + * File: time_complexity.rs + * Created Time: 2023-01-10 + * Author: xBLACICEx (xBLACKICEx@outlook.com), codingonion (coderonion@gmail.com) + */ + +/* Constant order */ +fn constant(n: i32) -> i32 { + _ = n; + let mut count = 0; + let size = 100_000; + for _ in 0..size { + count += 1; + } + count +} + +/* Linear order */ +fn linear(n: i32) -> i32 { + let mut count = 0; + for _ in 0..n { + count += 1; + } + count +} + +/* Linear order (traversing array) */ +fn array_traversal(nums: &[i32]) -> i32 { + let mut count = 0; + // Number of iterations is proportional to the array length + for _ in nums { + count += 1; + } + count +} + +/* Exponential order */ +fn quadratic(n: i32) -> i32 { + let mut count = 0; + // Number of iterations is quadratically related to the data size n + for _ in 0..n { + for _ in 0..n { + count += 1; + } + } + count +} + +/* Quadratic order (bubble sort) */ +fn bubble_sort(nums: &mut [i32]) -> i32 { + let mut count = 0; // Counter + + // Outer loop: unsorted range is [0, i] + for i in (1..nums.len()).rev() { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for j in 0..i { + if nums[j] > nums[j + 1] { + // Swap nums[j] and nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // Element swap includes 3 unit operations + } + } + } + count +} + +/* Exponential order (loop implementation) */ +fn exponential(n: i32) -> i32 { + let mut count = 0; + let mut base = 1; + // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + for _ in 0..n { + for _ in 0..base { + count += 1 + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + count +} + +/* Exponential order (recursive implementation) */ +fn exp_recur(n: i32) -> i32 { + if n == 1 { + return 1; + } + exp_recur(n - 1) + exp_recur(n - 1) + 1 +} + +/* Logarithmic order (loop implementation) */ +fn logarithmic(mut n: i32) -> i32 { + let mut count = 0; + while n > 1 { + n = n / 2; + count += 1; + } + count +} + +/* Logarithmic order (recursive implementation) */ +fn log_recur(n: i32) -> i32 { + if n <= 1 { + return 0; + } + log_recur(n / 2) + 1 +} + +/* Linearithmic order */ +fn linear_log_recur(n: i32) -> i32 { + if n <= 1 { + return 1; + } + let mut count = linear_log_recur(n / 2) + linear_log_recur(n / 2); + for _ in 0..n { + count += 1; + } + return count; +} + +/* Factorial order (recursive implementation) */ +fn factorial_recur(n: i32) -> i32 { + if n == 0 { + return 1; + } + let mut count = 0; + // Split from 1 into n + for _ in 0..n { + count += factorial_recur(n - 1); + } + count +} + +/* Driver Code */ +fn main() { + // You can modify n to run and observe the trend of the number of operations for various complexities + let n: i32 = 8; + println!("Input data size n = {}", n); + + let mut count = constant(n); + println!("Constant-time operations count = {}", count); + + count = linear(n); + println!("Linear-time operations count = {}", count); + count = array_traversal(&vec![0; n as usize]); + println!("Linear-time (array traversal) operations count = {}", count); + + count = quadratic(n); + println!("Quadratic-time operations count = {}", count); + let mut nums = (1..=n).rev().collect::>(); // [n,n-1,...,2,1] + count = bubble_sort(&mut nums); + println!("Quadratic-time (bubble sort) operations count = {}", count); + + count = exponential(n); + println!("Exponential-time (iterative) operations count = {}", count); + count = exp_recur(n); + println!("Exponential-time (recursive) operations count = {}", count); + + count = logarithmic(n); + println!("Logarithmic-time (iterative) operations count = {}", count); + count = log_recur(n); + println!("Logarithmic-time (recursive) operations count = {}", count); + + count = linear_log_recur(n); + println!("Linearithmic-time (recursive) operations count = {}", count); + + count = factorial_recur(n); + println!("Factorial-time (recursive) operations count = {}", count); +} diff --git a/en/codes/rust/chapter_computational_complexity/worst_best_time_complexity.rs b/en/codes/rust/chapter_computational_complexity/worst_best_time_complexity.rs new file mode 100644 index 000000000..ada71c112 --- /dev/null +++ b/en/codes/rust/chapter_computational_complexity/worst_best_time_complexity.rs @@ -0,0 +1,42 @@ +/* + * File: worst_best_time_complexity.rs + * Created Time: 2023-01-13 + * Author: xBLACICEx (xBLACKICEx@outlook.com), codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::print_util; +use rand::seq::SliceRandom; +use rand::thread_rng; + +/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +fn random_numbers(n: i32) -> Vec { + // Generate array nums = { 1, 2, 3, ..., n } + let mut nums = (1..=n).collect::>(); + // Randomly shuffle array elements + nums.shuffle(&mut thread_rng()); + nums +} + +/* Find the index of number 1 in array nums */ +fn find_one(nums: &[i32]) -> Option { + for i in 0..nums.len() { + // When element 1 is at the head of the array, best time complexity O(1) is achieved + // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + if nums[i] == 1 { + return Some(i); + } + } + None +} + +/* Driver Code */ +fn main() { + for _ in 0..10 { + let n = 100; + let nums = random_numbers(n); + let index = find_one(&nums).unwrap(); + print!("\nArray [ 1, 2, ..., n ] after shuffling = "); + print_util::print_array(&nums); + println!("\nIndex of number 1 is {}", index); + } +} diff --git a/en/codes/rust/chapter_divide_and_conquer/binary_search_recur.rs b/en/codes/rust/chapter_divide_and_conquer/binary_search_recur.rs new file mode 100644 index 000000000..b5a395f14 --- /dev/null +++ b/en/codes/rust/chapter_divide_and_conquer/binary_search_recur.rs @@ -0,0 +1,41 @@ +/* + * File: binary_search_recur.rs + * Created Time: 2023-07-15 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Binary search: problem f(i, j) */ +fn dfs(nums: &[i32], target: i32, i: i32, j: i32) -> i32 { + // If the interval is empty, it means there is no target element, return -1 + if i > j { + return -1; + } + let m: i32 = i + (j - i) / 2; + if nums[m as usize] < target { + // Recursion subproblem f(m+1, j) + return dfs(nums, target, m + 1, j); + } else if nums[m as usize] > target { + // Recursion subproblem f(i, m-1) + return dfs(nums, target, i, m - 1); + } else { + // Found the target element, return its index + return m; + } +} + +/* Binary search */ +fn binary_search(nums: &[i32], target: i32) -> i32 { + let n = nums.len() as i32; + // Solve the problem f(0, n-1) + dfs(nums, target, 0, n - 1) +} + +/* Driver Code */ +pub fn main() { + let target = 6; + let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; + + // Binary search (closed interval on both sides) + let index = binary_search(&nums, target); + println!("Index of target element 6 is {index}"); +} diff --git a/en/codes/rust/chapter_divide_and_conquer/build_tree.rs b/en/codes/rust/chapter_divide_and_conquer/build_tree.rs new file mode 100644 index 000000000..da83e565f --- /dev/null +++ b/en/codes/rust/chapter_divide_and_conquer/build_tree.rs @@ -0,0 +1,56 @@ +/* + * File: build_tree.rs + * Created Time: 2023-07-15 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::{print_util, TreeNode}; +use std::collections::HashMap; +use std::{cell::RefCell, rc::Rc}; + +/* Build binary tree: divide and conquer */ +fn dfs( + preorder: &[i32], + inorder_map: &HashMap, + i: i32, + l: i32, + r: i32, +) -> Option>> { + // Terminate when the subtree interval is empty + if r - l < 0 { + return None; + } + // Initialize the root node + let root = TreeNode::new(preorder[i as usize]); + // Query m to divide the left and right subtrees + let m = inorder_map.get(&preorder[i as usize]).unwrap(); + // Subproblem: build the left subtree + root.borrow_mut().left = dfs(preorder, inorder_map, i + 1, l, m - 1); + // Subproblem: build the right subtree + root.borrow_mut().right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r); + // Return the root node + Some(root) +} + +/* Build binary tree */ +fn build_tree(preorder: &[i32], inorder: &[i32]) -> Option>> { + // Initialize hash map, storing the mapping from inorder elements to indices + let mut inorder_map: HashMap = HashMap::new(); + for i in 0..inorder.len() { + inorder_map.insert(inorder[i], i as i32); + } + let root = dfs(preorder, &inorder_map, 0, 0, inorder.len() as i32 - 1); + root +} + +/* Driver Code */ +fn main() { + let preorder = [3, 9, 2, 1, 7]; + let inorder = [9, 3, 1, 2, 7]; + println!("In-order traversal = {:?}", preorder); + println!("Pre-order traversal = {:?}", inorder); + + let root = build_tree(&preorder, &inorder); + println!("The constructed binary tree is:"); + print_util::print_tree(root.as_ref().unwrap()); +} diff --git a/en/codes/rust/chapter_divide_and_conquer/hanota.rs b/en/codes/rust/chapter_divide_and_conquer/hanota.rs new file mode 100644 index 000000000..4501fbc0d --- /dev/null +++ b/en/codes/rust/chapter_divide_and_conquer/hanota.rs @@ -0,0 +1,55 @@ +/* + * File: hanota.rs + * Created Time: 2023-07-15 + * Author: codingonion (coderonion@gmail.com) + */ + +#![allow(non_snake_case)] + +/* Move a disk */ +fn move_pan(src: &mut Vec, tar: &mut Vec) { + // Take out a disk from the top of src + let pan = src.pop().unwrap(); + // Place the disk on top of tar + tar.push(pan); +} + +/* Solve the Tower of Hanoi problem f(i) */ +fn dfs(i: i32, src: &mut Vec, buf: &mut Vec, tar: &mut Vec) { + // If there is only one disk left in src, move it directly to tar + if i == 1 { + move_pan(src, tar); + return; + } + // Subproblem f(i-1): move the top i-1 disks from src to buf using tar + dfs(i - 1, src, tar, buf); + // Subproblem f(1): move the remaining disk from src to tar + move_pan(src, tar); + // Subproblem f(i-1): move the top i-1 disks from buf to tar using src + dfs(i - 1, buf, src, tar); +} + +/* Solve the Tower of Hanoi problem */ +fn solve_hanota(A: &mut Vec, B: &mut Vec, C: &mut Vec) { + let n = A.len() as i32; + // Move the top n disks from A to C using B + dfs(n, A, B, C); +} + +/* Driver Code */ +pub fn main() { + let mut A = vec![5, 4, 3, 2, 1]; + let mut B = Vec::new(); + let mut C = Vec::new(); + println!("In initial state:"); + println!("A = {:?}", A); + println!("B = {:?}", B); + println!("C = {:?}", C); + + solve_hanota(&mut A, &mut B, &mut C); + + println!("After disk movement is complete:"); + println!("A = {:?}", A); + println!("B = {:?}", B); + println!("C = {:?}", C); +} diff --git a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_backtrack.rs b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_backtrack.rs new file mode 100644 index 000000000..8bbd005b7 --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_backtrack.rs @@ -0,0 +1,41 @@ +/* + * File: climbing_stairs_backtrack.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Backtracking */ +fn backtrack(choices: &[i32], state: i32, n: i32, res: &mut [i32]) { + // When climbing to the n-th stair, add 1 to the solution count + if state == n { + res[0] = res[0] + 1; + } + // Traverse all choices + for &choice in choices { + // Pruning: not allowed to go beyond the n-th stair + if state + choice > n { + continue; + } + // Attempt: make choice, update state + backtrack(choices, state + choice, n, res); + // Backtrack + } +} + +/* Climbing stairs: Backtracking */ +fn climbing_stairs_backtrack(n: usize) -> i32 { + let choices = vec![1, 2]; // Can choose to climb up 1 or 2 stairs + let state = 0; // Start climbing from the 0-th stair + let mut res = Vec::new(); + res.push(0); // Use res[0] to record the solution count + backtrack(&choices, state, n as i32, &mut res); + res[0] +} + +/* Driver Code */ +pub fn main() { + let n: usize = 9; + + let res = climbing_stairs_backtrack(n); + println!("Climbing {n} stairs has {res} solutions"); +} diff --git a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_constraint_dp.rs b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_constraint_dp.rs new file mode 100644 index 000000000..cfc008424 --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_constraint_dp.rs @@ -0,0 +1,33 @@ +/* + * File: climbing_stairs_constraint_dp.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Climbing stairs with constraint: Dynamic programming */ +fn climbing_stairs_constraint_dp(n: usize) -> i32 { + if n == 1 || n == 2 { + return 1; + }; + // Initialize dp table, used to store solutions to subproblems + let mut dp = vec![vec![-1; 3]; n + 1]; + // Initial state: preset the solution to the smallest subproblem + dp[1][1] = 1; + dp[1][2] = 0; + dp[2][1] = 0; + dp[2][2] = 1; + // State transition: gradually solve larger subproblems from smaller ones + for i in 3..=n { + dp[i][1] = dp[i - 1][2]; + dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; + } + dp[n][1] + dp[n][2] +} + +/* Driver Code */ +pub fn main() { + let n: usize = 9; + + let res = climbing_stairs_constraint_dp(n); + println!("Climbing {n} stairs has {res} solutions"); +} diff --git a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs.rs b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs.rs new file mode 100644 index 000000000..d25a751e4 --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs.rs @@ -0,0 +1,29 @@ +/* + * File: climbing_stairs_dfs.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Search */ +fn dfs(i: usize) -> i32 { + // Known dp[1] and dp[2], return them + if i == 1 || i == 2 { + return i as i32; + } + // dp[i] = dp[i-1] + dp[i-2] + let count = dfs(i - 1) + dfs(i - 2); + count +} + +/* Climbing stairs: Search */ +fn climbing_stairs_dfs(n: usize) -> i32 { + dfs(n) +} + +/* Driver Code */ +pub fn main() { + let n: usize = 9; + + let res = climbing_stairs_dfs(n); + println!("Climbing {n} stairs has {res} solutions"); +} diff --git a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs_mem.rs b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs_mem.rs new file mode 100644 index 000000000..d3f5e02c7 --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dfs_mem.rs @@ -0,0 +1,37 @@ +/* + * File: climbing_stairs_dfs_mem.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Memoization search */ +fn dfs(i: usize, mem: &mut [i32]) -> i32 { + // Known dp[1] and dp[2], return them + if i == 1 || i == 2 { + return i as i32; + } + // If record dp[i] exists, return it directly + if mem[i] != -1 { + return mem[i]; + } + // dp[i] = dp[i-1] + dp[i-2] + let count = dfs(i - 1, mem) + dfs(i - 2, mem); + // Record dp[i] + mem[i] = count; + count +} + +/* Climbing stairs: Memoization search */ +fn climbing_stairs_dfs_mem(n: usize) -> i32 { + // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + let mut mem = vec![-1; n + 1]; + dfs(n, &mut mem) +} + +/* Driver Code */ +pub fn main() { + let n: usize = 9; + + let res = climbing_stairs_dfs_mem(n); + println!("Climbing {n} stairs has {res} solutions"); +} diff --git a/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dp.rs b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dp.rs new file mode 100644 index 000000000..9c4eb59ad --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/climbing_stairs_dp.rs @@ -0,0 +1,48 @@ +/* + * File: climbing_stairs_dp.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Climbing stairs: Dynamic programming */ +fn climbing_stairs_dp(n: usize) -> i32 { + // Known dp[1] and dp[2], return them + if n == 1 || n == 2 { + return n as i32; + } + // Initialize dp table, used to store solutions to subproblems + let mut dp = vec![-1; n + 1]; + // Initial state: preset the solution to the smallest subproblem + dp[1] = 1; + dp[2] = 2; + // State transition: gradually solve larger subproblems from smaller ones + for i in 3..=n { + dp[i] = dp[i - 1] + dp[i - 2]; + } + dp[n] +} + +/* Climbing stairs: Space-optimized dynamic programming */ +fn climbing_stairs_dp_comp(n: usize) -> i32 { + if n == 1 || n == 2 { + return n as i32; + } + let (mut a, mut b) = (1, 2); + for _ in 3..=n { + let tmp = b; + b = a + b; + a = tmp; + } + b +} + +/* Driver Code */ +pub fn main() { + let n: usize = 9; + + let res = climbing_stairs_dp(n); + println!("Climbing {n} stairs has {res} solutions"); + + let res = climbing_stairs_dp_comp(n); + println!("Climbing {n} stairs has {res} solutions"); +} diff --git a/en/codes/rust/chapter_dynamic_programming/coin_change.rs b/en/codes/rust/chapter_dynamic_programming/coin_change.rs new file mode 100644 index 000000000..82237ffc0 --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/coin_change.rs @@ -0,0 +1,75 @@ +/* + * File: coin_change.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Coin change: Dynamic programming */ +fn coin_change_dp(coins: &[i32], amt: usize) -> i32 { + let n = coins.len(); + let max = amt + 1; + // Initialize dp table + let mut dp = vec![vec![0; amt + 1]; n + 1]; + // State transition: first row and first column + for a in 1..=amt { + dp[0][a] = max; + } + // State transition: rest of the rows and columns + for i in 1..=n { + for a in 1..=amt { + if coins[i - 1] > a as i32 { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[i][a] = std::cmp::min(dp[i - 1][a], dp[i][a - coins[i - 1] as usize] + 1); + } + } + } + if dp[n][amt] != max { + return dp[n][amt] as i32; + } else { + -1 + } +} + +/* Coin change: Space-optimized dynamic programming */ +fn coin_change_dp_comp(coins: &[i32], amt: usize) -> i32 { + let n = coins.len(); + let max = amt + 1; + // Initialize dp table + let mut dp = vec![0; amt + 1]; + dp.fill(max); + dp[0] = 0; + // State transition + for i in 1..=n { + for a in 1..=amt { + if coins[i - 1] > a as i32 { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[a] = std::cmp::min(dp[a], dp[a - coins[i - 1] as usize] + 1); + } + } + } + if dp[amt] != max { + return dp[amt] as i32; + } else { + -1 + } +} + +/* Driver Code */ +pub fn main() { + let coins = [1, 2, 5]; + let amt: usize = 4; + + // Dynamic programming + let res = coin_change_dp(&coins, amt); + println!("Minimum coins needed to make target amount is {res}"); + + // Space-optimized dynamic programming + let res = coin_change_dp_comp(&coins, amt); + println!("Minimum coins needed to make target amount is {res}"); +} diff --git a/en/codes/rust/chapter_dynamic_programming/coin_change_ii.rs b/en/codes/rust/chapter_dynamic_programming/coin_change_ii.rs new file mode 100644 index 000000000..f275df1a9 --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/coin_change_ii.rs @@ -0,0 +1,64 @@ +/* + * File: coin_change_ii.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Coin change II: Dynamic programming */ +fn coin_change_ii_dp(coins: &[i32], amt: usize) -> i32 { + let n = coins.len(); + // Initialize dp table + let mut dp = vec![vec![0; amt + 1]; n + 1]; + // Initialize first column + for i in 0..=n { + dp[i][0] = 1; + } + // State transition + for i in 1..=n { + for a in 1..=amt { + if coins[i - 1] > a as i32 { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1] as usize]; + } + } + } + dp[n][amt] +} + +/* Coin change II: Space-optimized dynamic programming */ +fn coin_change_ii_dp_comp(coins: &[i32], amt: usize) -> i32 { + let n = coins.len(); + // Initialize dp table + let mut dp = vec![0; amt + 1]; + dp[0] = 1; + // State transition + for i in 1..=n { + for a in 1..=amt { + if coins[i - 1] > a as i32 { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[a] = dp[a] + dp[a - coins[i - 1] as usize]; + } + } + } + dp[amt] +} + +/* Driver Code */ +pub fn main() { + let coins = [1, 2, 5]; + let amt: usize = 5; + + // Dynamic programming + let res = coin_change_ii_dp(&coins, amt); + println!("Number of coin combinations to make target amount is {res}"); + + // Space-optimized dynamic programming + let res = coin_change_ii_dp_comp(&coins, amt); + println!("Number of coin combinations to make target amount is {res}"); +} diff --git a/en/codes/rust/chapter_dynamic_programming/edit_distance.rs b/en/codes/rust/chapter_dynamic_programming/edit_distance.rs new file mode 100644 index 000000000..c11134598 --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/edit_distance.rs @@ -0,0 +1,145 @@ +/* + * File: edit_distance.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Edit distance: Brute-force search */ +fn edit_distance_dfs(s: &str, t: &str, i: usize, j: usize) -> i32 { + // If both s and t are empty, return 0 + if i == 0 && j == 0 { + return 0; + } + // If s is empty, return length of t + if i == 0 { + return j as i32; + } + // If t is empty, return length of s + if j == 0 { + return i as i32; + } + // If two characters are equal, skip both characters + if s.chars().nth(i - 1) == t.chars().nth(j - 1) { + return edit_distance_dfs(s, t, i - 1, j - 1); + } + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + let insert = edit_distance_dfs(s, t, i, j - 1); + let delete = edit_distance_dfs(s, t, i - 1, j); + let replace = edit_distance_dfs(s, t, i - 1, j - 1); + // Return minimum edit steps + std::cmp::min(std::cmp::min(insert, delete), replace) + 1 +} + +/* Edit distance: Memoization search */ +fn edit_distance_dfs_mem(s: &str, t: &str, mem: &mut Vec>, i: usize, j: usize) -> i32 { + // If both s and t are empty, return 0 + if i == 0 && j == 0 { + return 0; + } + // If s is empty, return length of t + if i == 0 { + return j as i32; + } + // If t is empty, return length of s + if j == 0 { + return i as i32; + } + // If there's a record, return it directly + if mem[i][j] != -1 { + return mem[i][j]; + } + // If two characters are equal, skip both characters + if s.chars().nth(i - 1) == t.chars().nth(j - 1) { + return edit_distance_dfs_mem(s, t, mem, i - 1, j - 1); + } + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + let insert = edit_distance_dfs_mem(s, t, mem, i, j - 1); + let delete = edit_distance_dfs_mem(s, t, mem, i - 1, j); + let replace = edit_distance_dfs_mem(s, t, mem, i - 1, j - 1); + // Record and return minimum edit steps + mem[i][j] = std::cmp::min(std::cmp::min(insert, delete), replace) + 1; + mem[i][j] +} + +/* Edit distance: Dynamic programming */ +fn edit_distance_dp(s: &str, t: &str) -> i32 { + let (n, m) = (s.len(), t.len()); + let mut dp = vec![vec![0; m + 1]; n + 1]; + // State transition: first row and first column + for i in 1..=n { + dp[i][0] = i as i32; + } + for j in 1..m { + dp[0][j] = j as i32; + } + // State transition: rest of the rows and columns + for i in 1..=n { + for j in 1..=m { + if s.chars().nth(i - 1) == t.chars().nth(j - 1) { + // If two characters are equal, skip both characters + dp[i][j] = dp[i - 1][j - 1]; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[i][j] = + std::cmp::min(std::cmp::min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; + } + } + } + dp[n][m] +} + +/* Edit distance: Space-optimized dynamic programming */ +fn edit_distance_dp_comp(s: &str, t: &str) -> i32 { + let (n, m) = (s.len(), t.len()); + let mut dp = vec![0; m + 1]; + // State transition: first row + for j in 1..m { + dp[j] = j as i32; + } + // State transition: rest of the rows + for i in 1..=n { + // State transition: first column + let mut leftup = dp[0]; // Temporarily store dp[i-1, j-1] + dp[0] = i as i32; + // State transition: rest of the columns + for j in 1..=m { + let temp = dp[j]; + if s.chars().nth(i - 1) == t.chars().nth(j - 1) { + // If two characters are equal, skip both characters + dp[j] = leftup; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[j] = std::cmp::min(std::cmp::min(dp[j - 1], dp[j]), leftup) + 1; + } + leftup = temp; // Update for next round's dp[i-1, j-1] + } + } + dp[m] +} + +/* Driver Code */ +pub fn main() { + let s = "bag"; + let t = "pack"; + let (n, m) = (s.len(), t.len()); + + // Brute-force search + let res = edit_distance_dfs(s, t, n, m); + println!("Changing {s} to {t} requires minimum {res} edits"); + + // Memoization search + let mut mem = vec![vec![0; m + 1]; n + 1]; + for row in mem.iter_mut() { + row.fill(-1); + } + let res = edit_distance_dfs_mem(s, t, &mut mem, n, m); + println!("Changing {s} to {t} requires minimum {res} edits"); + + // Dynamic programming + let res = edit_distance_dp(s, t); + println!("Changing {s} to {t} requires minimum {res} edits"); + + // Space-optimized dynamic programming + let res = edit_distance_dp_comp(s, t); + println!("Changing {s} to {t} requires minimum {res} edits"); +} diff --git a/en/codes/rust/chapter_dynamic_programming/knapsack.rs b/en/codes/rust/chapter_dynamic_programming/knapsack.rs new file mode 100644 index 000000000..80eaaf5af --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/knapsack.rs @@ -0,0 +1,113 @@ +/* + * File: knapsack.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* 0-1 knapsack: Brute-force search */ +fn knapsack_dfs(wgt: &[i32], val: &[i32], i: usize, c: usize) -> i32 { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if i == 0 || c == 0 { + return 0; + } + // If exceeds knapsack capacity, can only choose not to put it in + if wgt[i - 1] > c as i32 { + return knapsack_dfs(wgt, val, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + let no = knapsack_dfs(wgt, val, i - 1, c); + let yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1] as usize) + val[i - 1]; + // Return the larger value of the two options + std::cmp::max(no, yes) +} + +/* 0-1 knapsack: Memoization search */ +fn knapsack_dfs_mem(wgt: &[i32], val: &[i32], mem: &mut Vec>, i: usize, c: usize) -> i32 { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if i == 0 || c == 0 { + return 0; + } + // If there's a record, return it directly + if mem[i][c] != -1 { + return mem[i][c]; + } + // If exceeds knapsack capacity, can only choose not to put it in + if wgt[i - 1] > c as i32 { + return knapsack_dfs_mem(wgt, val, mem, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + let no = knapsack_dfs_mem(wgt, val, mem, i - 1, c); + let yes = knapsack_dfs_mem(wgt, val, mem, i - 1, c - wgt[i - 1] as usize) + val[i - 1]; + // Record and return the larger value of the two options + mem[i][c] = std::cmp::max(no, yes); + mem[i][c] +} + +/* 0-1 knapsack: Dynamic programming */ +fn knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { + let n = wgt.len(); + // Initialize dp table + let mut dp = vec![vec![0; cap + 1]; n + 1]; + // State transition + for i in 1..=n { + for c in 1..=cap { + if wgt[i - 1] > c as i32 { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c]; + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = std::cmp::max( + dp[i - 1][c], + dp[i - 1][c - wgt[i - 1] as usize] + val[i - 1], + ); + } + } + } + dp[n][cap] +} + +/* 0-1 knapsack: Space-optimized dynamic programming */ +fn knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { + let n = wgt.len(); + // Initialize dp table + let mut dp = vec![0; cap + 1]; + // State transition + for i in 1..=n { + // Traverse in reverse order + for c in (1..=cap).rev() { + if wgt[i - 1] <= c as i32 { + // The larger value between not selecting and selecting item i + dp[c] = std::cmp::max(dp[c], dp[c - wgt[i - 1] as usize] + val[i - 1]); + } + } + } + dp[cap] +} + +/* Driver Code */ +pub fn main() { + let wgt = [10, 20, 30, 40, 50]; + let val = [50, 120, 150, 210, 240]; + let cap: usize = 50; + let n = wgt.len(); + + // Brute-force search + let res = knapsack_dfs(&wgt, &val, n, cap); + println!("Maximum item value not exceeding knapsack capacity is {res}"); + + // Memoization search + let mut mem = vec![vec![0; cap + 1]; n + 1]; + for row in mem.iter_mut() { + row.fill(-1); + } + let res = knapsack_dfs_mem(&wgt, &val, &mut mem, n, cap); + println!("Maximum item value not exceeding knapsack capacity is {res}"); + + // Dynamic programming + let res = knapsack_dp(&wgt, &val, cap); + println!("Maximum item value not exceeding knapsack capacity is {res}"); + + // Space-optimized dynamic programming + let res = knapsack_dp_comp(&wgt, &val, cap); + println!("Maximum item value not exceeding knapsack capacity is {res}"); +} diff --git a/en/codes/rust/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rs b/en/codes/rust/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rs new file mode 100644 index 000000000..7930f3501 --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/min_cost_climbing_stairs_dp.rs @@ -0,0 +1,52 @@ +/* + * File: min_cost_climbing_stairs_dp.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +use std::cmp; + +/* Minimum cost climbing stairs: Dynamic programming */ +fn min_cost_climbing_stairs_dp(cost: &[i32]) -> i32 { + let n = cost.len() - 1; + if n == 1 || n == 2 { + return cost[n]; + } + // Initialize dp table, used to store solutions to subproblems + let mut dp = vec![-1; n + 1]; + // Initial state: preset the solution to the smallest subproblem + dp[1] = cost[1]; + dp[2] = cost[2]; + // State transition: gradually solve larger subproblems from smaller ones + for i in 3..=n { + dp[i] = cmp::min(dp[i - 1], dp[i - 2]) + cost[i]; + } + dp[n] +} + +/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +fn min_cost_climbing_stairs_dp_comp(cost: &[i32]) -> i32 { + let n = cost.len() - 1; + if n == 1 || n == 2 { + return cost[n]; + }; + let (mut a, mut b) = (cost[1], cost[2]); + for i in 3..=n { + let tmp = b; + b = cmp::min(a, tmp) + cost[i]; + a = tmp; + } + b +} + +/* Driver Code */ +pub fn main() { + let cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]; + println!("Input stair cost list is {:?}", &cost); + + let res = min_cost_climbing_stairs_dp(&cost); + println!("Minimum cost to climb stairs is {res}"); + + let res = min_cost_climbing_stairs_dp_comp(&cost); + println!("Minimum cost to climb stairs is {res}"); +} diff --git a/en/codes/rust/chapter_dynamic_programming/min_path_sum.rs b/en/codes/rust/chapter_dynamic_programming/min_path_sum.rs new file mode 100644 index 000000000..ff4b3153e --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/min_path_sum.rs @@ -0,0 +1,120 @@ +/* + * File: min_path_sum.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Minimum path sum: Brute-force search */ +fn min_path_sum_dfs(grid: &Vec>, i: i32, j: i32) -> i32 { + // If it's the top-left cell, terminate the search + if i == 0 && j == 0 { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if i < 0 || j < 0 { + return i32::MAX; + } + // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + let up = min_path_sum_dfs(grid, i - 1, j); + let left = min_path_sum_dfs(grid, i, j - 1); + // Return the minimum path cost from top-left to (i, j) + std::cmp::min(left, up) + grid[i as usize][j as usize] +} + +/* Minimum path sum: Memoization search */ +fn min_path_sum_dfs_mem(grid: &Vec>, mem: &mut Vec>, i: i32, j: i32) -> i32 { + // If it's the top-left cell, terminate the search + if i == 0 && j == 0 { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if i < 0 || j < 0 { + return i32::MAX; + } + // If there's a record, return it directly + if mem[i as usize][j as usize] != -1 { + return mem[i as usize][j as usize]; + } + // Minimum path cost for left and upper cells + let up = min_path_sum_dfs_mem(grid, mem, i - 1, j); + let left = min_path_sum_dfs_mem(grid, mem, i, j - 1); + // Record and return the minimum path cost from top-left to (i, j) + mem[i as usize][j as usize] = std::cmp::min(left, up) + grid[i as usize][j as usize]; + mem[i as usize][j as usize] +} + +/* Minimum path sum: Dynamic programming */ +fn min_path_sum_dp(grid: &Vec>) -> i32 { + let (n, m) = (grid.len(), grid[0].len()); + // Initialize dp table + let mut dp = vec![vec![0; m]; n]; + dp[0][0] = grid[0][0]; + // State transition: first row + for j in 1..m { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + // State transition: first column + for i in 1..n { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + // State transition: rest of the rows and columns + for i in 1..n { + for j in 1..m { + dp[i][j] = std::cmp::min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; + } + } + dp[n - 1][m - 1] +} + +/* Minimum path sum: Space-optimized dynamic programming */ +fn min_path_sum_dp_comp(grid: &Vec>) -> i32 { + let (n, m) = (grid.len(), grid[0].len()); + // Initialize dp table + let mut dp = vec![0; m]; + // State transition: first row + dp[0] = grid[0][0]; + for j in 1..m { + dp[j] = dp[j - 1] + grid[0][j]; + } + // State transition: rest of the rows + for i in 1..n { + // State transition: first column + dp[0] = dp[0] + grid[i][0]; + // State transition: rest of the columns + for j in 1..m { + dp[j] = std::cmp::min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + dp[m - 1] +} + +/* Driver Code */ +pub fn main() { + let grid = vec![ + vec![1, 3, 1, 5], + vec![2, 2, 4, 2], + vec![5, 3, 2, 1], + vec![4, 3, 5, 2], + ]; + let (n, m) = (grid.len(), grid[0].len()); + + // Brute-force search + let res = min_path_sum_dfs(&grid, n as i32 - 1, m as i32 - 1); + println!("Minimum path sum from top-left to bottom-right is {res}"); + + // Memoization search + let mut mem = vec![vec![0; m]; n]; + for row in mem.iter_mut() { + row.fill(-1); + } + let res = min_path_sum_dfs_mem(&grid, &mut mem, n as i32 - 1, m as i32 - 1); + println!("Minimum path sum from top-left to bottom-right is {res}"); + + // Dynamic programming + let res = min_path_sum_dp(&grid); + println!("Minimum path sum from top-left to bottom-right is {res}"); + + // Space-optimized dynamic programming + let res = min_path_sum_dp_comp(&grid); + println!("Minimum path sum from top-left to bottom-right is {res}"); +} diff --git a/en/codes/rust/chapter_dynamic_programming/unbounded_knapsack.rs b/en/codes/rust/chapter_dynamic_programming/unbounded_knapsack.rs new file mode 100644 index 000000000..32f90845a --- /dev/null +++ b/en/codes/rust/chapter_dynamic_programming/unbounded_knapsack.rs @@ -0,0 +1,60 @@ +/* + * File: unbounded_knapsack.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Unbounded knapsack: Dynamic programming */ +fn unbounded_knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { + let n = wgt.len(); + // Initialize dp table + let mut dp = vec![vec![0; cap + 1]; n + 1]; + // State transition + for i in 1..=n { + for c in 1..=cap { + if wgt[i - 1] > c as i32 { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c]; + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = std::cmp::max(dp[i - 1][c], dp[i][c - wgt[i - 1] as usize] + val[i - 1]); + } + } + } + return dp[n][cap]; +} + +/* Unbounded knapsack: Space-optimized dynamic programming */ +fn unbounded_knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { + let n = wgt.len(); + // Initialize dp table + let mut dp = vec![0; cap + 1]; + // State transition + for i in 1..=n { + for c in 1..=cap { + if wgt[i - 1] > c as i32 { + // If exceeds knapsack capacity, don't select item i + dp[c] = dp[c]; + } else { + // The larger value between not selecting and selecting item i + dp[c] = std::cmp::max(dp[c], dp[c - wgt[i - 1] as usize] + val[i - 1]); + } + } + } + dp[cap] +} + +/* Driver Code */ +pub fn main() { + let wgt = [1, 2, 3]; + let val = [5, 11, 15]; + let cap: usize = 4; + + // Dynamic programming + let res = unbounded_knapsack_dp(&wgt, &val, cap); + println!("Maximum item value not exceeding knapsack capacity is {res}"); + + // Space-optimized dynamic programming + let res = unbounded_knapsack_dp_comp(&wgt, &val, cap); + println!("Maximum item value not exceeding knapsack capacity is {res}"); +} diff --git a/en/codes/rust/chapter_graph/graph_adjacency_list.rs b/en/codes/rust/chapter_graph/graph_adjacency_list.rs new file mode 100644 index 000000000..e51e5c39b --- /dev/null +++ b/en/codes/rust/chapter_graph/graph_adjacency_list.rs @@ -0,0 +1,135 @@ +/* + * File: graph_adjacency_list.rs + * Created Time: 2023-07-12 + * Author: night-cruise (2586447362@qq.com) + */ + +pub use hello_algo_rust::include::{vals_to_vets, vets_to_vals, Vertex}; + +use std::collections::HashMap; + +/* Undirected graph type based on adjacency list */ +pub struct GraphAdjList { + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + pub adj_list: HashMap>, // maybe HashSet for value part is better? +} + +impl GraphAdjList { + /* Constructor */ + pub fn new(edges: Vec<[Vertex; 2]>) -> Self { + let mut graph = GraphAdjList { + adj_list: HashMap::new(), + }; + // Add all vertices and edges + for edge in edges { + graph.add_vertex(edge[0]); + graph.add_vertex(edge[1]); + graph.add_edge(edge[0], edge[1]); + } + + graph + } + + /* Get the number of vertices */ + #[allow(unused)] + pub fn size(&self) -> usize { + self.adj_list.len() + } + + /* Add edge */ + pub fn add_edge(&mut self, vet1: Vertex, vet2: Vertex) { + if vet1 == vet2 { + panic!("value error"); + } + // Add edge vet1 - vet2 + self.adj_list.entry(vet1).or_default().push(vet2); + self.adj_list.entry(vet2).or_default().push(vet1); + } + + /* Remove edge */ + #[allow(unused)] + pub fn remove_edge(&mut self, vet1: Vertex, vet2: Vertex) { + if vet1 == vet2 { + panic!("value error"); + } + // Remove edge vet1 - vet2 + self.adj_list + .entry(vet1) + .and_modify(|v| v.retain(|&e| e != vet2)); + self.adj_list + .entry(vet2) + .and_modify(|v| v.retain(|&e| e != vet1)); + } + + /* Add vertex */ + pub fn add_vertex(&mut self, vet: Vertex) { + if self.adj_list.contains_key(&vet) { + return; + } + // Add a new linked list in the adjacency list + self.adj_list.insert(vet, vec![]); + } + + /* Remove vertex */ + #[allow(unused)] + pub fn remove_vertex(&mut self, vet: Vertex) { + // Remove the linked list corresponding to vertex vet in the adjacency list + self.adj_list.remove(&vet); + // Traverse the linked lists of other vertices and remove all edges containing vet + for list in self.adj_list.values_mut() { + list.retain(|&v| v != vet); + } + } + + /* Print adjacency list */ + pub fn print(&self) { + println!("Adjacency list ="); + for (vertex, list) in &self.adj_list { + let list = list.iter().map(|vertex| vertex.val).collect::>(); + println!("{}: {:?},", vertex.val, list); + } + } +} + +/* Driver Code */ +#[allow(unused)] +fn main() { + /* Add edge */ + let v = vals_to_vets(vec![1, 3, 2, 5, 4]); + let edges = vec![ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[2], v[3]], + [v[2], v[4]], + [v[3], v[4]], + ]; + + let mut graph = GraphAdjList::new(edges); + println!("\nAfter initialization, graph is"); + graph.print(); + + /* Add edge */ + // Vertices 1, 3 are v[0], v[1] + graph.add_edge(v[0], v[2]); + println!("\nAfter adding edge 1-2, graph is"); + graph.print(); + + /* Remove edge */ + // Vertex 3 is v[1] + graph.remove_edge(v[0], v[1]); + println!("\nAfter removing edge 1-3, graph is"); + graph.print(); + + /* Add vertex */ + let v5 = Vertex { val: 6 }; + graph.add_vertex(v5); + println!("\nAfter adding vertex 6, graph is"); + graph.print(); + + /* Remove vertex */ + // Vertex 3 is v[1] + graph.remove_vertex(v[1]); + println!("\nAfter removing vertex 3, graph is"); + graph.print(); +} diff --git a/en/codes/rust/chapter_graph/graph_adjacency_matrix.rs b/en/codes/rust/chapter_graph/graph_adjacency_matrix.rs new file mode 100644 index 000000000..1f5269e85 --- /dev/null +++ b/en/codes/rust/chapter_graph/graph_adjacency_matrix.rs @@ -0,0 +1,136 @@ +/* + * File: graph_adjacency_matrix.rs + * Created Time: 2023-07-12 + * Author: night-cruise (2586447362@qq.com) + */ + +/* Undirected graph type based on adjacency matrix */ +pub struct GraphAdjMat { + // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + pub vertices: Vec, + // Adjacency matrix, where the row and column indices correspond to the "vertex index" + pub adj_mat: Vec>, +} + +impl GraphAdjMat { + /* Constructor */ + pub fn new(vertices: Vec, edges: Vec<[usize; 2]>) -> Self { + let mut graph = GraphAdjMat { + vertices: vec![], + adj_mat: vec![], + }; + // Add vertex + for val in vertices { + graph.add_vertex(val); + } + // Add edge + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + for edge in edges { + graph.add_edge(edge[0], edge[1]) + } + + graph + } + + /* Get the number of vertices */ + pub fn size(&self) -> usize { + self.vertices.len() + } + + /* Add vertex */ + pub fn add_vertex(&mut self, val: i32) { + let n = self.size(); + // Add the value of the new vertex to the vertex list + self.vertices.push(val); + // Add a row to the adjacency matrix + self.adj_mat.push(vec![0; n]); + // Add a column to the adjacency matrix + for row in self.adj_mat.iter_mut() { + row.push(0); + } + } + + /* Remove vertex */ + pub fn remove_vertex(&mut self, index: usize) { + if index >= self.size() { + panic!("index error") + } + // Remove the vertex at index from the vertex list + self.vertices.remove(index); + // Remove the row at index from the adjacency matrix + self.adj_mat.remove(index); + // Remove the column at index from the adjacency matrix + for row in self.adj_mat.iter_mut() { + row.remove(index); + } + } + + /* Add edge */ + pub fn add_edge(&mut self, i: usize, j: usize) { + // Parameters i, j correspond to the vertices element indices + // Handle index out of bounds and equality + if i >= self.size() || j >= self.size() || i == j { + panic!("index error") + } + // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + self.adj_mat[i][j] = 1; + self.adj_mat[j][i] = 1; + } + + /* Remove edge */ + // Parameters i, j correspond to the vertices element indices + pub fn remove_edge(&mut self, i: usize, j: usize) { + // Parameters i, j correspond to the vertices element indices + // Handle index out of bounds and equality + if i >= self.size() || j >= self.size() || i == j { + panic!("index error") + } + self.adj_mat[i][j] = 0; + self.adj_mat[j][i] = 0; + } + + /* Print adjacency matrix */ + pub fn print(&self) { + println!("Vertex list = {:?}", self.vertices); + println!("Adjacency matrix ="); + println!("["); + for row in &self.adj_mat { + println!(" {:?},", row); + } + println!("]") + } +} + +/* Driver Code */ +fn main() { + /* Add edge */ + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + let vertices = vec![1, 3, 2, 5, 4]; + let edges = vec![[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]]; + let mut graph = GraphAdjMat::new(vertices, edges); + println!("\nAfter initialization, graph is"); + graph.print(); + + /* Add edge */ + // Add vertex + graph.add_edge(0, 2); + println!("\nAfter adding edge 1-2, graph is"); + graph.print(); + + /* Remove edge */ + // Vertices 1, 3 have indices 0, 1 respectively + graph.remove_edge(0, 1); + println!("\nAfter removing edge 1-3, graph is"); + graph.print(); + + /* Add vertex */ + graph.add_vertex(6); + println!("\nAfter adding vertex 6, graph is"); + graph.print(); + + /* Remove vertex */ + // Vertex 3 has index 1 + graph.remove_vertex(1); + println!("\nAfter removing vertex 3, graph is"); + graph.print(); +} diff --git a/en/codes/rust/chapter_graph/graph_bfs.rs b/en/codes/rust/chapter_graph/graph_bfs.rs new file mode 100644 index 000000000..c28a118fe --- /dev/null +++ b/en/codes/rust/chapter_graph/graph_bfs.rs @@ -0,0 +1,69 @@ +/* + * File: graph_bfs.rs + * Created Time: 2023-07-12 + * Author: night-cruise (2586447362@qq.com) + */ + +mod graph_adjacency_list; + +use graph_adjacency_list::GraphAdjList; +use graph_adjacency_list::{vals_to_vets, vets_to_vals, Vertex}; +use std::collections::{HashSet, VecDeque}; + +/* Breadth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +fn graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> Vec { + // Vertex traversal sequence + let mut res = vec![]; + // Hash set for recording vertices that have been visited + let mut visited = HashSet::new(); + visited.insert(start_vet); + // Queue used to implement BFS + let mut que = VecDeque::new(); + que.push_back(start_vet); + // Starting from vertex vet, loop until all vertices are visited + while let Some(vet) = que.pop_front() { + res.push(vet); // Record visited vertex + + // Traverse all adjacent vertices of this vertex + if let Some(adj_vets) = graph.adj_list.get(&vet) { + for &adj_vet in adj_vets { + if visited.contains(&adj_vet) { + continue; // Skip vertices that have been visited + } + que.push_back(adj_vet); // Only enqueue unvisited vertices + visited.insert(adj_vet); // Mark this vertex as visited + } + } + } + // Return vertex traversal sequence + res +} + +/* Driver Code */ +fn main() { + /* Add edge */ + let v = vals_to_vets(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + let edges = vec![ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[1], v[4]], + [v[2], v[5]], + [v[3], v[4]], + [v[3], v[6]], + [v[4], v[5]], + [v[4], v[7]], + [v[5], v[8]], + [v[6], v[7]], + [v[7], v[8]], + ]; + let graph = GraphAdjList::new(edges); + println!("\nAfter initialization, graph is"); + graph.print(); + + /* Breadth-first traversal */ + let res = graph_bfs(graph, v[0]); + println!("\nBreadth-first traversal (BFS) vertex sequence is"); + println!("{:?}", vets_to_vals(res)); +} diff --git a/en/codes/rust/chapter_graph/graph_dfs.rs b/en/codes/rust/chapter_graph/graph_dfs.rs new file mode 100644 index 000000000..3f405659c --- /dev/null +++ b/en/codes/rust/chapter_graph/graph_dfs.rs @@ -0,0 +1,61 @@ +/* + * File: graph_dfs.rs + * Created Time: 2023-07-12 + * Author: night-cruise (2586447362@qq.com) + */ + +mod graph_adjacency_list; + +use graph_adjacency_list::GraphAdjList; +use graph_adjacency_list::{vals_to_vets, vets_to_vals, Vertex}; +use std::collections::HashSet; + +/* Depth-first traversal helper function */ +fn dfs(graph: &GraphAdjList, visited: &mut HashSet, res: &mut Vec, vet: Vertex) { + res.push(vet); // Record visited vertex + visited.insert(vet); // Mark this vertex as visited + // Traverse all adjacent vertices of this vertex + if let Some(adj_vets) = graph.adj_list.get(&vet) { + for &adj_vet in adj_vets { + if visited.contains(&adj_vet) { + continue; // Skip vertices that have been visited + } + // Recursively visit adjacent vertices + dfs(graph, visited, res, adj_vet); + } + } +} + +/* Depth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +fn graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> Vec { + // Vertex traversal sequence + let mut res = vec![]; + // Hash set for recording vertices that have been visited + let mut visited = HashSet::new(); + dfs(&graph, &mut visited, &mut res, start_vet); + + res +} + +/* Driver Code */ +fn main() { + /* Add edge */ + let v = vals_to_vets(vec![0, 1, 2, 3, 4, 5, 6]); + let edges = vec![ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[2], v[5]], + [v[4], v[5]], + [v[5], v[6]], + ]; + let graph = GraphAdjList::new(edges); + println!("\nAfter initialization, graph is"); + graph.print(); + + /* Depth-first traversal */ + let res = graph_dfs(graph, v[0]); + println!("\nDepth-first traversal (DFS) vertex sequence is"); + println!("{:?}", vets_to_vals(res)); +} diff --git a/en/codes/rust/chapter_greedy/coin_change_greedy.rs b/en/codes/rust/chapter_greedy/coin_change_greedy.rs new file mode 100644 index 000000000..7a25cedf8 --- /dev/null +++ b/en/codes/rust/chapter_greedy/coin_change_greedy.rs @@ -0,0 +1,54 @@ +/* + * File: coin_change_greedy.rs + * Created Time: 2023-07-22 + * Author: night-cruise (2586447362@qq.com) + */ + +/* Coin change: Greedy algorithm */ +fn coin_change_greedy(coins: &[i32], mut amt: i32) -> i32 { + // Assume coins list is sorted + let mut i = coins.len() - 1; + let mut count = 0; + // Loop to make greedy choices until no remaining amount + while amt > 0 { + // Find the coin that is less than and closest to the remaining amount + while i > 0 && coins[i] > amt { + i -= 1; + } + // Choose coins[i] + amt -= coins[i]; + count += 1; + } + // If no feasible solution is found, return -1 + if amt == 0 { + count + } else { + -1 + } +} + +/* Driver Code */ +fn main() { + // Greedy algorithm: Can guarantee finding the global optimal solution + let coins = [1, 5, 10, 20, 50, 100]; + let amt = 186; + let res = coin_change_greedy(&coins, amt); + println!("\ncoins = {:?}, amt = {}", coins, amt); + println!("Minimum coins needed to make {} is {}", amt, res); + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + let coins = [1, 20, 50]; + let amt = 60; + let res = coin_change_greedy(&coins, amt); + println!("\ncoins = {:?}, amt = {}", coins, amt); + println!("Minimum coins needed to make {} is {}", amt, res); + println!("Actually the minimum number needed is 3, i.e., 20 + 20 + 20"); + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + let coins = [1, 49, 50]; + let amt = 98; + let res = coin_change_greedy(&coins, amt); + println!("\ncoins = {:?}, amt = {}", coins, amt); + println!("Minimum coins needed to make {} is {}", amt, res); + println!("Actually the minimum number needed is 2, i.e., 49 + 49"); +} diff --git a/en/codes/rust/chapter_greedy/fractional_knapsack.rs b/en/codes/rust/chapter_greedy/fractional_knapsack.rs new file mode 100644 index 000000000..eea768d18 --- /dev/null +++ b/en/codes/rust/chapter_greedy/fractional_knapsack.rs @@ -0,0 +1,59 @@ +/* + * File: coin_change_greedy.rs + * Created Time: 2023-07-22 + * Author: night-cruise (2586447362@qq.com) + */ + +/* Item */ +struct Item { + w: i32, // Item weight + v: i32, // Item value +} + +impl Item { + fn new(w: i32, v: i32) -> Self { + Self { w, v } + } +} + +/* Fractional knapsack: Greedy algorithm */ +fn fractional_knapsack(wgt: &[i32], val: &[i32], mut cap: i32) -> f64 { + // Create item list with two attributes: weight, value + let mut items = wgt + .iter() + .zip(val.iter()) + .map(|(&w, &v)| Item::new(w, v)) + .collect::>(); + // Sort by unit value item.v / item.w from high to low + items.sort_by(|a, b| { + (b.v as f64 / b.w as f64) + .partial_cmp(&(a.v as f64 / a.w as f64)) + .unwrap() + }); + // Loop for greedy selection + let mut res = 0.0; + for item in &items { + if item.w <= cap { + // If remaining capacity is sufficient, put the entire current item into the knapsack + res += item.v as f64; + cap -= item.w; + } else { + // If remaining capacity is insufficient, put part of the current item into the knapsack + res += item.v as f64 / item.w as f64 * cap as f64; + // No remaining capacity, so break out of the loop + break; + } + } + res +} + +/* Driver Code */ +fn main() { + let wgt = [10, 20, 30, 40, 50]; + let val = [50, 120, 150, 210, 240]; + let cap = 50; + + // Greedy algorithm + let res = fractional_knapsack(&wgt, &val, cap); + println!("Maximum item value not exceeding knapsack capacity is {}", res); +} diff --git a/en/codes/rust/chapter_greedy/max_capacity.rs b/en/codes/rust/chapter_greedy/max_capacity.rs new file mode 100644 index 000000000..5e7fcb68b --- /dev/null +++ b/en/codes/rust/chapter_greedy/max_capacity.rs @@ -0,0 +1,36 @@ +/* + * File: coin_change_greedy.rs + * Created Time: 2023-07-22 + * Author: night-cruise (2586447362@qq.com) + */ + +/* Max capacity: Greedy algorithm */ +fn max_capacity(ht: &[i32]) -> i32 { + // Initialize i, j to be at both ends of the array + let mut i = 0; + let mut j = ht.len() - 1; + // Initial max capacity is 0 + let mut res = 0; + // Loop for greedy selection until the two boards meet + while i < j { + // Update max capacity + let cap = std::cmp::min(ht[i], ht[j]) * (j - i) as i32; + res = std::cmp::max(res, cap); + // Move the shorter board inward + if ht[i] < ht[j] { + i += 1; + } else { + j -= 1; + } + } + res +} + +/* Driver Code */ +fn main() { + let ht = [3, 8, 5, 2, 7, 7, 3, 4]; + + // Greedy algorithm + let res = max_capacity(&ht); + println!("Maximum capacity is {}", res); +} diff --git a/en/codes/rust/chapter_greedy/max_product_cutting.rs b/en/codes/rust/chapter_greedy/max_product_cutting.rs new file mode 100644 index 000000000..c8348230c --- /dev/null +++ b/en/codes/rust/chapter_greedy/max_product_cutting.rs @@ -0,0 +1,35 @@ +/* + * File: coin_change_greedy.rs + * Created Time: 2023-07-22 + * Author: night-cruise (2586447362@qq.com) + */ + +/* Max product cutting: Greedy algorithm */ +fn max_product_cutting(n: i32) -> i32 { + // When n <= 3, must cut out a 1 + if n <= 3 { + return 1 * (n - 1); + } + // Greedily cut out 3, a is the number of 3s, b is the remainder + let a = n / 3; + let b = n % 3; + if b == 1 { + // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + 3_i32.pow(a as u32 - 1) * 2 * 2 + } else if b == 2 { + // When the remainder is 2, do nothing + 3_i32.pow(a as u32) * 2 + } else { + // When the remainder is 0, do nothing + 3_i32.pow(a as u32) + } +} + +/* Driver Code */ +fn main() { + let n = 58; + + // Greedy algorithm + let res = max_product_cutting(n); + println!("Maximum cutting product is {}", res); +} diff --git a/en/codes/rust/chapter_hashing/array_hash_map.rs b/en/codes/rust/chapter_hashing/array_hash_map.rs new file mode 100644 index 000000000..6f3dfb0ea --- /dev/null +++ b/en/codes/rust/chapter_hashing/array_hash_map.rs @@ -0,0 +1,124 @@ +/** + * File: array_hash_map.rs + * Created Time: 2023-2-18 + * Author: xBLACICEx (xBLACKICEx@outlook.com) + */ + +/* Key-value pair */ +#[derive(Debug, Clone, PartialEq)] +pub struct Pair { + pub key: i32, + pub val: String, +} +/* Hash table based on array implementation */ +pub struct ArrayHashMap { + buckets: Vec>, +} + +impl ArrayHashMap { + pub fn new() -> ArrayHashMap { + // Initialize array with 100 buckets + Self { + buckets: vec![None; 100], + } + } + + /* Hash function */ + fn hash_func(&self, key: i32) -> usize { + key as usize % 100 + } + + /* Query operation */ + pub fn get(&self, key: i32) -> Option<&String> { + let index = self.hash_func(key); + self.buckets[index].as_ref().map(|pair| &pair.val) + } + + /* Add operation */ + pub fn put(&mut self, key: i32, val: &str) { + let index = self.hash_func(key); + self.buckets[index] = Some(Pair { + key, + val: val.to_string(), + }); + } + + /* Remove operation */ + pub fn remove(&mut self, key: i32) { + let index = self.hash_func(key); + // Set to None to represent removal + self.buckets[index] = None; + } + + /* Get all key-value pairs */ + pub fn entry_set(&self) -> Vec<&Pair> { + self.buckets + .iter() + .filter_map(|pair| pair.as_ref()) + .collect() + } + + /* Get all keys */ + pub fn key_set(&self) -> Vec<&i32> { + self.buckets + .iter() + .filter_map(|pair| pair.as_ref().map(|pair| &pair.key)) + .collect() + } + + /* Get all values */ + pub fn value_set(&self) -> Vec<&String> { + self.buckets + .iter() + .filter_map(|pair| pair.as_ref().map(|pair| &pair.val)) + .collect() + } + + /* Print hash table */ + pub fn print(&self) { + for pair in self.entry_set() { + println!("{} -> {}", pair.key, pair.val); + } + } +} + +fn main() { + /* Initialize hash table */ + let mut map = ArrayHashMap::new(); + /* Add operation */ + // Add key-value pair (key, value) to hash table + map.put(12836, "Xiao Ha"); + map.put(15937, "Xiao Luo"); + map.put(16750, "Xiao Suan"); + map.put(13276, "Xiao Fa"); + map.put(10583, "Xiao Ya"); + println!("\nAfter adding is complete, hash table is\nKey -> Value"); + map.print(); + + /* Query operation */ + // Input key into hash table to get value + let name = map.get(15937).unwrap(); + println!("\nInput student ID 15937, found name {}", name); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(10583); + println!("\nAfter removing 10583, hash table is\nKey -> Value"); + map.print(); + + /* Traverse hash table */ + println!("\nTraverse key-value pairs Key->Value"); + for pair in map.entry_set() { + println!("{} -> {}", pair.key, pair.val); + } + + println!("\nTraverse keys only Key"); + for key in map.key_set() { + println!("{}", key); + } + + println!("\nTraverse values only Value"); + for val in map.value_set() { + println!("{}", val); + } +} diff --git a/en/codes/rust/chapter_hashing/build_in_hash.rs b/en/codes/rust/chapter_hashing/build_in_hash.rs new file mode 100644 index 000000000..1ecd0296c --- /dev/null +++ b/en/codes/rust/chapter_hashing/build_in_hash.rs @@ -0,0 +1,49 @@ +/* + * File: build_in_hash.rs + * Created Time: 2023-7-6 + * Author: WSL0809 (wslzzy@outlook.com) + */ + +use hello_algo_rust::include::ListNode; + +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; + +/* Driver Code */ +fn main() { + let num = 3; + let mut num_hasher = DefaultHasher::new(); + num.hash(&mut num_hasher); + let hash_num = num_hasher.finish(); + println!("Hash value of integer {} is {}", num, hash_num); + + let bol = true; + let mut bol_hasher = DefaultHasher::new(); + bol.hash(&mut bol_hasher); + let hash_bol = bol_hasher.finish(); + println!("Hash value of boolean {} is {}", bol, hash_bol); + + let dec: f32 = 3.14159; + let mut dec_hasher = DefaultHasher::new(); + dec.to_bits().hash(&mut dec_hasher); + let hash_dec = dec_hasher.finish(); + println!("Hash value of decimal {} is {}", dec, hash_dec); + + let str = "Hello Algo"; + let mut str_hasher = DefaultHasher::new(); + str.hash(&mut str_hasher); + let hash_str = str_hasher.finish(); + println!("Hash value of string {} is {}", str, hash_str); + + let arr = (&12836, &"Xiao Ha"); + let mut tup_hasher = DefaultHasher::new(); + arr.hash(&mut tup_hasher); + let hash_tup = tup_hasher.finish(); + println!("Hash value of tuple {:?} is {}", arr, hash_tup); + + let node = ListNode::new(42); + let mut hasher = DefaultHasher::new(); + node.borrow().val.hash(&mut hasher); + let hash = hasher.finish(); + println!("Hash value of node object {:?} is {}", node, hash); +} diff --git a/en/codes/rust/chapter_hashing/hash_map.rs b/en/codes/rust/chapter_hashing/hash_map.rs new file mode 100644 index 000000000..cd39b3828 --- /dev/null +++ b/en/codes/rust/chapter_hashing/hash_map.rs @@ -0,0 +1,48 @@ +/* + * File: hash_map.rs + * Created Time: 2023-02-05 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::print_util; + +use std::collections::HashMap; + +/* Driver Code */ +pub fn main() { + // Initialize hash table + let mut map = HashMap::new(); + + // Add operation + // Add key-value pair (key, value) to the hash table + map.insert(12836, "Xiao Ha"); + map.insert(15937, "Xiao Luo"); + map.insert(16750, "Xiao Suan"); + map.insert(13276, "Xiao Fa"); + map.insert(10583, "Xiao Ya"); + println!("\nAfter adding is complete, hash table is\nKey -> Value"); + print_util::print_hash_map(&map); + + // Query operation + // Input key into hash table to get value + let name = map.get(&15937).copied().unwrap(); + println!("\nInput student ID 15937, found name {name}"); + + // Remove operation + // Remove key-value pair (key, value) from hash table + _ = map.remove(&10583); + println!("\nAfter removing 10583, hash table is\nKey -> Value"); + print_util::print_hash_map(&map); + + // Traverse hash table + println!("\nTraverse key-value pairs Key->Value"); + print_util::print_hash_map(&map); + println!("\nTraverse keys only Key"); + for key in map.keys() { + println!("{key}"); + } + println!("\nTraverse values separately"); + for value in map.values() { + println!("{value}"); + } +} diff --git a/en/codes/rust/chapter_hashing/hash_map_chaining.rs b/en/codes/rust/chapter_hashing/hash_map_chaining.rs new file mode 100644 index 000000000..4fc4bf1f8 --- /dev/null +++ b/en/codes/rust/chapter_hashing/hash_map_chaining.rs @@ -0,0 +1,160 @@ +/* + * File: hash_map_chaining.rs + * Created Time: 2023-07-07 + * Author: WSL0809 (wslzzy@outlook.com) + */ + +#[derive(Clone)] +/* Key-value pair */ +struct Pair { + key: i32, + val: String, +} + +/* Hash table with separate chaining */ +struct HashMapChaining { + size: usize, + capacity: usize, + load_thres: f32, + extend_ratio: usize, + buckets: Vec>, +} + +impl HashMapChaining { + /* Constructor */ + fn new() -> Self { + Self { + size: 0, + capacity: 4, + load_thres: 2.0 / 3.0, + extend_ratio: 2, + buckets: vec![vec![]; 4], + } + } + + /* Hash function */ + fn hash_func(&self, key: i32) -> usize { + key as usize % self.capacity + } + + /* Load factor */ + fn load_factor(&self) -> f32 { + self.size as f32 / self.capacity as f32 + } + + /* Remove operation */ + fn remove(&mut self, key: i32) -> Option { + let index = self.hash_func(key); + + // Traverse bucket and remove key-value pair from it + for (i, p) in self.buckets[index].iter_mut().enumerate() { + if p.key == key { + let pair = self.buckets[index].remove(i); + self.size -= 1; + return Some(pair.val); + } + } + + // If key is not found, return None + None + } + + /* Expand hash table */ + fn extend(&mut self) { + // Temporarily store the original hash table + let buckets_tmp = std::mem::take(&mut self.buckets); + + // Initialize expanded new hash table + self.capacity *= self.extend_ratio; + self.buckets = vec![Vec::new(); self.capacity as usize]; + self.size = 0; + + // Move key-value pairs from original hash table to new hash table + for bucket in buckets_tmp { + for pair in bucket { + self.put(pair.key, pair.val); + } + } + } + + /* Print hash table */ + fn print(&self) { + for bucket in &self.buckets { + let mut res = Vec::new(); + for pair in bucket { + res.push(format!("{} -> {}", pair.key, pair.val)); + } + println!("{:?}", res); + } + } + + /* Add operation */ + fn put(&mut self, key: i32, val: String) { + // When load factor exceeds threshold, perform expansion + if self.load_factor() > self.load_thres { + self.extend(); + } + + let index = self.hash_func(key); + + // Traverse bucket, if specified key is encountered, update corresponding val and return + for pair in self.buckets[index].iter_mut() { + if pair.key == key { + pair.val = val; + return; + } + } + + // If key does not exist, append key-value pair to the end + let pair = Pair { key, val }; + self.buckets[index].push(pair); + self.size += 1; + } + + /* Query operation */ + fn get(&self, key: i32) -> Option<&str> { + let index = self.hash_func(key); + + // Traverse bucket, if key is found, return corresponding val + for pair in self.buckets[index].iter() { + if pair.key == key { + return Some(&pair.val); + } + } + + // If key is not found, return None + None + } +} + +/* Driver Code */ +pub fn main() { + /* Initialize hash table */ + let mut map = HashMapChaining::new(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.put(12836, "Xiao Ha".to_string()); + map.put(15937, "Xiao Luo".to_string()); + map.put(16750, "Xiao Suan".to_string()); + map.put(13276, "Xiao Fa".to_string()); + map.put(10583, "Xiao Ya".to_string()); + println!("\nAfter adding is complete, hash table is\nKey -> Value"); + map.print(); + + /* Query operation */ + // Input key into hash table to get value + println!( + "\nInput student ID 13276, found name {}", + match map.get(13276) { + Some(value) => value, + None => "Not a valid Key", + } + ); + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(12836); + println!("\nAfter removing 12836, hash table is\nKey -> Value"); + map.print(); +} diff --git a/en/codes/rust/chapter_hashing/hash_map_open_addressing.rs b/en/codes/rust/chapter_hashing/hash_map_open_addressing.rs new file mode 100644 index 000000000..999b652f4 --- /dev/null +++ b/en/codes/rust/chapter_hashing/hash_map_open_addressing.rs @@ -0,0 +1,181 @@ +/* + * File: hash_map_open_addressing.rs + * Created Time: 2023-07-16 + * Author: WSL0809 (wslzzy@outlook.com), night-cruise (2586447362@qq.com) + */ +#![allow(non_snake_case)] +#![allow(unused)] + +mod array_hash_map; + +use array_hash_map::Pair; + +/* Hash table with open addressing */ +struct HashMapOpenAddressing { + size: usize, // Number of key-value pairs + capacity: usize, // Hash table capacity + load_thres: f64, // Load factor threshold for triggering expansion + extend_ratio: usize, // Expansion multiplier + buckets: Vec>, // Bucket array + TOMBSTONE: Option, // Removal marker +} + +impl HashMapOpenAddressing { + /* Constructor */ + fn new() -> Self { + Self { + size: 0, + capacity: 4, + load_thres: 2.0 / 3.0, + extend_ratio: 2, + buckets: vec![None; 4], + TOMBSTONE: Some(Pair { + key: -1, + val: "-1".to_string(), + }), + } + } + + /* Hash function */ + fn hash_func(&self, key: i32) -> usize { + (key % self.capacity as i32) as usize + } + + /* Load factor */ + fn load_factor(&self) -> f64 { + self.size as f64 / self.capacity as f64 + } + + /* Search for bucket index corresponding to key */ + fn find_bucket(&mut self, key: i32) -> usize { + let mut index = self.hash_func(key); + let mut first_tombstone = -1; + // Linear probing, break when encountering an empty bucket + while self.buckets[index].is_some() { + // If key is found, return corresponding bucket index + if self.buckets[index].as_ref().unwrap().key == key { + // If deletion marker was encountered before, move key-value pair to that index + if first_tombstone != -1 { + self.buckets[first_tombstone as usize] = self.buckets[index].take(); + self.buckets[index] = self.TOMBSTONE.clone(); + return first_tombstone as usize; // Return the moved bucket index + } + return index; // Return bucket index + } + // Record the first removal marker encountered + if first_tombstone == -1 && self.buckets[index] == self.TOMBSTONE { + first_tombstone = index as i32; + } + // Calculate bucket index, wrap around to the head if past the tail + index = (index + 1) % self.capacity; + } + // If key does not exist, return the index for insertion + if first_tombstone == -1 { + index + } else { + first_tombstone as usize + } + } + + /* Query operation */ + fn get(&mut self, key: i32) -> Option<&str> { + // Search for bucket index corresponding to key + let index = self.find_bucket(key); + // If key-value pair is found, return corresponding val + if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE { + return self.buckets[index].as_ref().map(|pair| &pair.val as &str); + } + // If key-value pair does not exist, return null + None + } + + /* Add operation */ + fn put(&mut self, key: i32, val: String) { + // When load factor exceeds threshold, perform expansion + if self.load_factor() > self.load_thres { + self.extend(); + } + // Search for bucket index corresponding to key + let index = self.find_bucket(key); + // If key-value pair is found, overwrite val and return + if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE { + self.buckets[index].as_mut().unwrap().val = val; + return; + } + // If key-value pair does not exist, add the key-value pair + self.buckets[index] = Some(Pair { key, val }); + self.size += 1; + } + + /* Remove operation */ + fn remove(&mut self, key: i32) { + // Search for bucket index corresponding to key + let index = self.find_bucket(key); + // If key-value pair is found, overwrite it with removal marker + if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE { + self.buckets[index] = self.TOMBSTONE.clone(); + self.size -= 1; + } + } + + /* Expand hash table */ + fn extend(&mut self) { + // Temporarily store the original hash table + let buckets_tmp = self.buckets.clone(); + // Initialize expanded new hash table + self.capacity *= self.extend_ratio; + self.buckets = vec![None; self.capacity]; + self.size = 0; + + // Move key-value pairs from original hash table to new hash table + for pair in buckets_tmp { + if pair.is_none() || pair == self.TOMBSTONE { + continue; + } + let pair = pair.unwrap(); + + self.put(pair.key, pair.val); + } + } + /* Print hash table */ + fn print(&self) { + for pair in &self.buckets { + if pair.is_none() { + println!("null"); + } else if pair == &self.TOMBSTONE { + println!("TOMBSTONE"); + } else { + let pair = pair.as_ref().unwrap(); + println!("{} -> {}", pair.key, pair.val); + } + } + } +} + +/* Driver Code */ +fn main() { + /* Initialize hash table */ + let mut hashmap = HashMapOpenAddressing::new(); + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + hashmap.put(12836, "Xiao Ha".to_string()); + hashmap.put(15937, "Xiao Luo".to_string()); + hashmap.put(16750, "Xiao Suan".to_string()); + hashmap.put(13276, "Xiao Fa".to_string()); + hashmap.put(10583, "Xiao Ya".to_string()); + + println!("\nAfter adding is complete, hash table is\nKey -> Value"); + hashmap.print(); + + /* Query operation */ + // Input key into hash table to get value val + let name = hashmap.get(13276).unwrap(); + println!("\nInput student ID 13276, found name {}", name); + + /* Remove operation */ + // Remove key-value pair (key, val) from hash table + hashmap.remove(16750); + println!("\nAfter removing 16750, hash table is\nKey -> Value"); + hashmap.print(); +} diff --git a/en/codes/rust/chapter_hashing/simple_hash.rs b/en/codes/rust/chapter_hashing/simple_hash.rs new file mode 100644 index 000000000..373620351 --- /dev/null +++ b/en/codes/rust/chapter_hashing/simple_hash.rs @@ -0,0 +1,70 @@ +/* + * File: simple_hash.rs + * Created Time: 2023-09-07 + * Author: night-cruise (2586447362@qq.com) + */ + +/* Additive hash */ +fn add_hash(key: &str) -> i32 { + let mut hash = 0_i64; + const MODULUS: i64 = 1000000007; + + for c in key.chars() { + hash = (hash + c as i64) % MODULUS; + } + + hash as i32 +} + +/* Multiplicative hash */ +fn mul_hash(key: &str) -> i32 { + let mut hash = 0_i64; + const MODULUS: i64 = 1000000007; + + for c in key.chars() { + hash = (31 * hash + c as i64) % MODULUS; + } + + hash as i32 +} + +/* XOR hash */ +fn xor_hash(key: &str) -> i32 { + let mut hash = 0_i64; + const MODULUS: i64 = 1000000007; + + for c in key.chars() { + hash ^= c as i64; + } + + (hash & MODULUS) as i32 +} + +/* Rotational hash */ +fn rot_hash(key: &str) -> i32 { + let mut hash = 0_i64; + const MODULUS: i64 = 1000000007; + + for c in key.chars() { + hash = ((hash << 4) ^ (hash >> 28) ^ c as i64) % MODULUS; + } + + hash as i32 +} + +/* Driver Code */ +fn main() { + let key = "Hello Algo"; + + let hash = add_hash(key); + println!("Additive hash value is {hash}"); + + let hash = mul_hash(key); + println!("Multiplicative hash value is {hash}"); + + let hash = xor_hash(key); + println!("XOR hash value is {hash}"); + + let hash = rot_hash(key); + println!("Rotational hash value is {hash}"); +} diff --git a/en/codes/rust/chapter_heap/heap.rs b/en/codes/rust/chapter_heap/heap.rs new file mode 100644 index 000000000..b7320d4f8 --- /dev/null +++ b/en/codes/rust/chapter_heap/heap.rs @@ -0,0 +1,71 @@ +/* + * File: heap.rs + * Created Time: 2023-07-16 + * Author: night-cruise (2586447362@qq.com) + */ + +use hello_algo_rust::include::print_util; + +use std::{cmp::Reverse, collections::BinaryHeap}; + +fn test_push_max(heap: &mut BinaryHeap, val: i32) { + heap.push(val); // Element enters heap + println!("\nAfter element {} pushes to heap", val); + print_util::print_heap(heap.iter().map(|&val| val).collect()); +} + +fn test_pop_max(heap: &mut BinaryHeap) { + let val = heap.pop().unwrap(); + println!("\nAfter heap top element {} pops from heap", val); + print_util::print_heap(heap.iter().map(|&val| val).collect()); +} + +/* Driver Code */ +fn main() { + /* Initialize heap */ + // Python's heapq module implements min heap by default + #[allow(unused_assignments)] + let mut min_heap = BinaryHeap::new(); + // Rust's BinaryHeap is a max heap, min heap typically wraps elements with Reverse + // Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap + let mut max_heap = BinaryHeap::new(); + + println!("\nThe following test cases are for max heap"); + + /* Element enters heap */ + test_push_max(&mut max_heap, 1); + test_push_max(&mut max_heap, 3); + test_push_max(&mut max_heap, 2); + test_push_max(&mut max_heap, 5); + test_push_max(&mut max_heap, 4); + + /* Check if heap is empty */ + let peek = max_heap.peek().unwrap(); + println!("\nHeap top element is {}", peek); + + /* Time complexity is O(n), not O(nlogn) */ + test_pop_max(&mut max_heap); + test_pop_max(&mut max_heap); + test_pop_max(&mut max_heap); + test_pop_max(&mut max_heap); + test_pop_max(&mut max_heap); + + /* Get heap size */ + let size = max_heap.len(); + println!("\nHeap size is {}", size); + + /* Check if heap is empty */ + let is_empty = max_heap.is_empty(); + println!("\nIs heap empty {}", is_empty); + + /* Input list and build heap */ + // Time complexity is O(n), not O(nlogn) + min_heap = BinaryHeap::from( + vec![1, 3, 2, 5, 4] + .into_iter() + .map(|val| Reverse(val)) + .collect::>>(), + ); + println!("\nAfter inputting list and building min heap"); + print_util::print_heap(min_heap.iter().map(|&val| val.0).collect()); +} diff --git a/en/codes/rust/chapter_heap/my_heap.rs b/en/codes/rust/chapter_heap/my_heap.rs new file mode 100644 index 000000000..07706c7d6 --- /dev/null +++ b/en/codes/rust/chapter_heap/my_heap.rs @@ -0,0 +1,165 @@ +/* + * File: my_heap.rs + * Created Time: 2023-07-16 + * Author: night-cruise (2586447362@qq.com) + */ + +use hello_algo_rust::include::print_util; + +/* Max heap */ +struct MaxHeap { + // Use vector instead of array to avoid capacity concerns + max_heap: Vec, +} + +impl MaxHeap { + /* Constructor, build heap based on input list */ + fn new(nums: Vec) -> Self { + // Add list elements to heap as is + let mut heap = MaxHeap { max_heap: nums }; + // Heapify all nodes except leaf nodes + for i in (0..=Self::parent(heap.size() - 1)).rev() { + heap.sift_down(i); + } + heap + } + + /* Get index of left child node */ + fn left(i: usize) -> usize { + 2 * i + 1 + } + + /* Get index of right child node */ + fn right(i: usize) -> usize { + 2 * i + 2 + } + + /* Get index of parent node */ + fn parent(i: usize) -> usize { + (i - 1) / 2 // Floor division + } + + /* Swap elements */ + fn swap(&mut self, i: usize, j: usize) { + self.max_heap.swap(i, j); + } + + /* Get heap size */ + fn size(&self) -> usize { + self.max_heap.len() + } + + /* Check if heap is empty */ + fn is_empty(&self) -> bool { + self.max_heap.is_empty() + } + + /* Access top element */ + fn peek(&self) -> Option { + self.max_heap.first().copied() + } + + /* Element enters heap */ + fn push(&mut self, val: i32) { + // Add node + self.max_heap.push(val); + // Heapify from bottom to top + self.sift_up(self.size() - 1); + } + + /* Starting from node i, heapify from bottom to top */ + fn sift_up(&mut self, mut i: usize) { + loop { + // Node i is already the heap root, end heapification + if i == 0 { + break; + } + // Get parent node of node i + let p = Self::parent(i); + // When "node needs no repair", end heapification + if self.max_heap[i] <= self.max_heap[p] { + break; + } + // Swap two nodes + self.swap(i, p); + // Loop upward heapify + i = p; + } + } + + /* Element exits heap */ + fn pop(&mut self) -> i32 { + // Handle empty case + if self.is_empty() { + panic!("index out of bounds"); + } + // Delete node + self.swap(0, self.size() - 1); + // Remove node + let val = self.max_heap.pop().unwrap(); + // Return top element + self.sift_down(0); + // Return heap top element + val + } + + /* Starting from node i, heapify from top to bottom */ + fn sift_down(&mut self, mut i: usize) { + loop { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + let (l, r, mut ma) = (Self::left(i), Self::right(i), i); + if l < self.size() && self.max_heap[l] > self.max_heap[ma] { + ma = l; + } + if r < self.size() && self.max_heap[r] > self.max_heap[ma] { + ma = r; + } + // Swap two nodes + if ma == i { + break; + } + // Swap two nodes + self.swap(i, ma); + // Loop downwards heapification + i = ma; + } + } + + /* Driver Code */ + fn print(&self) { + print_util::print_heap(self.max_heap.clone()); + } +} + +/* Driver Code */ +fn main() { + /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + let mut max_heap = MaxHeap::new(vec![9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); + println!("\nAfter inputting list and building heap"); + max_heap.print(); + + /* Check if heap is empty */ + let peek = max_heap.peek(); + if let Some(peek) = peek { + println!("\nHeap top element is {}", peek); + } + + /* Element enters heap */ + let val = 7; + max_heap.push(val); + println!("\nAfter element {} pushes to heap", val); + max_heap.print(); + + /* Time complexity is O(n), not O(nlogn) */ + let peek = max_heap.pop(); + println!("\nAfter heap top element {} pops from heap", peek); + max_heap.print(); + + /* Get heap size */ + let size = max_heap.size(); + println!("\nHeap size is {}", size); + + /* Check if heap is empty */ + let is_empty = max_heap.is_empty(); + println!("\nIs heap empty {}", is_empty); +} diff --git a/en/codes/rust/chapter_heap/top_k.rs b/en/codes/rust/chapter_heap/top_k.rs new file mode 100644 index 000000000..6362da5a9 --- /dev/null +++ b/en/codes/rust/chapter_heap/top_k.rs @@ -0,0 +1,39 @@ +/* + * File: top_k.rs + * Created Time: 2023-07-16 + * Author: night-cruise (2586447362@qq.com) + */ + +use hello_algo_rust::include::print_util; + +use std::cmp::Reverse; +use std::collections::BinaryHeap; + +/* Find the largest k elements in array based on heap */ +fn top_k_heap(nums: Vec, k: usize) -> BinaryHeap> { + // BinaryHeap is a max heap, use Reverse to negate elements to implement min heap + let mut heap = BinaryHeap::>::new(); + // Enter the first k elements of array into heap + for &num in nums.iter().take(k) { + heap.push(Reverse(num)); + } + // Starting from the (k+1)th element, maintain heap length as k + for &num in nums.iter().skip(k) { + // If current element is greater than top element, top element exits heap, current element enters heap + if num > heap.peek().unwrap().0 { + heap.pop(); + heap.push(Reverse(num)); + } + } + heap +} + +/* Driver Code */ +fn main() { + let nums = vec![1, 7, 6, 3, 2]; + let k = 3; + + let res = top_k_heap(nums, k); + println!("The largest {} elements are", k); + print_util::print_heap(res.into_iter().map(|item| item.0).collect()); +} diff --git a/en/codes/rust/chapter_searching/binary_search.rs b/en/codes/rust/chapter_searching/binary_search.rs new file mode 100644 index 000000000..12f833c9a --- /dev/null +++ b/en/codes/rust/chapter_searching/binary_search.rs @@ -0,0 +1,65 @@ +/* + * File: binary_search.rs + * Created Time: 2023-02-05 + * Author: codingonion (coderonion@gmail.com) + */ + +/* Binary search (closed interval on both sides) */ +fn binary_search(nums: &[i32], target: i32) -> i32 { + // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + let mut i = 0; + let mut j = nums.len() as i32 - 1; + // Loop, exit when the search interval is empty (empty when i > j) + while i <= j { + let m = i + (j - i) / 2; // Calculate the midpoint index m + if nums[m as usize] < target { + // This means target is in the interval [m+1, j] + i = m + 1; + } else if nums[m as usize] > target { + // This means target is in the interval [i, m-1] + j = m - 1; + } else { + // Found the target element, return its index + return m; + } + } + // Target element not found, return -1 + return -1; +} + +/* Binary search (left-closed right-open interval) */ +fn binary_search_lcro(nums: &[i32], target: i32) -> i32 { + // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + let mut i = 0; + let mut j = nums.len() as i32; + // Loop, exit when the search interval is empty (empty when i = j) + while i < j { + let m = i + (j - i) / 2; // Calculate the midpoint index m + if nums[m as usize] < target { + // This means target is in the interval [m+1, j) + i = m + 1; + } else if nums[m as usize] > target { + // This means target is in the interval [i, m) + j = m; + } else { + // Found the target element, return its index + return m; + } + } + // Target element not found, return -1 + return -1; +} + +/* Driver Code */ +pub fn main() { + let target = 6; + let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; + + // Binary search (closed interval on both sides) + let mut index = binary_search(&nums, target); + println!("Index of target element 6 is {index}"); + + // Binary search (left-closed right-open interval) + index = binary_search_lcro(&nums, target); + println!("Index of target element 6 is {index}"); +} diff --git a/en/codes/rust/chapter_searching/binary_search_edge.rs b/en/codes/rust/chapter_searching/binary_search_edge.rs new file mode 100644 index 000000000..7ec3cbad8 --- /dev/null +++ b/en/codes/rust/chapter_searching/binary_search_edge.rs @@ -0,0 +1,50 @@ +/* + * File: binary_search_edge.rs + * Created Time: 2023-08-30 + * Author: night-cruise (2586447362@qq.com) + */ + +mod binary_search_insertion; + +use binary_search_insertion::binary_search_insertion; + +/* Binary search for the leftmost target */ +fn binary_search_left_edge(nums: &[i32], target: i32) -> i32 { + // Equivalent to finding the insertion point of target + let i = binary_search_insertion(nums, target); + // Target not found, return -1 + if i == nums.len() as i32 || nums[i as usize] != target { + return -1; + } + // Found target, return index i + i +} + +/* Binary search for the rightmost target */ +fn binary_search_right_edge(nums: &[i32], target: i32) -> i32 { + // Convert to finding the leftmost target + 1 + let i = binary_search_insertion(nums, target + 1); + // j points to the rightmost target, i points to the first element greater than target + let j = i - 1; + // Target not found, return -1 + if j == -1 || nums[j as usize] != target { + return -1; + } + // Found target, return index j + j +} + +/* Driver Code */ +fn main() { + // Array with duplicate elements + let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; + println!("\nArray nums = {:?}", nums); + + // Binary search left and right boundaries + for target in [6, 7] { + let index = binary_search_left_edge(&nums, target); + println!("Leftmost element {} index is {}", target, index); + let index = binary_search_right_edge(&nums, target); + println!("Rightmost element {} index is {}", target, index); + } +} diff --git a/en/codes/rust/chapter_searching/binary_search_insertion.rs b/en/codes/rust/chapter_searching/binary_search_insertion.rs new file mode 100644 index 000000000..53ce9ea40 --- /dev/null +++ b/en/codes/rust/chapter_searching/binary_search_insertion.rs @@ -0,0 +1,61 @@ +/* + * File: binary_search_insertion.rs + * Created Time: 2023-08-30 + * Author: night-cruise (2586447362@qq.com) + */ +#![allow(unused)] + +/* Binary search for insertion point (no duplicate elements) */ +fn binary_search_insertion_simple(nums: &[i32], target: i32) -> i32 { + let (mut i, mut j) = (0, nums.len() as i32 - 1); // Initialize closed interval [0, n-1] + while i <= j { + let m = i + (j - i) / 2; // Calculate the midpoint index m + if nums[m as usize] < target { + i = m + 1; // target is in the interval [m+1, j] + } else if nums[m as usize] > target { + j = m - 1; // target is in the interval [i, m-1] + } else { + return m; + } + } + // Target not found, return insertion point i + i +} + +/* Binary search for insertion point (with duplicate elements) */ +pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 { + let (mut i, mut j) = (0, nums.len() as i32 - 1); // Initialize closed interval [0, n-1] + while i <= j { + let m = i + (j - i) / 2; // Calculate the midpoint index m + if nums[m as usize] < target { + i = m + 1; // target is in the interval [m+1, j] + } else if nums[m as usize] > target { + j = m - 1; // target is in the interval [i, m-1] + } else { + j = m - 1; // The first element less than target is in the interval [i, m-1] + } + } + // Return insertion point i + i +} + +/* Driver Code */ +fn main() { + // Array without duplicate elements + let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; + println!("\nArray nums = {:?}", nums); + // Binary search for insertion point + for target in [6, 9] { + let index = binary_search_insertion_simple(&nums, target); + println!("Insertion point index for element {} is {}", target, index); + } + + // Array with duplicate elements + let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; + println!("\nArray nums = {:?}", nums); + // Binary search for insertion point + for target in [2, 6, 20] { + let index = binary_search_insertion(&nums, target); + println!("Insertion point index for element {} is {}", target, index); + } +} diff --git a/en/codes/rust/chapter_searching/hashing_search.rs b/en/codes/rust/chapter_searching/hashing_search.rs new file mode 100644 index 000000000..53316910e --- /dev/null +++ b/en/codes/rust/chapter_searching/hashing_search.rs @@ -0,0 +1,50 @@ +/* + * File: hashing_search.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::ListNode; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +/* Hash search (array) */ +fn hashing_search_array<'a>(map: &'a HashMap, target: i32) -> Option<&'a usize> { + // Hash table's key: target element, value: index + // If this key does not exist in the hash table, return None + map.get(&target) +} + +/* Hash search (linked list) */ +fn hashing_search_linked_list( + map: &HashMap>>>, + target: i32, +) -> Option<&Rc>>> { + // Hash table key: target node value, value: node object + // If this key does not exist in the hash table, return None + map.get(&target) +} + +/* Driver Code */ +pub fn main() { + let target = 3; + + /* Hash search (array) */ + let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; + // Initialize hash table + let mut map = HashMap::new(); + for (i, num) in nums.iter().enumerate() { + map.insert(*num, i); // key: element, value: index + } + let index = hashing_search_array(&map, target); + println!("Index of target element 3 = {}", index.unwrap()); + + /* Hash search (linked list) */ + let head = ListNode::arr_to_linked_list(&nums); + // Initialize hash table + // let mut map1 = HashMap::new(); + let map1 = ListNode::linked_list_to_hashmap(head); + let node = hashing_search_linked_list(&map1, target); + println!("Node object corresponding to target node value 3 is {:?}", node); +} diff --git a/en/codes/rust/chapter_searching/linear_search.rs b/en/codes/rust/chapter_searching/linear_search.rs new file mode 100644 index 000000000..bcc584634 --- /dev/null +++ b/en/codes/rust/chapter_searching/linear_search.rs @@ -0,0 +1,54 @@ +/* + * File: linear_search.rs + * Created Time: 2023-07-09 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::ListNode; +use std::cell::RefCell; +use std::rc::Rc; + +/* Linear search (array) */ +fn linear_search_array(nums: &[i32], target: i32) -> i32 { + // Traverse array + for (i, num) in nums.iter().enumerate() { + // Found the target element, return its index + if num == &target { + return i as i32; + } + } + // Target element not found, return -1 + return -1; +} + +/* Linear search (linked list) */ +fn linear_search_linked_list( + head: Rc>>, + target: i32, +) -> Option>>> { + // Found the target node, return it + if head.borrow().val == target { + return Some(head); + }; + // Found the target node, return it + if let Some(node) = &head.borrow_mut().next { + return linear_search_linked_list(node.clone(), target); + } + // Target node not found, return None + return None; +} + +/* Driver Code */ +pub fn main() { + let target = 3; + + /* Perform linear search in array */ + let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; + let index = linear_search_array(&nums, target); + println!("Index of target element 3 = {}", index); + + /* Perform linear search in linked list */ + let head = ListNode::arr_to_linked_list(&nums); + let node = linear_search_linked_list(head.unwrap(), target); + println!("Node object corresponding to target node value 3 is {:?}", node); +} diff --git a/en/codes/rust/chapter_searching/two_sum.rs b/en/codes/rust/chapter_searching/two_sum.rs new file mode 100644 index 000000000..9641c7f91 --- /dev/null +++ b/en/codes/rust/chapter_searching/two_sum.rs @@ -0,0 +1,52 @@ +/* + * File: two_sum.rs + * Created Time: 2023-01-14 + * Author: xBLACICEx (xBLACKICEx@outlook.com), codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::print_util; +use std::collections::HashMap; + +/* Method 1: Brute force enumeration */ +pub fn two_sum_brute_force(nums: &Vec, target: i32) -> Option> { + let size = nums.len(); + // Two nested loops, time complexity is O(n^2) + for i in 0..size - 1 { + for j in i + 1..size { + if nums[i] + nums[j] == target { + return Some(vec![i as i32, j as i32]); + } + } + } + None +} + +/* Method 2: Auxiliary hash table */ +pub fn two_sum_hash_table(nums: &Vec, target: i32) -> Option> { + // Auxiliary hash table, space complexity is O(n) + let mut dic = HashMap::new(); + // Single loop, time complexity is O(n) + for (i, num) in nums.iter().enumerate() { + match dic.get(&(target - num)) { + Some(v) => return Some(vec![*v as i32, i as i32]), + None => dic.insert(num, i as i32), + }; + } + None +} + +fn main() { + // ======= Test Case ======= + let nums = vec![2, 7, 11, 15]; + let target = 13; + + // ====== Driver Code ====== + // Method 1 + let res = two_sum_brute_force(&nums, target).unwrap(); + print!("Method 1 res = "); + print_util::print_array(&res); + // Method 2 + let res = two_sum_hash_table(&nums, target).unwrap(); + print!("\nMethod 2 res = "); + print_util::print_array(&res); +} diff --git a/en/codes/rust/chapter_sorting/bubble_sort.rs b/en/codes/rust/chapter_sorting/bubble_sort.rs new file mode 100644 index 000000000..bc8c5b81a --- /dev/null +++ b/en/codes/rust/chapter_sorting/bubble_sort.rs @@ -0,0 +1,53 @@ +/* + * File: bubble_sort.rs + * Created Time: 2023-02-05 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::print_util; + +/* Bubble sort */ +fn bubble_sort(nums: &mut [i32]) { + // Outer loop: unsorted range is [0, i] + for i in (1..nums.len()).rev() { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for j in 0..i { + if nums[j] > nums[j + 1] { + // Swap nums[j] and nums[j + 1] + nums.swap(j, j + 1); + } + } + } +} + +/* Bubble sort (flag optimization) */ +fn bubble_sort_with_flag(nums: &mut [i32]) { + // Outer loop: unsorted range is [0, i] + for i in (1..nums.len()).rev() { + let mut flag = false; // Initialize flag + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for j in 0..i { + if nums[j] > nums[j + 1] { + // Swap nums[j] and nums[j + 1] + nums.swap(j, j + 1); + flag = true; // Record element swap + } + } + if !flag { + break; // No elements were swapped in this round of "bubbling", exit directly + }; + } +} + +/* Driver Code */ +pub fn main() { + let mut nums = [4, 1, 3, 1, 5, 2]; + bubble_sort(&mut nums); + print!("After bubble sort completes, nums = "); + print_util::print_array(&nums); + + let mut nums1 = [4, 1, 3, 1, 5, 2]; + bubble_sort_with_flag(&mut nums1); + print!("\nAfter bubble sort, nums1 = "); + print_util::print_array(&nums1); +} diff --git a/en/codes/rust/chapter_sorting/bucket_sort.rs b/en/codes/rust/chapter_sorting/bucket_sort.rs new file mode 100644 index 000000000..c8b310283 --- /dev/null +++ b/en/codes/rust/chapter_sorting/bucket_sort.rs @@ -0,0 +1,43 @@ +/* + * File: bucket_sort.rs + * Created Time: 2023-07-09 + * Author: night-cruise (2586447362@qq.com) + */ + +use hello_algo_rust::include::print_util; + +/* Bucket sort */ +fn bucket_sort(nums: &mut [f64]) { + // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + let k = nums.len() / 2; + let mut buckets = vec![vec![]; k]; + // 1. Distribute array elements into various buckets + for &num in nums.iter() { + // Input data range is [0, 1), use num * k to map to index range [0, k-1] + let i = (num * k as f64) as usize; + // Add num to bucket i + buckets[i].push(num); + } + // 2. Sort each bucket + for bucket in &mut buckets { + // Use built-in sorting function, can also replace with other sorting algorithms + bucket.sort_by(|a, b| a.partial_cmp(b).unwrap()); + } + // 3. Traverse buckets to merge results + let mut i = 0; + for bucket in buckets.iter() { + for &num in bucket.iter() { + nums[i] = num; + i += 1; + } + } +} + +/* Driver Code */ +fn main() { + // Assume input data is floating point, interval [0, 1) + let mut nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37]; + bucket_sort(&mut nums); + print!("After bucket sort completes, nums = "); + print_util::print_array(&nums); +} diff --git a/en/codes/rust/chapter_sorting/counting_sort.rs b/en/codes/rust/chapter_sorting/counting_sort.rs new file mode 100644 index 000000000..0253ecf64 --- /dev/null +++ b/en/codes/rust/chapter_sorting/counting_sort.rs @@ -0,0 +1,70 @@ +/* + * File: counting_sort.rs + * Created Time: 2023-07-09 + * Author: night-cruise (2586447362@qq.com) + */ + +use hello_algo_rust::include::print_util; + +/* Counting sort */ +// Simple implementation, cannot be used for sorting objects +fn counting_sort_naive(nums: &mut [i32]) { + // 1. Count the maximum element m in the array + let m = *nums.iter().max().unwrap(); + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + let mut counter = vec![0; m as usize + 1]; + for &num in nums.iter() { + counter[num as usize] += 1; + } + // 3. Traverse counter, filling each element back into the original array nums + let mut i = 0; + for num in 0..m + 1 { + for _ in 0..counter[num as usize] { + nums[i] = num; + i += 1; + } + } +} + +/* Counting sort */ +// Complete implementation, can sort objects and is a stable sort +fn counting_sort(nums: &mut [i32]) { + // 1. Count the maximum element m in the array + let m = *nums.iter().max().unwrap() as usize; + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + let mut counter = vec![0; m + 1]; + for &num in nums.iter() { + counter[num as usize] += 1; + } + // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + // counter[num]-1 is the last index where num appears in res + for i in 0..m { + counter[i + 1] += counter[i]; + } + // 4. Traverse nums in reverse order, placing each element into the result array res + // Initialize the array res to record results + let n = nums.len(); + let mut res = vec![0; n]; + for i in (0..n).rev() { + let num = nums[i]; + res[counter[num as usize] - 1] = num; // Place num at the corresponding index + counter[num as usize] -= 1; // Decrement the prefix sum by 1, getting the next index to place num + } + // Use result array res to overwrite the original array nums + nums.copy_from_slice(&res) +} + +/* Driver Code */ +fn main() { + let mut nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; + counting_sort_naive(&mut nums); + print!("After counting sort (cannot sort objects) completes, nums = "); + print_util::print_array(&nums); + + let mut nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; + counting_sort(&mut nums1); + print!("\nAfter counting sort, nums1 = "); + print_util::print_array(&nums1); +} diff --git a/en/codes/rust/chapter_sorting/heap_sort.rs b/en/codes/rust/chapter_sorting/heap_sort.rs new file mode 100644 index 000000000..4160228e6 --- /dev/null +++ b/en/codes/rust/chapter_sorting/heap_sort.rs @@ -0,0 +1,54 @@ +/* + * File: heap_sort.rs + * Created Time: 2023-07-04 + * Author: night-cruise (2586447362@qq.com) + */ + +use hello_algo_rust::include::print_util; + +/* Heap length is n, start heapifying node i, from top to bottom */ +fn sift_down(nums: &mut [i32], n: usize, mut i: usize) { + loop { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + let l = 2 * i + 1; + let r = 2 * i + 2; + let mut ma = i; + if l < n && nums[l] > nums[ma] { + ma = l; + } + if r < n && nums[r] > nums[ma] { + ma = r; + } + // Swap two nodes + if ma == i { + break; + } + // Swap two nodes + nums.swap(i, ma); + // Loop downwards heapification + i = ma; + } +} + +/* Heap sort */ +fn heap_sort(nums: &mut [i32]) { + // Build heap operation: heapify all nodes except leaves + for i in (0..nums.len() / 2).rev() { + sift_down(nums, nums.len(), i); + } + // Extract the largest element from the heap and repeat for n-1 rounds + for i in (1..nums.len()).rev() { + // Delete node + nums.swap(0, i); + // Start heapifying the root node, from top to bottom + sift_down(nums, i, 0); + } +} + +/* Driver Code */ +fn main() { + let mut nums = [4, 1, 3, 1, 5, 2]; + heap_sort(&mut nums); + print!("After heap sort completes, nums = "); + print_util::print_array(&nums); +} diff --git a/en/codes/rust/chapter_sorting/insertion_sort.rs b/en/codes/rust/chapter_sorting/insertion_sort.rs new file mode 100644 index 000000000..c4cca52e8 --- /dev/null +++ b/en/codes/rust/chapter_sorting/insertion_sort.rs @@ -0,0 +1,29 @@ +/* + * File: insertion_sort.rs + * Created Time: 2023-02-13 + * Author: xBLACKICEx (xBLACKICEx@outlook.com) + */ + +use hello_algo_rust::include::print_util; + +/* Insertion sort */ +fn insertion_sort(nums: &mut [i32]) { + // Outer loop: sorted interval is [0, i-1] + for i in 1..nums.len() { + let (base, mut j) = (nums[i], (i - 1) as i32); + // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + while j >= 0 && nums[j as usize] > base { + nums[(j + 1) as usize] = nums[j as usize]; // Move nums[j] to the right by one position + j -= 1; + } + nums[(j + 1) as usize] = base; // Assign base to the correct position + } +} + +/* Driver Code */ +fn main() { + let mut nums = [4, 1, 3, 1, 5, 2]; + insertion_sort(&mut nums); + print!("After insertion sort completes, nums = "); + print_util::print_array(&nums); +} diff --git a/en/codes/rust/chapter_sorting/merge_sort.rs b/en/codes/rust/chapter_sorting/merge_sort.rs new file mode 100644 index 000000000..bbef475be --- /dev/null +++ b/en/codes/rust/chapter_sorting/merge_sort.rs @@ -0,0 +1,66 @@ +/** + * File: merge_sort.rs + * Created Time: 2023-02-14 + * Author: xBLACKICEx (xBLACKICEx@outlook.com) + */ + +/* Merge left subarray and right subarray */ +fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) { + // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + // Create a temporary array tmp to store the merged results + let tmp_size = right - left + 1; + let mut tmp = vec![0; tmp_size]; + // Initialize the start indices of the left and right subarrays + let (mut i, mut j, mut k) = (left, mid + 1, 0); + // While both subarrays still have elements, compare and copy the smaller element into the temporary array + while i <= mid && j <= right { + if nums[i] <= nums[j] { + tmp[k] = nums[i]; + i += 1; + } else { + tmp[k] = nums[j]; + j += 1; + } + k += 1; + } + // Copy the remaining elements of the left and right subarrays into the temporary array + while i <= mid { + tmp[k] = nums[i]; + k += 1; + i += 1; + } + while j <= right { + tmp[k] = nums[j]; + k += 1; + j += 1; + } + // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + for k in 0..tmp_size { + nums[left + k] = tmp[k]; + } +} + +/* Merge sort */ +fn merge_sort(nums: &mut [i32], left: usize, right: usize) { + // Termination condition + if left >= right { + return; // Terminate recursion when subarray length is 1 + } + + // Divide and conquer stage + let mid = left + (right - left) / 2; // Calculate midpoint + merge_sort(nums, left, mid); // Recursively process the left subarray + merge_sort(nums, mid + 1, right); // Recursively process the right subarray + + // Merge stage + merge(nums, left, mid, right); +} + +/* Driver Code */ +fn main() { + /* Merge sort */ + let mut nums = [7, 3, 2, 6, 0, 1, 5, 4]; + let right = nums.len() - 1; + merge_sort(&mut nums, 0, right); + println!("After merge sort, nums = {:?}", nums); +} diff --git a/en/codes/rust/chapter_sorting/quick_sort.rs b/en/codes/rust/chapter_sorting/quick_sort.rs new file mode 100644 index 000000000..a534307e8 --- /dev/null +++ b/en/codes/rust/chapter_sorting/quick_sort.rs @@ -0,0 +1,148 @@ +/** + * File: quick_sort.rs + * Created Time: 2023-02-16 + * Author: xBLACKICEx (xBLACKICE@outlook.com) + */ + +/* Quick sort */ +struct QuickSort; + +impl QuickSort { + /* Sentinel partition */ + fn partition(nums: &mut [i32], left: usize, right: usize) -> usize { + // Use nums[left] as the pivot + let (mut i, mut j) = (left, right); + while i < j { + while i < j && nums[j] >= nums[left] { + j -= 1; // Search from right to left for the first element smaller than the pivot + } + while i < j && nums[i] <= nums[left] { + i += 1; // Search from left to right for the first element greater than the pivot + } + nums.swap(i, j); // Swap these two elements + } + nums.swap(i, left); // Swap the pivot to the boundary between the two subarrays + i // Return the index of the pivot + } + + /* Quick sort */ + pub fn quick_sort(left: i32, right: i32, nums: &mut [i32]) { + // Terminate recursion when subarray length is 1 + if left >= right { + return; + } + // Sentinel partition + let pivot = Self::partition(nums, left as usize, right as usize) as i32; + // Recursively process the left subarray and right subarray + Self::quick_sort(left, pivot - 1, nums); + Self::quick_sort(pivot + 1, right, nums); + } +} + +/* Quick sort (recursion depth optimization) */ +struct QuickSortMedian; + +impl QuickSortMedian { + /* Select the median of three candidate elements */ + fn median_three(nums: &mut [i32], left: usize, mid: usize, right: usize) -> usize { + let (l, m, r) = (nums[left], nums[mid], nums[right]); + if (l <= m && m <= r) || (r <= m && m <= l) { + return mid; // m is between l and r + } + if (m <= l && l <= r) || (r <= l && l <= m) { + return left; // l is between m and r + } + right + } + + /* Sentinel partition (median of three) */ + fn partition(nums: &mut [i32], left: usize, right: usize) -> usize { + // Select the median of three candidate elements + let med = Self::median_three(nums, left, (left + right) / 2, right); + // Swap the median to the array's leftmost position + nums.swap(left, med); + // Use nums[left] as the pivot + let (mut i, mut j) = (left, right); + while i < j { + while i < j && nums[j] >= nums[left] { + j -= 1; // Search from right to left for the first element smaller than the pivot + } + while i < j && nums[i] <= nums[left] { + i += 1; // Search from left to right for the first element greater than the pivot + } + nums.swap(i, j); // Swap these two elements + } + nums.swap(i, left); // Swap the pivot to the boundary between the two subarrays + i // Return the index of the pivot + } + + /* Quick sort */ + pub fn quick_sort(left: i32, right: i32, nums: &mut [i32]) { + // Terminate recursion when subarray length is 1 + if left >= right { + return; + } + // Sentinel partition + let pivot = Self::partition(nums, left as usize, right as usize) as i32; + // Recursively process the left subarray and right subarray + Self::quick_sort(left, pivot - 1, nums); + Self::quick_sort(pivot + 1, right, nums); + } +} + +/* Quick sort (recursion depth optimization) */ +struct QuickSortTailCall; + +impl QuickSortTailCall { + /* Sentinel partition */ + fn partition(nums: &mut [i32], left: usize, right: usize) -> usize { + // Use nums[left] as the pivot + let (mut i, mut j) = (left, right); + while i < j { + while i < j && nums[j] >= nums[left] { + j -= 1; // Search from right to left for the first element smaller than the pivot + } + while i < j && nums[i] <= nums[left] { + i += 1; // Search from left to right for the first element greater than the pivot + } + nums.swap(i, j); // Swap these two elements + } + nums.swap(i, left); // Swap the pivot to the boundary between the two subarrays + i // Return the index of the pivot + } + + /* Quick sort (recursion depth optimization) */ + pub fn quick_sort(mut left: i32, mut right: i32, nums: &mut [i32]) { + // Terminate when subarray length is 1 + while left < right { + // Sentinel partition operation + let pivot = Self::partition(nums, left as usize, right as usize) as i32; + // Perform quick sort on the shorter of the two subarrays + if pivot - left < right - pivot { + Self::quick_sort(left, pivot - 1, nums); // Recursively sort the left subarray + left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + } else { + Self::quick_sort(pivot + 1, right, nums); // Recursively sort the right subarray + right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + } + } + } +} + +/* Driver Code */ +fn main() { + /* Quick sort */ + let mut nums = [2, 4, 1, 0, 3, 5]; + QuickSort::quick_sort(0, (nums.len() - 1) as i32, &mut nums); + println!("After quick sort, nums = {:?}", nums); + + /* Quick sort (recursion depth optimization) */ + let mut nums = [2, 4, 1, 0, 3, 5]; + QuickSortMedian::quick_sort(0, (nums.len() - 1) as i32, &mut nums); + println!("After quick sort (median pivot optimization), nums = {:?}", nums); + + /* Quick sort (recursion depth optimization) */ + let mut nums = [2, 4, 1, 0, 3, 5]; + QuickSortTailCall::quick_sort(0, (nums.len() - 1) as i32, &mut nums); + println!("After quick sort (recursion depth optimization), nums = {:?}", nums); +} diff --git a/en/codes/rust/chapter_sorting/radix_sort.rs b/en/codes/rust/chapter_sorting/radix_sort.rs new file mode 100644 index 000000000..8c06a7939 --- /dev/null +++ b/en/codes/rust/chapter_sorting/radix_sort.rs @@ -0,0 +1,63 @@ +/* + * File: radix_sort.rs + * Created Time: 2023-07-09 + * Author: night-cruise (2586447362@qq.com) + */ + +use hello_algo_rust::include::print_util; + +/* Get the k-th digit of element num, where exp = 10^(k-1) */ +fn digit(num: i32, exp: i32) -> usize { + // Passing exp instead of k can avoid repeated expensive exponentiation here + return ((num / exp) % 10) as usize; +} + +/* Counting sort (based on nums k-th digit) */ +fn counting_sort_digit(nums: &mut [i32], exp: i32) { + // Decimal digit range is 0~9, therefore need a bucket array of length 10 + let mut counter = [0; 10]; + let n = nums.len(); + // Count the occurrence of digits 0~9 + for i in 0..n { + let d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d + counter[d] += 1; // Count the occurrence of digit d + } + // Calculate prefix sum, converting "occurrence count" into "array index" + for i in 1..10 { + counter[i] += counter[i - 1]; + } + // Traverse in reverse, based on bucket statistics, place each element into res + let mut res = vec![0; n]; + for i in (0..n).rev() { + let d = digit(nums[i], exp); + let j = counter[d] - 1; // Get the index j for d in the array + res[j] = nums[i]; // Place the current element at index j + counter[d] -= 1; // Decrease the count of d by 1 + } + // Use result to overwrite the original array nums + nums.copy_from_slice(&res); +} + +/* Radix sort */ +fn radix_sort(nums: &mut [i32]) { + // Get the maximum element of the array, used to determine the maximum number of digits + let m = *nums.into_iter().max().unwrap(); + // Traverse from the lowest to the highest digit + let mut exp = 1; + while exp <= m { + counting_sort_digit(nums, exp); + exp *= 10; + } +} + +/* Driver Code */ +fn main() { + // Radix sort + let mut nums = [ + 10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337, + 63832996, + ]; + radix_sort(&mut nums); + print!("After radix sort completes, nums = "); + print_util::print_array(&nums); +} diff --git a/en/codes/rust/chapter_sorting/selection_sort.rs b/en/codes/rust/chapter_sorting/selection_sort.rs new file mode 100644 index 000000000..b03e0f399 --- /dev/null +++ b/en/codes/rust/chapter_sorting/selection_sort.rs @@ -0,0 +1,35 @@ +/* + * File: selection_sort.rs + * Created Time: 2023-05-30 + * Author: WSL0809 (wslzzy@outlook.com) + */ + +use hello_algo_rust::include::print_util; + +/* Selection sort */ +fn selection_sort(nums: &mut [i32]) { + if nums.is_empty() { + return; + } + let n = nums.len(); + // Outer loop: unsorted interval is [i, n-1] + for i in 0..n - 1 { + // Inner loop: find the smallest element within the unsorted interval + let mut k = i; + for j in i + 1..n { + if nums[j] < nums[k] { + k = j; // Record the index of the smallest element + } + } + // Swap the smallest element with the first element of the unsorted interval + nums.swap(i, k); + } +} + +/* Driver Code */ +pub fn main() { + let mut nums = [4, 1, 3, 1, 5, 2]; + selection_sort(&mut nums); + print!("\nAfter selection sort, nums = "); + print_util::print_array(&nums); +} diff --git a/en/codes/rust/chapter_stack_and_queue/array_deque.rs b/en/codes/rust/chapter_stack_and_queue/array_deque.rs new file mode 100644 index 000000000..02c5f18be --- /dev/null +++ b/en/codes/rust/chapter_stack_and_queue/array_deque.rs @@ -0,0 +1,160 @@ +/* + * File: array_deque.rs + * Created Time: 2023-03-11 + * Author: codingonion (coderonion@gmail.com) + */ +use hello_algo_rust::include::print_util; +/* Double-ended queue based on circular array implementation */ +struct ArrayDeque { + nums: Vec, // Array for storing double-ended queue elements + front: usize, // Front pointer, points to the front of the queue element + que_size: usize, // Double-ended queue length +} + +impl ArrayDeque { + /* Constructor */ + pub fn new(capacity: usize) -> Self { + Self { + nums: vec![T::default(); capacity], + front: 0, + que_size: 0, + } + } + + /* Get the capacity of the double-ended queue */ + pub fn capacity(&self) -> usize { + self.nums.len() + } + + /* Get the length of the double-ended queue */ + pub fn size(&self) -> usize { + self.que_size + } + + /* Check if the double-ended queue is empty */ + pub fn is_empty(&self) -> bool { + self.que_size == 0 + } + + /* Calculate circular array index */ + fn index(&self, i: i32) -> usize { + // Use modulo operation to wrap the array head and tail together + // When i passes the tail of the array, return to the head + // When i passes the head of the array, return to the tail + ((i + self.capacity() as i32) % self.capacity() as i32) as usize + } + + /* Front of the queue enqueue */ + pub fn push_first(&mut self, num: T) { + if self.que_size == self.capacity() { + println!("Double-ended queue is full"); + return; + } + // Use modulo operation to wrap front around to the tail after passing the head of the array + // Add num to the front of the queue + self.front = self.index(self.front as i32 - 1); + // Add num to front of queue + self.nums[self.front] = num; + self.que_size += 1; + } + + /* Rear of the queue enqueue */ + pub fn push_last(&mut self, num: T) { + if self.que_size == self.capacity() { + println!("Double-ended queue is full"); + return; + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + let rear = self.index(self.front as i32 + self.que_size as i32); + // Front pointer moves one position backward + self.nums[rear] = num; + self.que_size += 1; + } + + /* Rear of the queue dequeue */ + fn pop_first(&mut self) -> T { + let num = self.peek_first(); + // Move front pointer backward by one position + self.front = self.index(self.front as i32 + 1); + self.que_size -= 1; + num + } + + /* Access rear of the queue element */ + fn pop_last(&mut self) -> T { + let num = self.peek_last(); + self.que_size -= 1; + num + } + + /* Return list for printing */ + fn peek_first(&self) -> T { + if self.is_empty() { + panic!("Deque is empty") + }; + self.nums[self.front] + } + + /* Driver Code */ + fn peek_last(&self) -> T { + if self.is_empty() { + panic!("Deque is empty") + }; + // Initialize double-ended queue + let last = self.index(self.front as i32 + self.que_size as i32 - 1); + self.nums[last] + } + + /* Return array for printing */ + fn to_array(&self) -> Vec { + // Elements enqueue + let mut res = vec![T::default(); self.que_size]; + let mut j = self.front; + for i in 0..self.que_size { + res[i] = self.nums[self.index(j as i32)]; + j += 1; + } + res + } +} + +/* Driver Code */ +fn main() { + /* Get the length of the double-ended queue */ + let mut deque = ArrayDeque::new(10); + deque.push_last(3); + deque.push_last(2); + deque.push_last(5); + print!("Double-ended queue deque = "); + print_util::print_array(&deque.to_array()); + + /* Update element */ + let peek_first = deque.peek_first(); + print!("\nFront element peek_first = {}", peek_first); + let peek_last = deque.peek_last(); + print!("\nRear element peek_last = {}", peek_last); + + /* Elements enqueue */ + deque.push_last(4); + print!("\nAfter element 4 enqueues at rear, deque = "); + print_util::print_array(&deque.to_array()); + deque.push_first(1); + print!("\nAfter element 1 enqueues at front, deque = "); + print_util::print_array(&deque.to_array()); + + /* Element dequeue */ + let pop_last = deque.pop_last(); + print!("\nDequeue rear element = {}, after dequeue deque = ", pop_last); + print_util::print_array(&deque.to_array()); + let pop_first = deque.pop_first(); + print!("\nDequeue front element = {}, after dequeue deque = ", pop_first); + print_util::print_array(&deque.to_array()); + + /* Get the length of the double-ended queue */ + let size = deque.size(); + print!("\nDeque length size = {}", size); + + /* Check if the double-ended queue is empty */ + let is_empty = deque.is_empty(); + print!("\nIs deque empty = {}", is_empty); +} diff --git a/en/codes/rust/chapter_stack_and_queue/array_queue.rs b/en/codes/rust/chapter_stack_and_queue/array_queue.rs new file mode 100644 index 000000000..7ad2cbd59 --- /dev/null +++ b/en/codes/rust/chapter_stack_and_queue/array_queue.rs @@ -0,0 +1,125 @@ +/* + * File: array_queue.rs + * Created Time: 2023-02-06 + * Author: WSL0809 (wslzzy@outlook.com) + */ + +/* Queue based on circular array implementation */ +struct ArrayQueue { + nums: Vec, // Array for storing queue elements + front: i32, // Front pointer, points to the front of the queue element + que_size: i32, // Queue length + que_capacity: i32, // Queue capacity +} + +impl ArrayQueue { + /* Constructor */ + fn new(capacity: i32) -> ArrayQueue { + ArrayQueue { + nums: vec![T::default(); capacity as usize], + front: 0, + que_size: 0, + que_capacity: capacity, + } + } + + /* Get the capacity of the queue */ + fn capacity(&self) -> i32 { + self.que_capacity + } + + /* Get the length of the queue */ + fn size(&self) -> i32 { + self.que_size + } + + /* Check if the queue is empty */ + fn is_empty(&self) -> bool { + self.que_size == 0 + } + + /* Enqueue */ + fn push(&mut self, num: T) { + if self.que_size == self.capacity() { + println!("Queue is full"); + return; + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Add num to the rear of the queue + let rear = (self.front + self.que_size) % self.que_capacity; + // Front pointer moves one position backward + self.nums[rear as usize] = num; + self.que_size += 1; + } + + /* Dequeue */ + fn pop(&mut self) -> T { + let num = self.peek(); + // Move front pointer backward by one position, if it passes the tail, return to array head + self.front = (self.front + 1) % self.que_capacity; + self.que_size -= 1; + num + } + + /* Return list for printing */ + fn peek(&self) -> T { + if self.is_empty() { + panic!("index out of bounds"); + } + self.nums[self.front as usize] + } + + /* Return array */ + fn to_vector(&self) -> Vec { + let cap = self.que_capacity; + let mut j = self.front; + let mut arr = vec![T::default(); cap as usize]; + for i in 0..self.que_size { + arr[i as usize] = self.nums[(j % cap) as usize]; + j += 1; + } + arr + } +} + +/* Driver Code */ +fn main() { + /* Access front of the queue element */ + let capacity = 10; + let mut queue = ArrayQueue::new(capacity); + + /* Elements enqueue */ + queue.push(1); + queue.push(3); + queue.push(2); + queue.push(5); + queue.push(4); + println!("Queue queue = {:?}", queue.to_vector()); + + /* Return list for printing */ + let peek = queue.peek(); + println!("Front element peek = {}", peek); + + /* Element dequeue */ + let pop = queue.pop(); + println!( + "Dequeue element pop = {:?}, after dequeue queue = {:?}", + pop, + queue.to_vector() + ); + + /* Get the length of the queue */ + let size = queue.size(); + println!("Queue length size = {}", size); + + /* Check if the queue is empty */ + let is_empty = queue.is_empty(); + println!("Is queue empty = {}", is_empty); + + /* Test circular array */ + for i in 0..10 { + queue.push(i); + queue.pop(); + println!("After round {:?} of enqueue + dequeue, queue = {:?}", i, queue.to_vector()); + } +} diff --git a/en/codes/rust/chapter_stack_and_queue/array_stack.rs b/en/codes/rust/chapter_stack_and_queue/array_stack.rs new file mode 100644 index 000000000..c4e91f2ea --- /dev/null +++ b/en/codes/rust/chapter_stack_and_queue/array_stack.rs @@ -0,0 +1,86 @@ +/* + * File: array_stack.rs + * Created Time: 2023-02-05 + * Author: WSL0809 (wslzzy@outlook.com), codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::print_util; + +/* Stack based on array implementation */ +struct ArrayStack { + stack: Vec, +} + +impl ArrayStack { + /* Access top of the stack element */ + fn new() -> ArrayStack { + ArrayStack:: { + stack: Vec::::new(), + } + } + + /* Get the length of the stack */ + fn size(&self) -> usize { + self.stack.len() + } + + /* Check if the stack is empty */ + fn is_empty(&self) -> bool { + self.size() == 0 + } + + /* Push */ + fn push(&mut self, num: T) { + self.stack.push(num); + } + + /* Pop */ + fn pop(&mut self) -> Option { + self.stack.pop() + } + + /* Return list for printing */ + fn peek(&self) -> Option<&T> { + if self.is_empty() { + panic!("Stack is empty") + }; + self.stack.last() + } + + /* Return &Vec */ + fn to_array(&self) -> &Vec { + &self.stack + } +} + +/* Driver Code */ +fn main() { + // Access top of the stack element + let mut stack = ArrayStack::::new(); + + // Elements push onto stack + stack.push(1); + stack.push(3); + stack.push(2); + stack.push(5); + stack.push(4); + print!("Stack stack = "); + print_util::print_array(stack.to_array()); + + // Return list for printing + let peek = stack.peek().unwrap(); + print!("\nTop element peek = {}", peek); + + // Element pop from stack + let pop = stack.pop().unwrap(); + print!("\nPop element pop = {pop}, after pop stack = "); + print_util::print_array(stack.to_array()); + + // Get the length of the stack + let size = stack.size(); + print!("\nStack length size = {size}"); + + // Check if empty + let is_empty = stack.is_empty(); + print!("\nIs stack empty = {is_empty}"); +} diff --git a/en/codes/rust/chapter_stack_and_queue/deque.rs b/en/codes/rust/chapter_stack_and_queue/deque.rs new file mode 100644 index 000000000..56d0f23f3 --- /dev/null +++ b/en/codes/rust/chapter_stack_and_queue/deque.rs @@ -0,0 +1,49 @@ +/* + * File: deque.rs + * Created Time: 2023-02-05 + * Author: codingonion (coderonion@gmail.com), xBLACKICEx (xBLACKICEx@outlook.com) + */ + +use hello_algo_rust::include::print_util; +use std::collections::VecDeque; + +/* Driver Code */ +pub fn main() { + // Get the length of the double-ended queue + let mut deque: VecDeque = VecDeque::new(); + deque.push_back(3); + deque.push_back(2); + deque.push_back(5); + print!("Double-ended queue deque = "); + print_util::print_queue(&deque); + + // Update element + let peek_first = deque.front().unwrap(); + print!("\nFront element peekFirst = {peek_first}"); + let peek_last = deque.back().unwrap(); + print!("\nRear element peekLast = {peek_last}"); + + /* Elements enqueue */ + deque.push_back(4); + print!("\nAfter element 4 enqueues at rear, deque = "); + print_util::print_queue(&deque); + deque.push_front(1); + print!("\nAfter element 1 enqueues at front, deque = "); + print_util::print_queue(&deque); + + // Element dequeue + let pop_last = deque.pop_back().unwrap(); + print!("\nDequeue rear element = {pop_last}, after dequeue deque = "); + print_util::print_queue(&deque); + let pop_first = deque.pop_front().unwrap(); + print!("\nDequeue front element = {pop_first}, after dequeue deque = "); + print_util::print_queue(&deque); + + // Get the length of the double-ended queue + let size = deque.len(); + print!("\nDeque length size = {size}"); + + // Check if the double-ended queue is empty + let is_empty = deque.is_empty(); + print!("\nIs deque empty = {is_empty}"); +} diff --git a/en/codes/rust/chapter_stack_and_queue/linkedlist_deque.rs b/en/codes/rust/chapter_stack_and_queue/linkedlist_deque.rs new file mode 100644 index 000000000..6a08c134c --- /dev/null +++ b/en/codes/rust/chapter_stack_and_queue/linkedlist_deque.rs @@ -0,0 +1,218 @@ +/* + * File: linkedlist_deque.rs + * Created Time: 2023-03-11 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::print_util; + +use std::cell::RefCell; +use std::rc::Rc; + +/* Doubly linked list node */ +pub struct ListNode { + pub val: T, // Node value + pub next: Option>>>, // Successor node pointer + pub prev: Option>>>, // Predecessor node pointer +} + +impl ListNode { + pub fn new(val: T) -> Rc>> { + Rc::new(RefCell::new(ListNode { + val, + next: None, + prev: None, + })) + } +} + +/* Double-ended queue based on doubly linked list implementation */ +#[allow(dead_code)] +pub struct LinkedListDeque { + front: Option>>>, // Head node front + rear: Option>>>, // Tail node rear + que_size: usize, // Length of the double-ended queue +} + +impl LinkedListDeque { + pub fn new() -> Self { + Self { + front: None, + rear: None, + que_size: 0, + } + } + + /* Get the length of the double-ended queue */ + pub fn size(&self) -> usize { + return self.que_size; + } + + /* Check if the double-ended queue is empty */ + pub fn is_empty(&self) -> bool { + return self.que_size == 0; + } + + /* Enqueue operation */ + fn push(&mut self, num: T, is_front: bool) { + let node = ListNode::new(num); + // Front of the queue enqueue operation + if is_front { + match self.front.take() { + // If the linked list is empty, make both front and rear point to node + None => { + self.rear = Some(node.clone()); + self.front = Some(node); + } + // Add node to the head of the linked list + Some(old_front) => { + old_front.borrow_mut().prev = Some(node.clone()); + node.borrow_mut().next = Some(old_front); + self.front = Some(node); // Update head node + } + } + } + // Rear of the queue enqueue operation + else { + match self.rear.take() { + // If the linked list is empty, make both front and rear point to node + None => { + self.front = Some(node.clone()); + self.rear = Some(node); + } + // Add node to the tail of the linked list + Some(old_rear) => { + old_rear.borrow_mut().next = Some(node.clone()); + node.borrow_mut().prev = Some(old_rear); + self.rear = Some(node); // Update tail node + } + } + } + self.que_size += 1; // Update queue length + } + + /* Front of the queue enqueue */ + pub fn push_first(&mut self, num: T) { + self.push(num, true); + } + + /* Rear of the queue enqueue */ + pub fn push_last(&mut self, num: T) { + self.push(num, false); + } + + /* Dequeue operation */ + fn pop(&mut self, is_front: bool) -> Option { + // If queue is empty, return None directly + if self.is_empty() { + return None; + }; + // Temporarily store head node value + if is_front { + self.front.take().map(|old_front| { + match old_front.borrow_mut().next.take() { + Some(new_front) => { + new_front.borrow_mut().prev.take(); + self.front = Some(new_front); // Update head node + } + None => { + self.rear.take(); + } + } + self.que_size -= 1; // Update queue length + old_front.borrow().val + }) + } + // Temporarily store tail node value + else { + self.rear.take().map(|old_rear| { + match old_rear.borrow_mut().prev.take() { + Some(new_rear) => { + new_rear.borrow_mut().next.take(); + self.rear = Some(new_rear); // Update tail node + } + None => { + self.front.take(); + } + } + self.que_size -= 1; // Update queue length + old_rear.borrow().val + }) + } + } + + /* Rear of the queue dequeue */ + pub fn pop_first(&mut self) -> Option { + return self.pop(true); + } + + /* Access rear of the queue element */ + pub fn pop_last(&mut self) -> Option { + return self.pop(false); + } + + /* Return list for printing */ + pub fn peek_first(&self) -> Option<&Rc>>> { + self.front.as_ref() + } + + /* Driver Code */ + pub fn peek_last(&self) -> Option<&Rc>>> { + self.rear.as_ref() + } + + /* Return array for printing */ + pub fn to_array(&self, head: Option<&Rc>>>) -> Vec { + let mut res: Vec = Vec::new(); + fn recur(cur: Option<&Rc>>>, res: &mut Vec) { + if let Some(cur) = cur { + res.push(cur.borrow().val); + recur(cur.borrow().next.as_ref(), res); + } + } + + recur(head, &mut res); + res + } +} + +/* Driver Code */ +fn main() { + /* Get the length of the double-ended queue */ + let mut deque = LinkedListDeque::new(); + deque.push_last(3); + deque.push_last(2); + deque.push_last(5); + print!("Double-ended queue deque = "); + print_util::print_array(&deque.to_array(deque.peek_first())); + + /* Update element */ + let peek_first = deque.peek_first().unwrap().borrow().val; + print!("\nFront element peek_first = {}", peek_first); + let peek_last = deque.peek_last().unwrap().borrow().val; + print!("\nRear element peek_last = {}", peek_last); + + /* Elements enqueue */ + deque.push_last(4); + print!("\nAfter element 4 enqueues at rear, deque = "); + print_util::print_array(&deque.to_array(deque.peek_first())); + deque.push_first(1); + print!("\nAfter element 1 enqueues at front, deque = "); + print_util::print_array(&deque.to_array(deque.peek_first())); + + /* Element dequeue */ + let pop_last = deque.pop_last().unwrap(); + print!("\nDequeue rear element = {}, after dequeue deque = ", pop_last); + print_util::print_array(&deque.to_array(deque.peek_first())); + let pop_first = deque.pop_first().unwrap(); + print!("\nDequeue front element = {}, after dequeue deque = ", pop_first); + print_util::print_array(&deque.to_array(deque.peek_first())); + + /* Get the length of the double-ended queue */ + let size = deque.size(); + print!("\nDeque length size = {}", size); + + /* Check if the double-ended queue is empty */ + let is_empty = deque.is_empty(); + print!("\nIs deque empty = {}", is_empty); +} diff --git a/en/codes/rust/chapter_stack_and_queue/linkedlist_queue.rs b/en/codes/rust/chapter_stack_and_queue/linkedlist_queue.rs new file mode 100644 index 000000000..ca7d2351f --- /dev/null +++ b/en/codes/rust/chapter_stack_and_queue/linkedlist_queue.rs @@ -0,0 +1,126 @@ +/* + * File: linkedlist_queue.rs + * Created Time: 2023-03-11 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::{print_util, ListNode}; + +use std::cell::RefCell; +use std::rc::Rc; + +/* Queue based on linked list implementation */ +#[allow(dead_code)] +pub struct LinkedListQueue { + front: Option>>>, // Head node front + rear: Option>>>, // Tail node rear + que_size: usize, // Queue length +} + +impl LinkedListQueue { + pub fn new() -> Self { + Self { + front: None, + rear: None, + que_size: 0, + } + } + + /* Get the length of the queue */ + pub fn size(&self) -> usize { + return self.que_size; + } + + /* Check if the queue is empty */ + pub fn is_empty(&self) -> bool { + return self.que_size == 0; + } + + /* Enqueue */ + pub fn push(&mut self, num: T) { + // Add num after the tail node + let new_rear = ListNode::new(num); + match self.rear.take() { + // If the queue is not empty, add the node after the tail node + Some(old_rear) => { + old_rear.borrow_mut().next = Some(new_rear.clone()); + self.rear = Some(new_rear); + } + // If the queue is empty, make both front and rear point to the node + None => { + self.front = Some(new_rear.clone()); + self.rear = Some(new_rear); + } + } + self.que_size += 1; + } + + /* Dequeue */ + pub fn pop(&mut self) -> Option { + self.front.take().map(|old_front| { + match old_front.borrow_mut().next.take() { + Some(new_front) => { + self.front = Some(new_front); + } + None => { + self.rear.take(); + } + } + self.que_size -= 1; + old_front.borrow().val + }) + } + + /* Return list for printing */ + pub fn peek(&self) -> Option<&Rc>>> { + self.front.as_ref() + } + + /* Convert linked list to Array and return */ + pub fn to_array(&self, head: Option<&Rc>>>) -> Vec { + let mut res: Vec = Vec::new(); + + fn recur(cur: Option<&Rc>>>, res: &mut Vec) { + if let Some(cur) = cur { + res.push(cur.borrow().val); + recur(cur.borrow().next.as_ref(), res); + } + } + + recur(head, &mut res); + + res + } +} + +/* Driver Code */ +fn main() { + /* Access front of the queue element */ + let mut queue = LinkedListQueue::new(); + + /* Elements enqueue */ + queue.push(1); + queue.push(3); + queue.push(2); + queue.push(5); + queue.push(4); + print!("Queue queue = "); + print_util::print_array(&queue.to_array(queue.peek())); + + /* Return list for printing */ + let peek = queue.peek().unwrap().borrow().val; + print!("\nFront element peek = {}", peek); + + /* Element dequeue */ + let pop = queue.pop().unwrap(); + print!("\nDequeue element pop = {}, after dequeue queue = ", pop); + print_util::print_array(&queue.to_array(queue.peek())); + + /* Get the length of the queue */ + let size = queue.size(); + print!("\nQueue length size = {}", size); + + /* Check if the queue is empty */ + let is_empty = queue.is_empty(); + print!("\nIs queue empty = {}", is_empty); +} diff --git a/en/codes/rust/chapter_stack_and_queue/linkedlist_stack.rs b/en/codes/rust/chapter_stack_and_queue/linkedlist_stack.rs new file mode 100644 index 000000000..cc4d73f79 --- /dev/null +++ b/en/codes/rust/chapter_stack_and_queue/linkedlist_stack.rs @@ -0,0 +1,105 @@ +/* + * File: linkedlist_stack.rs + * Created Time: 2023-03-11 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::{print_util, ListNode}; + +use std::cell::RefCell; +use std::rc::Rc; + +/* Stack based on linked list implementation */ +#[allow(dead_code)] +pub struct LinkedListStack { + stack_peek: Option>>>, // Use head node as stack top + stk_size: usize, // Stack length +} + +impl LinkedListStack { + pub fn new() -> Self { + Self { + stack_peek: None, + stk_size: 0, + } + } + + /* Get the length of the stack */ + pub fn size(&self) -> usize { + return self.stk_size; + } + + /* Check if the stack is empty */ + pub fn is_empty(&self) -> bool { + return self.size() == 0; + } + + /* Push */ + pub fn push(&mut self, num: T) { + let node = ListNode::new(num); + node.borrow_mut().next = self.stack_peek.take(); + self.stack_peek = Some(node); + self.stk_size += 1; + } + + /* Pop */ + pub fn pop(&mut self) -> Option { + self.stack_peek.take().map(|old_head| { + self.stack_peek = old_head.borrow_mut().next.take(); + self.stk_size -= 1; + + old_head.borrow().val + }) + } + + /* Return list for printing */ + pub fn peek(&self) -> Option<&Rc>>> { + self.stack_peek.as_ref() + } + + /* Convert List to Array and return */ + pub fn to_array(&self) -> Vec { + fn _to_array(head: Option<&Rc>>>) -> Vec { + if let Some(node) = head { + let mut nums = _to_array(node.borrow().next.as_ref()); + nums.push(node.borrow().val); + return nums; + } + return Vec::new(); + } + + _to_array(self.peek()) + } +} + +/* Driver Code */ +fn main() { + /* Access top of the stack element */ + let mut stack = LinkedListStack::new(); + + /* Elements push onto stack */ + stack.push(1); + stack.push(3); + stack.push(2); + stack.push(5); + stack.push(4); + print!("Stack stack = "); + print_util::print_array(&stack.to_array()); + + /* Return list for printing */ + let peek = stack.peek().unwrap().borrow().val; + print!("\nTop element peek = {}", peek); + + /* Element pop from stack */ + let pop = stack.pop().unwrap(); + print!("\nPop element pop = {}, after pop stack = ", pop); + print_util::print_array(&stack.to_array()); + + /* Get the length of the stack */ + let size = stack.size(); + print!("\nStack length size = {}", size); + + /* Check if empty */ + let is_empty = stack.is_empty(); + print!("\nIs stack empty = {}", is_empty); +} diff --git a/en/codes/rust/chapter_stack_and_queue/queue.rs b/en/codes/rust/chapter_stack_and_queue/queue.rs new file mode 100644 index 000000000..208df0ede --- /dev/null +++ b/en/codes/rust/chapter_stack_and_queue/queue.rs @@ -0,0 +1,41 @@ +/* + * File: queue.rs + * Created Time: 2023-02-05 + * Author: codingonion (coderonion@gmail.com), xBLACKICEx (xBLACKICEx@outlook.com) + */ + +use hello_algo_rust::include::print_util; + +use std::collections::VecDeque; + +/* Driver Code */ +pub fn main() { + // Access front of the queue element + let mut queue: VecDeque = VecDeque::new(); + + // Elements enqueue + queue.push_back(1); + queue.push_back(3); + queue.push_back(2); + queue.push_back(5); + queue.push_back(4); + print!("Queue queue = "); + print_util::print_queue(&queue); + + // Return list for printing + let peek = queue.front().unwrap(); + println!("\nFront element peek = {peek}"); + + // Element dequeue + let pop = queue.pop_front().unwrap(); + print!("Dequeue element pop = {pop}, after dequeue queue = "); + print_util::print_queue(&queue); + + // Get the length of the queue + let size = queue.len(); + print!("\nQueue length size = {size}"); + + // Check if the queue is empty + let is_empty = queue.is_empty(); + print!("\nIs queue empty = {is_empty}"); +} diff --git a/en/codes/rust/chapter_stack_and_queue/stack.rs b/en/codes/rust/chapter_stack_and_queue/stack.rs new file mode 100644 index 000000000..a4a4bb2a1 --- /dev/null +++ b/en/codes/rust/chapter_stack_and_queue/stack.rs @@ -0,0 +1,40 @@ +/* + * File: stack.rs + * Created Time: 2023-02-05 + * Author: codingonion (coderonion@gmail.com) + */ + +use hello_algo_rust::include::print_util; + +/* Driver Code */ +pub fn main() { + // Access top of the stack element + // In Rust, it's recommended to use Vec as a stack + let mut stack: Vec = Vec::new(); + + // Elements push onto stack + stack.push(1); + stack.push(3); + stack.push(2); + stack.push(5); + stack.push(4); + print!("Stack stack = "); + print_util::print_array(&stack); + + // Return list for printing + let peek = stack.last().unwrap(); + print!("\nTop element peek = {peek}"); + + // Element pop from stack + let pop = stack.pop().unwrap(); + print!("\nPop element pop = {pop}, after pop stack = "); + print_util::print_array(&stack); + + // Get the length of the stack + let size = stack.len(); + print!("\nStack length size = {size}"); + + // Check if the stack is empty + let is_empty = stack.is_empty(); + print!("\nIs stack empty = {is_empty}"); +} diff --git a/en/codes/rust/chapter_tree/array_binary_tree.rs b/en/codes/rust/chapter_tree/array_binary_tree.rs new file mode 100644 index 000000000..bfed20915 --- /dev/null +++ b/en/codes/rust/chapter_tree/array_binary_tree.rs @@ -0,0 +1,192 @@ +/* + * File: array_binary_tree.rs + * Created Time: 2023-07-25 + * Author: night-cruise (2586447362@qq.com) + */ + +use hello_algo_rust::include::{print_util, tree_node}; + +/* Binary tree class represented by array */ +struct ArrayBinaryTree { + tree: Vec>, +} + +impl ArrayBinaryTree { + /* Constructor */ + fn new(arr: Vec>) -> Self { + Self { tree: arr } + } + + /* List capacity */ + fn size(&self) -> i32 { + self.tree.len() as i32 + } + + /* Get value of node at index i */ + fn val(&self, i: i32) -> Option { + // If index is out of bounds, return None, representing empty position + if i < 0 || i >= self.size() { + None + } else { + self.tree[i as usize] + } + } + + /* Get index of left child node of node at index i */ + fn left(&self, i: i32) -> i32 { + 2 * i + 1 + } + + /* Get index of right child node of node at index i */ + fn right(&self, i: i32) -> i32 { + 2 * i + 2 + } + + /* Get index of parent node of node at index i */ + fn parent(&self, i: i32) -> i32 { + (i - 1) / 2 + } + + /* Level-order traversal */ + fn level_order(&self) -> Vec { + self.tree.iter().filter_map(|&x| x).collect() + } + + /* Depth-first traversal */ + fn dfs(&self, i: i32, order: &'static str, res: &mut Vec) { + if self.val(i).is_none() { + return; + } + let val = self.val(i).unwrap(); + // Preorder traversal + if order == "pre" { + res.push(val); + } + self.dfs(self.left(i), order, res); + // Inorder traversal + if order == "in" { + res.push(val); + } + self.dfs(self.right(i), order, res); + // Postorder traversal + if order == "post" { + res.push(val); + } + } + + /* Preorder traversal */ + fn pre_order(&self) -> Vec { + let mut res = vec![]; + self.dfs(0, "pre", &mut res); + res + } + + /* Inorder traversal */ + fn in_order(&self) -> Vec { + let mut res = vec![]; + self.dfs(0, "in", &mut res); + res + } + + /* Postorder traversal */ + fn post_order(&self) -> Vec { + let mut res = vec![]; + self.dfs(0, "post", &mut res); + res + } +} + +/* Driver Code */ +fn main() { + // Initialize binary tree + // Here we use a function to generate a binary tree directly from an array + let arr = vec![ + Some(1), + Some(2), + Some(3), + Some(4), + None, + Some(6), + Some(7), + Some(8), + Some(9), + None, + None, + Some(12), + None, + None, + Some(15), + ]; + + let root = tree_node::vec_to_tree(arr.clone()).unwrap(); + println!("\nInitialize binary tree\n"); + println!("Array representation of binary tree:"); + println!( + "[{}]", + arr.iter() + .map(|&val| if let Some(val) = val { + format!("{val}") + } else { + "null".to_string() + }) + .collect::>() + .join(", ") + ); + println!("Linked list representation of binary tree:"); + print_util::print_tree(&root); + + // Binary tree class represented by array + let abt = ArrayBinaryTree::new(arr); + + // Access node + let i = 1; + let l = abt.left(i); + let r = abt.right(i); + let p = abt.parent(i); + println!( + "\nCurrent node index is {}, value is {}", + i, + if let Some(val) = abt.val(i) { + format!("{val}") + } else { + "null".to_string() + } + ); + println!( + "Left child index is {}, value is {}", + l, + if let Some(val) = abt.val(l) { + format!("{val}") + } else { + "null".to_string() + } + ); + println!( + "Right child index is {}, value is {}", + r, + if let Some(val) = abt.val(r) { + format!("{val}") + } else { + "null".to_string() + } + ); + println!( + "Parent node index is {}, value is {}", + p, + if let Some(val) = abt.val(p) { + format!("{val}") + } else { + "null".to_string() + } + ); + + // Traverse tree + let mut res = abt.level_order(); + println!("\nLevel-order traversal is: {:?}", res); + res = abt.pre_order(); + println!("Pre-order traversal is: {:?}", res); + res = abt.in_order(); + println!("In-order traversal is: {:?}", res); + res = abt.post_order(); + println!("Post-order traversal is: {:?}", res); +} diff --git a/en/codes/rust/chapter_tree/avl_tree.rs b/en/codes/rust/chapter_tree/avl_tree.rs new file mode 100644 index 000000000..3df88424d --- /dev/null +++ b/en/codes/rust/chapter_tree/avl_tree.rs @@ -0,0 +1,297 @@ +/* + * File: avl_tree.rs + * Created Time: 2023-07-14 + * Author: night-cruise (2586447362@qq.com) + */ + +use hello_algo_rust::include::{print_util, TreeNode}; + +use std::cell::RefCell; +use std::cmp::Ordering; +use std::rc::Rc; + +type OptionTreeNodeRc = Option>>; + +/* AVL tree */ +struct AVLTree { + root: OptionTreeNodeRc, // Root node +} + +impl AVLTree { + /* Constructor */ + fn new() -> Self { + Self { root: None } + } + + /* Get node height */ + fn height(node: OptionTreeNodeRc) -> i32 { + // Empty node height is -1, leaf node height is 0 + match node { + Some(node) => node.borrow().height, + None => -1, + } + } + + /* Update node height */ + fn update_height(node: OptionTreeNodeRc) { + if let Some(node) = node { + let left = node.borrow().left.clone(); + let right = node.borrow().right.clone(); + // Node height equals the height of the tallest subtree + 1 + node.borrow_mut().height = std::cmp::max(Self::height(left), Self::height(right)) + 1; + } + } + + /* Get balance factor */ + fn balance_factor(node: OptionTreeNodeRc) -> i32 { + match node { + // Empty node balance factor is 0 + None => 0, + // Node balance factor = left subtree height - right subtree height + Some(node) => { + Self::height(node.borrow().left.clone()) - Self::height(node.borrow().right.clone()) + } + } + } + + /* Right rotation operation */ + fn right_rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc { + match node { + Some(node) => { + let child = node.borrow().left.clone().unwrap(); + let grand_child = child.borrow().right.clone(); + // Using child as pivot, rotate node to the right + child.borrow_mut().right = Some(node.clone()); + node.borrow_mut().left = grand_child; + // Update node height + Self::update_height(Some(node)); + Self::update_height(Some(child.clone())); + // Return root node of subtree after rotation + Some(child) + } + None => None, + } + } + + /* Left rotation operation */ + fn left_rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc { + match node { + Some(node) => { + let child = node.borrow().right.clone().unwrap(); + let grand_child = child.borrow().left.clone(); + // Using child as pivot, rotate node to the left + child.borrow_mut().left = Some(node.clone()); + node.borrow_mut().right = grand_child; + // Update node height + Self::update_height(Some(node)); + Self::update_height(Some(child.clone())); + // Return root node of subtree after rotation + Some(child) + } + None => None, + } + } + + /* Perform rotation operation to restore balance to this subtree */ + fn rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc { + // Get balance factor of node + let balance_factor = Self::balance_factor(node.clone()); + // Left-leaning tree + if balance_factor > 1 { + let node = node.unwrap(); + if Self::balance_factor(node.borrow().left.clone()) >= 0 { + // Right rotation + Self::right_rotate(Some(node)) + } else { + // First left rotation then right rotation + let left = node.borrow().left.clone(); + node.borrow_mut().left = Self::left_rotate(left); + Self::right_rotate(Some(node)) + } + } + // Right-leaning tree + else if balance_factor < -1 { + let node = node.unwrap(); + if Self::balance_factor(node.borrow().right.clone()) <= 0 { + // Left rotation + Self::left_rotate(Some(node)) + } else { + // First right rotation then left rotation + let right = node.borrow().right.clone(); + node.borrow_mut().right = Self::right_rotate(right); + Self::left_rotate(Some(node)) + } + } else { + // Balanced tree, no rotation needed, return directly + node + } + } + + /* Insert node */ + fn insert(&mut self, val: i32) { + self.root = Self::insert_helper(self.root.clone(), val); + } + + /* Recursively insert node (helper method) */ + fn insert_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc { + match node { + Some(mut node) => { + /* 1. Find insertion position and insert node */ + match { + let node_val = node.borrow().val; + node_val + } + .cmp(&val) + { + Ordering::Greater => { + let left = node.borrow().left.clone(); + node.borrow_mut().left = Self::insert_helper(left, val); + } + Ordering::Less => { + let right = node.borrow().right.clone(); + node.borrow_mut().right = Self::insert_helper(right, val); + } + Ordering::Equal => { + return Some(node); // Duplicate node not inserted, return directly + } + } + Self::update_height(Some(node.clone())); // Update node height + + /* 2. Perform rotation operation to restore balance to this subtree */ + node = Self::rotate(Some(node)).unwrap(); + // Return root node of subtree + Some(node) + } + None => Some(TreeNode::new(val)), + } + } + + /* Remove node */ + fn remove(&self, val: i32) { + Self::remove_helper(self.root.clone(), val); + } + + /* Recursively delete node (helper method) */ + fn remove_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc { + match node { + Some(mut node) => { + /* 1. Find node and delete */ + if val < node.borrow().val { + let left = node.borrow().left.clone(); + node.borrow_mut().left = Self::remove_helper(left, val); + } else if val > node.borrow().val { + let right = node.borrow().right.clone(); + node.borrow_mut().right = Self::remove_helper(right, val); + } else if node.borrow().left.is_none() || node.borrow().right.is_none() { + let child = if node.borrow().left.is_some() { + node.borrow().left.clone() + } else { + node.borrow().right.clone() + }; + match child { + // Number of child nodes = 0, delete node directly and return + None => { + return None; + } + // Number of child nodes = 1, delete node directly + Some(child) => node = child, + } + } else { + // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + let mut temp = node.borrow().right.clone().unwrap(); + loop { + let temp_left = temp.borrow().left.clone(); + if temp_left.is_none() { + break; + } + temp = temp_left.unwrap(); + } + let right = node.borrow().right.clone(); + node.borrow_mut().right = Self::remove_helper(right, temp.borrow().val); + node.borrow_mut().val = temp.borrow().val; + } + Self::update_height(Some(node.clone())); // Update node height + + /* 2. Perform rotation operation to restore balance to this subtree */ + node = Self::rotate(Some(node)).unwrap(); + // Return root node of subtree + Some(node) + } + None => None, + } + } + + /* Search node */ + fn search(&self, val: i32) -> OptionTreeNodeRc { + let mut cur = self.root.clone(); + // Loop search, exit after passing leaf node + while let Some(current) = cur.clone() { + match current.borrow().val.cmp(&val) { + // Target node is in cur's right subtree + Ordering::Less => { + cur = current.borrow().right.clone(); + } + // Target node is in cur's left subtree + Ordering::Greater => { + cur = current.borrow().left.clone(); + } + // Found target node, exit loop + Ordering::Equal => { + break; + } + } + } + // Return target node + cur + } +} + +/* Driver Code */ +fn main() { + fn test_insert(tree: &mut AVLTree, val: i32) { + tree.insert(val); + println!("\nAfter inserting node {}, AVL tree is", val); + print_util::print_tree(&tree.root.clone().unwrap()); + } + + fn test_remove(tree: &mut AVLTree, val: i32) { + tree.remove(val); + println!("\nAfter deleting node {}, AVL tree is", val); + print_util::print_tree(&tree.root.clone().unwrap()); + } + + /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + let mut avl_tree = AVLTree::new(); + + /* Insert node */ + // Delete nodes + test_insert(&mut avl_tree, 1); + test_insert(&mut avl_tree, 2); + test_insert(&mut avl_tree, 3); + test_insert(&mut avl_tree, 4); + test_insert(&mut avl_tree, 5); + test_insert(&mut avl_tree, 8); + test_insert(&mut avl_tree, 7); + test_insert(&mut avl_tree, 9); + test_insert(&mut avl_tree, 10); + test_insert(&mut avl_tree, 6); + + /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + test_insert(&mut avl_tree, 7); + + /* Remove node */ + // Delete node with degree 1 + test_remove(&mut avl_tree, 8); // Delete node with degree 2 + test_remove(&mut avl_tree, 5); // Remove node with degree 1 + test_remove(&mut avl_tree, 4); // Remove node with degree 2 + + /* Search node */ + let node = avl_tree.search(7); + if let Some(node) = node { + println!( + "\nFound node object is {:?}, node value = {}", + &*node.borrow(), + node.borrow().val + ); + } +} diff --git a/en/codes/rust/chapter_tree/binary_search_tree.rs b/en/codes/rust/chapter_tree/binary_search_tree.rs new file mode 100644 index 000000000..96ca2478a --- /dev/null +++ b/en/codes/rust/chapter_tree/binary_search_tree.rs @@ -0,0 +1,195 @@ +/* + * File: binary_search_tree.rs + * Created Time: 2023-04-20 + * Author: xBLACKICEx (xBLACKICE@outlook.com)、night-cruise (2586447362@qq.com) + */ + +use hello_algo_rust::include::print_util; + +use std::cell::RefCell; +use std::cmp::Ordering; +use std::rc::Rc; + +use hello_algo_rust::include::TreeNode; + +type OptionTreeNodeRc = Option>>; + +/* Binary search tree */ +pub struct BinarySearchTree { + root: OptionTreeNodeRc, +} + +impl BinarySearchTree { + /* Constructor */ + pub fn new() -> Self { + // Initialize empty tree + Self { root: None } + } + + /* Get binary tree root node */ + pub fn get_root(&self) -> OptionTreeNodeRc { + self.root.clone() + } + + /* Search node */ + pub fn search(&self, num: i32) -> OptionTreeNodeRc { + let mut cur = self.root.clone(); + // Loop search, exit after passing leaf node + while let Some(node) = cur.clone() { + match num.cmp(&node.borrow().val) { + // Target node is in cur's right subtree + Ordering::Greater => cur = node.borrow().right.clone(), + // Target node is in cur's left subtree + Ordering::Less => cur = node.borrow().left.clone(), + // Found target node, exit loop + Ordering::Equal => break, + } + } + + // Return target node + cur + } + + /* Insert node */ + pub fn insert(&mut self, num: i32) { + // If tree is empty, initialize root node + if self.root.is_none() { + self.root = Some(TreeNode::new(num)); + return; + } + let mut cur = self.root.clone(); + let mut pre = None; + // Loop search, exit after passing leaf node + while let Some(node) = cur.clone() { + match num.cmp(&node.borrow().val) { + // Found duplicate node, return directly + Ordering::Equal => return, + // Insertion position is in cur's right subtree + Ordering::Greater => { + pre = cur.clone(); + cur = node.borrow().right.clone(); + } + // Insertion position is in cur's left subtree + Ordering::Less => { + pre = cur.clone(); + cur = node.borrow().left.clone(); + } + } + } + // Insert node + let pre = pre.unwrap(); + let node = Some(TreeNode::new(num)); + if num > pre.borrow().val { + pre.borrow_mut().right = node; + } else { + pre.borrow_mut().left = node; + } + } + + /* Remove node */ + pub fn remove(&mut self, num: i32) { + // If tree is empty, return directly + if self.root.is_none() { + return; + } + let mut cur = self.root.clone(); + let mut pre = None; + // Loop search, exit after passing leaf node + while let Some(node) = cur.clone() { + match num.cmp(&node.borrow().val) { + // Found node to delete, exit loop + Ordering::Equal => break, + // Node to delete is in cur's right subtree + Ordering::Greater => { + pre = cur.clone(); + cur = node.borrow().right.clone(); + } + // Node to delete is in cur's left subtree + Ordering::Less => { + pre = cur.clone(); + cur = node.borrow().left.clone(); + } + } + } + // If no node to delete, return directly + if cur.is_none() { + return; + } + let cur = cur.unwrap(); + let (left_child, right_child) = (cur.borrow().left.clone(), cur.borrow().right.clone()); + match (left_child.clone(), right_child.clone()) { + // Number of child nodes = 0 or 1 + (None, None) | (Some(_), None) | (None, Some(_)) => { + // When number of child nodes = 0 / 1, child = nullptr / that child node + let child = left_child.or(right_child); + let pre = pre.unwrap(); + // Delete node cur + if !Rc::ptr_eq(&cur, self.root.as_ref().unwrap()) { + let left = pre.borrow().left.clone(); + if left.is_some() && Rc::ptr_eq(left.as_ref().unwrap(), &cur) { + pre.borrow_mut().left = child; + } else { + pre.borrow_mut().right = child; + } + } else { + // If deleted node is root node, reassign root node + self.root = child; + } + } + // Number of child nodes = 2 + (Some(_), Some(_)) => { + // Get next node of cur in inorder traversal + let mut tmp = cur.borrow().right.clone(); + while let Some(node) = tmp.clone() { + if node.borrow().left.is_some() { + tmp = node.borrow().left.clone(); + } else { + break; + } + } + let tmp_val = tmp.unwrap().borrow().val; + // Recursively delete node tmp + self.remove(tmp_val); + // Replace cur with tmp + cur.borrow_mut().val = tmp_val; + } + } + } +} + +/* Driver Code */ +fn main() { + /* Initialize binary search tree */ + let mut bst = BinarySearchTree::new(); + // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + let nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]; + for &num in &nums { + bst.insert(num); + } + println!("\nInitialized binary tree is\n"); + print_util::print_tree(bst.get_root().as_ref().unwrap()); + + /* Search node */ + let node = bst.search(7); + println!( + "\nFound node object is {:?}, node value = {}", + node.clone().unwrap(), + node.clone().unwrap().borrow().val + ); + + /* Insert node */ + bst.insert(16); + println!("\nAfter inserting node 16, binary tree is\n"); + print_util::print_tree(bst.get_root().as_ref().unwrap()); + + /* Remove node */ + bst.remove(1); + println!("\nAfter removing node 1, binary tree is\n"); + print_util::print_tree(bst.get_root().as_ref().unwrap()); + bst.remove(2); + println!("\nAfter removing node 2, binary tree is\n"); + print_util::print_tree(bst.get_root().as_ref().unwrap()); + bst.remove(4); + println!("\nAfter removing node 4, binary tree is\n"); + print_util::print_tree(bst.get_root().as_ref().unwrap()); +} diff --git a/en/codes/rust/chapter_tree/binary_tree.rs b/en/codes/rust/chapter_tree/binary_tree.rs new file mode 100644 index 000000000..096ddcb62 --- /dev/null +++ b/en/codes/rust/chapter_tree/binary_tree.rs @@ -0,0 +1,38 @@ +/** + * File: binary_tree.rs + * Created Time: 2023-02-27 + * Author: xBLACKICEx (xBLACKICE@outlook.com) + */ +use std::rc::Rc; +use hello_algo_rust::include::{print_util, TreeNode}; + +/* Driver Code */ +fn main() { + /* Initialize binary tree */ + // Initialize nodes + let n1 = TreeNode::new(1); + let n2 = TreeNode::new(2); + let n3 = TreeNode::new(3); + let n4 = TreeNode::new(4); + let n5 = TreeNode::new(5); + // Build references (pointers) between nodes + n1.borrow_mut().left = Some(Rc::clone(&n2)); + n1.borrow_mut().right = Some(Rc::clone(&n3)); + n2.borrow_mut().left = Some(Rc::clone(&n4)); + n2.borrow_mut().right = Some(Rc::clone(&n5)); + println!("\nInitialize binary tree\n"); + print_util::print_tree(&n1); + + // Insert node and delete node + let p = TreeNode::new(0); + // Delete node + p.borrow_mut().left = Some(Rc::clone(&n2)); + n1.borrow_mut().left = Some(Rc::clone(&p)); + println!("\nAfter inserting node P\n"); + print_util::print_tree(&n1); + // Remove node P + drop(p); + n1.borrow_mut().left = Some(Rc::clone(&n2)); + println!("\nAfter removing node P\n"); + print_util::print_tree(&n1); +} diff --git a/en/codes/rust/chapter_tree/binary_tree_bfs.rs b/en/codes/rust/chapter_tree/binary_tree_bfs.rs new file mode 100644 index 000000000..95af73dbf --- /dev/null +++ b/en/codes/rust/chapter_tree/binary_tree_bfs.rs @@ -0,0 +1,45 @@ +/* + * File: binary_tree_bfs.rs + * Created Time: 2023-04-07 + * Author: xBLACKICEx (xBLACKICE@outlook.com) + */ + +use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode}; +use hello_algo_rust::op_vec; + +use std::collections::VecDeque; +use std::{cell::RefCell, rc::Rc}; + +/* Level-order traversal */ +fn level_order(root: &Rc>) -> Vec { + // Initialize queue, add root node + let mut que = VecDeque::new(); + que.push_back(root.clone()); + // Initialize a list to save the traversal sequence + let mut vec = Vec::new(); + + while let Some(node) = que.pop_front() { + // Dequeue + vec.push(node.borrow().val); // Save node value + if let Some(left) = node.borrow().left.as_ref() { + que.push_back(left.clone()); // Left child node enqueue + } + if let Some(right) = node.borrow().right.as_ref() { + que.push_back(right.clone()); // Right child node enqueue + }; + } + vec +} + +/* Driver Code */ +fn main() { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + let root = vec_to_tree(op_vec![1, 2, 3, 4, 5, 6, 7]).unwrap(); + println!("Initialize binary tree\n"); + print_util::print_tree(&root); + + /* Level-order traversal */ + let vec = level_order(&root); + print!("\nLevel-order traversal node sequence = {:?}", vec); +} diff --git a/en/codes/rust/chapter_tree/binary_tree_dfs.rs b/en/codes/rust/chapter_tree/binary_tree_dfs.rs new file mode 100644 index 000000000..d804c3d7b --- /dev/null +++ b/en/codes/rust/chapter_tree/binary_tree_dfs.rs @@ -0,0 +1,87 @@ +/* + * File: binary_tree_dfs.rs + * Created Time: 2023-04-06 + * Author: xBLACKICEx (xBLACKICE@outlook.com) + */ + +use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode}; +use hello_algo_rust::op_vec; + +use std::cell::RefCell; +use std::rc::Rc; + +/* Preorder traversal */ +fn pre_order(root: Option<&Rc>>) -> Vec { + let mut result = vec![]; + + fn dfs(root: Option<&Rc>>, res: &mut Vec) { + if let Some(node) = root { + // Visit priority: root node -> left subtree -> right subtree + let node = node.borrow(); + res.push(node.val); + dfs(node.left.as_ref(), res); + dfs(node.right.as_ref(), res); + } + } + dfs(root, &mut result); + + result +} + +/* Inorder traversal */ +fn in_order(root: Option<&Rc>>) -> Vec { + let mut result = vec![]; + + fn dfs(root: Option<&Rc>>, res: &mut Vec) { + if let Some(node) = root { + // Visit priority: left subtree -> root node -> right subtree + let node = node.borrow(); + dfs(node.left.as_ref(), res); + res.push(node.val); + dfs(node.right.as_ref(), res); + } + } + dfs(root, &mut result); + + result +} + +/* Postorder traversal */ +fn post_order(root: Option<&Rc>>) -> Vec { + let mut result = vec![]; + + fn dfs(root: Option<&Rc>>, res: &mut Vec) { + if let Some(node) = root { + // Visit priority: left subtree -> right subtree -> root node + let node = node.borrow(); + dfs(node.left.as_ref(), res); + dfs(node.right.as_ref(), res); + res.push(node.val); + } + } + + dfs(root, &mut result); + + result +} + +/* Driver Code */ +fn main() { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + let root = vec_to_tree(op_vec![1, 2, 3, 4, 5, 6, 7]); + println!("Initialize binary tree\n"); + print_util::print_tree(root.as_ref().unwrap()); + + /* Preorder traversal */ + let vec = pre_order(root.as_ref()); + println!("\nPre-order traversal node sequence = {:?}", vec); + + /* Inorder traversal */ + let vec = in_order(root.as_ref()); + println!("\nIn-order traversal node sequence = {:?}", vec); + + /* Postorder traversal */ + let vec = post_order(root.as_ref()); + print!("\nPost-order traversal node sequence = {:?}", vec); +} diff --git a/en/codes/rust/src/include/list_node.rs b/en/codes/rust/src/include/list_node.rs new file mode 100644 index 000000000..27ed78409 --- /dev/null +++ b/en/codes/rust/src/include/list_node.rs @@ -0,0 +1,57 @@ +/* + * File: list_node.rs + * Created Time: 2023-03-05 + * Author: codingonion (coderonion@gmail.com), rongyi (hiarongyi@gmail.com) + */ + +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +#[derive(Debug)] +pub struct ListNode { + pub val: T, + pub next: Option>>>, +} + +impl ListNode { + pub fn new(val: T) -> Rc>> { + Rc::new(RefCell::new(ListNode { val, next: None })) + } + + /* Deserialize array to linked list */ + pub fn arr_to_linked_list(array: &[T]) -> Option>>> + where + T: Copy + Clone, + { + let mut head = None; + // insert in reverse order + for item in array.iter().rev() { + let node = Rc::new(RefCell::new(ListNode { + val: *item, + next: head.take(), + })); + head = Some(node); + } + head + } + + /* Convert linked list to hash table */ + pub fn linked_list_to_hashmap( + linked_list: Option>>>, + ) -> HashMap>>> + where + T: std::hash::Hash + Eq + Copy + Clone, + { + let mut hashmap = HashMap::new(); + let mut node = linked_list; + + while let Some(cur) = node { + let borrow = cur.borrow(); + hashmap.insert(borrow.val.clone(), cur.clone()); + node = borrow.next.clone(); + } + + hashmap + } +} diff --git a/en/codes/rust/src/include/mod.rs b/en/codes/rust/src/include/mod.rs new file mode 100644 index 000000000..6cba6f9a5 --- /dev/null +++ b/en/codes/rust/src/include/mod.rs @@ -0,0 +1,16 @@ +/* + * File: include.rs + * Created Time: 2023-02-05 + * Author: codingonion (coderonion@gmail.com), xBLACKICEx (xBLACKICE@outlook.com) + */ + +pub mod list_node; +pub mod print_util; +pub mod tree_node; +pub mod vertex; + +// rexport to include +pub use list_node::*; +pub use print_util::*; +pub use tree_node::*; +pub use vertex::*; diff --git a/en/codes/rust/src/include/print_util.rs b/en/codes/rust/src/include/print_util.rs new file mode 100644 index 000000000..047579665 --- /dev/null +++ b/en/codes/rust/src/include/print_util.rs @@ -0,0 +1,103 @@ +/* + * File: print_util.rs + * Created Time: 2023-02-05 + * Author: codingonion (coderonion@gmail.com), xBLACKICEx (xBLACKICEx@outlook.com) + */ + +use std::cell::{Cell, RefCell}; +use std::fmt::Display; +use std::collections::{HashMap, VecDeque}; +use std::rc::Rc; + +use super::list_node::ListNode; +use super::tree_node::{TreeNode, vec_to_tree}; + +struct Trunk<'a, 'b> { + prev: Option<&'a Trunk<'a, 'b>>, + str: Cell<&'b str>, +} + +/* Print array */ +pub fn print_array(nums: &[T]) { + print!("["); + if nums.len() > 0 { + for (i, num) in nums.iter().enumerate() { + print!("{}{}", num, if i == nums.len() - 1 {"]"} else {", "} ); + } + } else { + print!("]"); + } +} + +/* Print hash table */ +pub fn print_hash_map(map: &HashMap) { + for (key, value) in map { + println!("{key} -> {value}"); + } +} + +/* Print queue (deque) */ +pub fn print_queue(queue: &VecDeque) { + print!("["); + let iter = queue.iter(); + for (i, data) in iter.enumerate() { + print!("{}{}", data, if i == queue.len() - 1 {"]"} else {", "} ); + } +} + +/* Print linked list */ +pub fn print_linked_list(head: &Rc>>) { + print!("{}{}", head.borrow().val, if head.borrow().next.is_none() {"\n"} else {" -> "}); + if let Some(node) = &head.borrow().next { + return print_linked_list(node); + } +} + +/* Print binary tree */ +pub fn print_tree(root: &Rc>) { + _print_tree(Some(root), None, false); +} + +/* Print binary tree */ +fn _print_tree(root: Option<&Rc>>, prev: Option<&Trunk>, is_right: bool) { + if let Some(node) = root { + let mut prev_str = " "; + let trunk = Trunk { prev, str: Cell::new(prev_str) }; + _print_tree(node.borrow().right.as_ref(), Some(&trunk), true); + + if prev.is_none() { + trunk.str.set("———"); + } else if is_right { + trunk.str.set("/———"); + prev_str = " |"; + } else { + trunk.str.set("\\———"); + prev.as_ref().unwrap().str.set(prev_str); + } + + show_trunks(Some(&trunk)); + println!(" {}", node.borrow().val); + if let Some(prev) = prev { + prev.str.set(prev_str); + } + trunk.str.set(" |"); + + _print_tree(node.borrow().left.as_ref(), Some(&trunk), false); + } +} + +fn show_trunks(trunk: Option<&Trunk>) { + if let Some(trunk) = trunk { + show_trunks(trunk.prev); + print!("{}", trunk.str.get()); + } +} + +/* Print heap */ +pub fn print_heap(heap: Vec) { + println!("Array representation of heap: {:?}", heap); + println!("Heap tree representation:"); + if let Some(root) = vec_to_tree(heap.into_iter().map(|val| Some(val)).collect()) { + print_tree(&root); + } +} diff --git a/en/codes/rust/src/include/tree_node.rs b/en/codes/rust/src/include/tree_node.rs new file mode 100644 index 000000000..9410b9342 --- /dev/null +++ b/en/codes/rust/src/include/tree_node.rs @@ -0,0 +1,92 @@ +/* + * File: tree_node.rs + * Created Time: 2023-02-27 + * Author: xBLACKICEx (xBLACKICE@outlook.com), night-cruise (2586447362@qq.com) + */ + +use std::cell::RefCell; +use std::rc::Rc; + +/* Binary tree node type */ +#[derive(Debug)] +pub struct TreeNode { + pub val: i32, + pub height: i32, + pub parent: Option>>, + pub left: Option>>, + pub right: Option>>, +} + +impl TreeNode { + /* Constructor */ + pub fn new(val: i32) -> Rc> { + Rc::new(RefCell::new(Self { + val, + height: 0, + parent: None, + left: None, + right: None, + })) + } +} + +#[macro_export] +macro_rules! op_vec { + ( $( $x:expr ),* ) => { + vec![ + $(Option::from($x)),* + ] + }; +} + +// For the serialization encoding rules, please refer to: +// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ +// Array representation of binary tree: +// [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] +// Linked list representation of binary tree: +// /——— 15 +// /——— 7 +// /——— 3 +// | \——— 6 +// | \——— 12 +// ——— 1 +// \——— 2 +// | /——— 9 +// \——— 4 +// \——— 8 + +/* Deserialize a list into a binary tree: recursion */ +fn vec_to_tree_dfs(arr: &[Option], i: usize) -> Option>> { + if i >= arr.len() || arr[i].is_none() { + return None; + } + let root = TreeNode::new(arr[i].unwrap()); + root.borrow_mut().left = vec_to_tree_dfs(arr, 2 * i + 1); + root.borrow_mut().right = vec_to_tree_dfs(arr, 2 * i + 2); + Some(root) +} + +/* Deserialize a list into a binary tree */ +pub fn vec_to_tree(arr: Vec>) -> Option>> { + vec_to_tree_dfs(&arr, 0) +} + +/* Serialize a binary tree into a list: recursion */ +fn tree_to_vec_dfs(root: Option<&Rc>>, i: usize, res: &mut Vec>) { + if let Some(root) = root { + // i + 1 is the minimum valid size to access index i + while res.len() < i + 1 { + res.push(None); + } + res[i] = Some(root.borrow().val); + tree_to_vec_dfs(root.borrow().left.as_ref(), 2 * i + 1, res); + tree_to_vec_dfs(root.borrow().right.as_ref(), 2 * i + 2, res); + } +} + +/* Serialize a binary tree into a list */ +pub fn tree_to_vec(root: Option>>) -> Vec> { + let mut res = vec![]; + tree_to_vec_dfs(root.as_ref(), 0, &mut res); + res +} diff --git a/en/codes/rust/src/include/vertex.rs b/en/codes/rust/src/include/vertex.rs new file mode 100644 index 000000000..e9420c53e --- /dev/null +++ b/en/codes/rust/src/include/vertex.rs @@ -0,0 +1,27 @@ +/* + * File: vertex.rs + * Created Time: 2023-07-13 + * Author: night-cruise (2586447362@qq.com) + */ + +/* Vertex type */ +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub struct Vertex { + pub val: i32, +} + +impl From for Vertex { + fn from(value: i32) -> Self { + Self { val: value } + } +} + +/* Input value list vals, return vertex list vets */ +pub fn vals_to_vets(vals: Vec) -> Vec { + vals.into_iter().map(|val| val.into()).collect() +} + +/* Input vertex list vets, return value list vals */ +pub fn vets_to_vals(vets: Vec) -> Vec { + vets.into_iter().map(|vet| vet.val).collect() +} diff --git a/en/codes/rust/src/lib.rs b/en/codes/rust/src/lib.rs new file mode 100644 index 000000000..2883b9104 --- /dev/null +++ b/en/codes/rust/src/lib.rs @@ -0,0 +1 @@ +pub mod include; diff --git a/en/codes/swift/.gitignore b/en/codes/swift/.gitignore new file mode 100644 index 000000000..6295af4cc --- /dev/null +++ b/en/codes/swift/.gitignore @@ -0,0 +1,130 @@ +# Created by https://www.toptal.com/developers/gitignore/api/objective-c,swift,swiftpackagemanager +# Edit at https://www.toptal.com/developers/gitignore?templates=objective-c,swift,swiftpackagemanager + +### Objective-C ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +# CocoaPods +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# Pods/ +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# fastlane +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ + +### Objective-C Patch ### + +### Swift ### +# Xcode +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + + + + + + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# CocoaPods +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# Pods/ +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + + +# Code Injection +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + + +### SwiftPackageManager ### +Packages +xcuserdata +*.xcodeproj + + +# End of https://www.toptal.com/developers/gitignore/api/objective-c,swift,swiftpackagemanager diff --git a/en/codes/swift/Package.resolved b/en/codes/swift/Package.resolved new file mode 100644 index 000000000..159c83d26 --- /dev/null +++ b/en/codes/swift/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections", + "state" : { + "branch" : "release/1.1", + "revision" : "4a1d92ba85027010d2c528c05576cde9a362254b" + } + } + ], + "version" : 2 +} diff --git a/en/codes/swift/Package.swift b/en/codes/swift/Package.swift new file mode 100644 index 000000000..5326dc138 --- /dev/null +++ b/en/codes/swift/Package.swift @@ -0,0 +1,206 @@ +// swift-tools-version: 5.7 + +import PackageDescription + +let package = Package( + name: "HelloAlgo", + products: [ + // chapter_computational_complexity + .executable(name: "iteration", targets: ["iteration"]), + .executable(name: "recursion", targets: ["recursion"]), + .executable(name: "time_complexity", targets: ["time_complexity"]), + .executable(name: "worst_best_time_complexity", targets: ["worst_best_time_complexity"]), + .executable(name: "space_complexity", targets: ["space_complexity"]), + // chapter_array_and_linkedlist + .executable(name: "array", targets: ["array"]), + .executable(name: "linked_list", targets: ["linked_list"]), + .executable(name: "list", targets: ["list"]), + .executable(name: "my_list", targets: ["my_list"]), + // chapter_stack_and_queue + .executable(name: "stack", targets: ["stack"]), + .executable(name: "linkedlist_stack", targets: ["linkedlist_stack"]), + .executable(name: "array_stack", targets: ["array_stack"]), + .executable(name: "queue", targets: ["queue"]), + .executable(name: "linkedlist_queue", targets: ["linkedlist_queue"]), + .executable(name: "array_queue", targets: ["array_queue"]), + .executable(name: "deque", targets: ["deque"]), + .executable(name: "linkedlist_deque", targets: ["linkedlist_deque"]), + .executable(name: "array_deque", targets: ["array_deque"]), + // chapter_hashing + .executable(name: "hash_map", targets: ["hash_map"]), + .executable(name: "array_hash_map", targets: ["array_hash_map"]), + .executable(name: "hash_map_chaining", targets: ["hash_map_chaining"]), + .executable(name: "hash_map_open_addressing", targets: ["hash_map_open_addressing"]), + .executable(name: "simple_hash", targets: ["simple_hash"]), + .executable(name: "built_in_hash", targets: ["built_in_hash"]), + // chapter_tree + .executable(name: "binary_tree", targets: ["binary_tree"]), + .executable(name: "binary_tree_bfs", targets: ["binary_tree_bfs"]), + .executable(name: "binary_tree_dfs", targets: ["binary_tree_dfs"]), + .executable(name: "array_binary_tree", targets: ["array_binary_tree"]), + .executable(name: "binary_search_tree", targets: ["binary_search_tree"]), + .executable(name: "avl_tree", targets: ["avl_tree"]), + // chapter_heap + .executable(name: "heap", targets: ["heap"]), + .executable(name: "my_heap", targets: ["my_heap"]), + .executable(name: "top_k", targets: ["top_k"]), + // chapter_graph + .executable(name: "graph_adjacency_matrix", targets: ["graph_adjacency_matrix"]), + .executable(name: "graph_adjacency_list", targets: ["graph_adjacency_list"]), + .executable(name: "graph_bfs", targets: ["graph_bfs"]), + .executable(name: "graph_dfs", targets: ["graph_dfs"]), + // chapter_searching + .executable(name: "binary_search", targets: ["binary_search"]), + .executable(name: "binary_search_insertion", targets: ["binary_search_insertion"]), + .executable(name: "binary_search_edge", targets: ["binary_search_edge"]), + .executable(name: "two_sum", targets: ["two_sum"]), + .executable(name: "linear_search", targets: ["linear_search"]), + .executable(name: "hashing_search", targets: ["hashing_search"]), + // chapter_sorting + .executable(name: "selection_sort", targets: ["selection_sort"]), + .executable(name: "bubble_sort", targets: ["bubble_sort"]), + .executable(name: "insertion_sort", targets: ["insertion_sort"]), + .executable(name: "quick_sort", targets: ["quick_sort"]), + .executable(name: "merge_sort", targets: ["merge_sort"]), + .executable(name: "heap_sort", targets: ["heap_sort"]), + .executable(name: "bucket_sort", targets: ["bucket_sort"]), + .executable(name: "counting_sort", targets: ["counting_sort"]), + .executable(name: "radix_sort", targets: ["radix_sort"]), + // chapter_divide_and_conquer + .executable(name: "binary_search_recur", targets: ["binary_search_recur"]), + .executable(name: "build_tree", targets: ["build_tree"]), + .executable(name: "hanota", targets: ["hanota"]), + // chapter_backtracking + .executable(name: "preorder_traversal_i_compact", targets: ["preorder_traversal_i_compact"]), + .executable(name: "preorder_traversal_ii_compact", targets: ["preorder_traversal_ii_compact"]), + .executable(name: "preorder_traversal_iii_compact", targets: ["preorder_traversal_iii_compact"]), + .executable(name: "preorder_traversal_iii_template", targets: ["preorder_traversal_iii_template"]), + .executable(name: "permutations_i", targets: ["permutations_i"]), + .executable(name: "permutations_ii", targets: ["permutations_ii"]), + .executable(name: "subset_sum_i_naive", targets: ["subset_sum_i_naive"]), + .executable(name: "subset_sum_i", targets: ["subset_sum_i"]), + .executable(name: "subset_sum_ii", targets: ["subset_sum_ii"]), + .executable(name: "n_queens", targets: ["n_queens"]), + // chapter_dynamic_programming + .executable(name: "climbing_stairs_backtrack", targets: ["climbing_stairs_backtrack"]), + .executable(name: "climbing_stairs_dfs", targets: ["climbing_stairs_dfs"]), + .executable(name: "climbing_stairs_dfs_mem", targets: ["climbing_stairs_dfs_mem"]), + .executable(name: "climbing_stairs_dp", targets: ["climbing_stairs_dp"]), + .executable(name: "min_cost_climbing_stairs_dp", targets: ["min_cost_climbing_stairs_dp"]), + .executable(name: "climbing_stairs_constraint_dp", targets: ["climbing_stairs_constraint_dp"]), + .executable(name: "min_path_sum", targets: ["min_path_sum"]), + .executable(name: "knapsack", targets: ["knapsack"]), + .executable(name: "unbounded_knapsack", targets: ["unbounded_knapsack"]), + .executable(name: "coin_change", targets: ["coin_change"]), + .executable(name: "coin_change_ii", targets: ["coin_change_ii"]), + .executable(name: "edit_distance", targets: ["edit_distance"]), + // chapter_greedy + .executable(name: "coin_change_greedy", targets: ["coin_change_greedy"]), + .executable(name: "fractional_knapsack", targets: ["fractional_knapsack"]), + .executable(name: "max_capacity", targets: ["max_capacity"]), + .executable(name: "max_product_cutting", targets: ["max_product_cutting"]), + ], + dependencies: [ + .package(url: "https://github.com/apple/swift-collections", branch: "release/1.1"), + ], + targets: [ + // helper + .target(name: "utils", path: "utils"), + .target(name: "graph_adjacency_list_target", dependencies: ["utils"], path: "chapter_graph", sources: ["graph_adjacency_list_target.swift"], swiftSettings: [.define("TARGET")]), + .target(name: "binary_search_insertion_target", path: "chapter_searching", sources: ["binary_search_insertion_target.swift"], swiftSettings: [.define("TARGET")]), + // chapter_computational_complexity + .executableTarget(name: "iteration", path: "chapter_computational_complexity", sources: ["iteration.swift"]), + .executableTarget(name: "recursion", path: "chapter_computational_complexity", sources: ["recursion.swift"]), + .executableTarget(name: "time_complexity", path: "chapter_computational_complexity", sources: ["time_complexity.swift"]), + .executableTarget(name: "worst_best_time_complexity", path: "chapter_computational_complexity", sources: ["worst_best_time_complexity.swift"]), + .executableTarget(name: "space_complexity", dependencies: ["utils"], path: "chapter_computational_complexity", sources: ["space_complexity.swift"]), + // chapter_array_and_linkedlist + .executableTarget(name: "array", path: "chapter_array_and_linkedlist", sources: ["array.swift"]), + .executableTarget(name: "linked_list", dependencies: ["utils"], path: "chapter_array_and_linkedlist", sources: ["linked_list.swift"]), + .executableTarget(name: "list", path: "chapter_array_and_linkedlist", sources: ["list.swift"]), + .executableTarget(name: "my_list", path: "chapter_array_and_linkedlist", sources: ["my_list.swift"]), + // chapter_stack_and_queue + .executableTarget(name: "stack", path: "chapter_stack_and_queue", sources: ["stack.swift"]), + .executableTarget(name: "linkedlist_stack", dependencies: ["utils"], path: "chapter_stack_and_queue", sources: ["linkedlist_stack.swift"]), + .executableTarget(name: "array_stack", path: "chapter_stack_and_queue", sources: ["array_stack.swift"]), + .executableTarget(name: "queue", path: "chapter_stack_and_queue", sources: ["queue.swift"]), + .executableTarget(name: "linkedlist_queue", dependencies: ["utils"], path: "chapter_stack_and_queue", sources: ["linkedlist_queue.swift"]), + .executableTarget(name: "array_queue", path: "chapter_stack_and_queue", sources: ["array_queue.swift"]), + .executableTarget(name: "deque", path: "chapter_stack_and_queue", sources: ["deque.swift"]), + .executableTarget(name: "linkedlist_deque", path: "chapter_stack_and_queue", sources: ["linkedlist_deque.swift"]), + .executableTarget(name: "array_deque", path: "chapter_stack_and_queue", sources: ["array_deque.swift"]), + // chapter_hashing + .executableTarget(name: "hash_map", dependencies: ["utils"], path: "chapter_hashing", sources: ["hash_map.swift"]), + .executableTarget(name: "array_hash_map", dependencies: ["utils"], path: "chapter_hashing", sources: ["array_hash_map.swift"]), + .executableTarget(name: "hash_map_chaining", dependencies: ["utils"], path: "chapter_hashing", sources: ["hash_map_chaining.swift"]), + .executableTarget(name: "hash_map_open_addressing", dependencies: ["utils"], path: "chapter_hashing", sources: ["hash_map_open_addressing.swift"]), + .executableTarget(name: "simple_hash", path: "chapter_hashing", sources: ["simple_hash.swift"]), + .executableTarget(name: "built_in_hash", dependencies: ["utils"], path: "chapter_hashing", sources: ["built_in_hash.swift"]), + // chapter_tree + .executableTarget(name: "binary_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["binary_tree.swift"]), + .executableTarget(name: "binary_tree_bfs", dependencies: ["utils"], path: "chapter_tree", sources: ["binary_tree_bfs.swift"]), + .executableTarget(name: "binary_tree_dfs", dependencies: ["utils"], path: "chapter_tree", sources: ["binary_tree_dfs.swift"]), + .executableTarget(name: "array_binary_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["array_binary_tree.swift"]), + .executableTarget(name: "binary_search_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["binary_search_tree.swift"]), + .executableTarget(name: "avl_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["avl_tree.swift"]), + // chapter_heap + .executableTarget(name: "heap", dependencies: ["utils", .product(name: "HeapModule", package: "swift-collections")], path: "chapter_heap", sources: ["heap.swift"]), + .executableTarget(name: "my_heap", dependencies: ["utils"], path: "chapter_heap", sources: ["my_heap.swift"]), + .executableTarget(name: "top_k", dependencies: ["utils", .product(name: "HeapModule", package: "swift-collections")], path: "chapter_heap", sources: ["top_k.swift"]), + // chapter_graph + .executableTarget(name: "graph_adjacency_matrix", dependencies: ["utils"], path: "chapter_graph", sources: ["graph_adjacency_matrix.swift"]), + .executableTarget(name: "graph_adjacency_list", dependencies: ["utils"], path: "chapter_graph", sources: ["graph_adjacency_list.swift"]), + .executableTarget(name: "graph_bfs", dependencies: ["utils", "graph_adjacency_list_target"], path: "chapter_graph", sources: ["graph_bfs.swift"]), + .executableTarget(name: "graph_dfs", dependencies: ["utils", "graph_adjacency_list_target"], path: "chapter_graph", sources: ["graph_dfs.swift"]), + // chapter_searching + .executableTarget(name: "binary_search", path: "chapter_searching", sources: ["binary_search.swift"]), + .executableTarget(name: "binary_search_insertion", path: "chapter_searching", sources: ["binary_search_insertion.swift"]), + .executableTarget(name: "binary_search_edge", dependencies: ["binary_search_insertion_target"], path: "chapter_searching", sources: ["binary_search_edge.swift"]), + .executableTarget(name: "two_sum", path: "chapter_searching", sources: ["two_sum.swift"]), + .executableTarget(name: "linear_search", dependencies: ["utils"], path: "chapter_searching", sources: ["linear_search.swift"]), + .executableTarget(name: "hashing_search", dependencies: ["utils"], path: "chapter_searching", sources: ["hashing_search.swift"]), + // chapter_sorting + .executableTarget(name: "selection_sort", path: "chapter_sorting", sources: ["selection_sort.swift"]), + .executableTarget(name: "bubble_sort", path: "chapter_sorting", sources: ["bubble_sort.swift"]), + .executableTarget(name: "insertion_sort", path: "chapter_sorting", sources: ["insertion_sort.swift"]), + .executableTarget(name: "quick_sort", path: "chapter_sorting", sources: ["quick_sort.swift"]), + .executableTarget(name: "merge_sort", path: "chapter_sorting", sources: ["merge_sort.swift"]), + .executableTarget(name: "heap_sort", path: "chapter_sorting", sources: ["heap_sort.swift"]), + .executableTarget(name: "bucket_sort", path: "chapter_sorting", sources: ["bucket_sort.swift"]), + .executableTarget(name: "counting_sort", path: "chapter_sorting", sources: ["counting_sort.swift"]), + .executableTarget(name: "radix_sort", path: "chapter_sorting", sources: ["radix_sort.swift"]), + // chapter_divide_and_conquer + .executableTarget(name: "binary_search_recur", path: "chapter_divide_and_conquer", sources: ["binary_search_recur.swift"]), + .executableTarget(name: "build_tree", dependencies: ["utils"], path: "chapter_divide_and_conquer", sources: ["build_tree.swift"]), + .executableTarget(name: "hanota", path: "chapter_divide_and_conquer", sources: ["hanota.swift"]), + // chapter_backtracking + .executableTarget(name: "preorder_traversal_i_compact", dependencies: ["utils"], path: "chapter_backtracking", sources: ["preorder_traversal_i_compact.swift"]), + .executableTarget(name: "preorder_traversal_ii_compact", dependencies: ["utils"], path: "chapter_backtracking", sources: ["preorder_traversal_ii_compact.swift"]), + .executableTarget(name: "preorder_traversal_iii_compact", dependencies: ["utils"], path: "chapter_backtracking", sources: ["preorder_traversal_iii_compact.swift"]), + .executableTarget(name: "preorder_traversal_iii_template", dependencies: ["utils"], path: "chapter_backtracking", sources: ["preorder_traversal_iii_template.swift"]), + .executableTarget(name: "permutations_i", path: "chapter_backtracking", sources: ["permutations_i.swift"]), + .executableTarget(name: "permutations_ii", path: "chapter_backtracking", sources: ["permutations_ii.swift"]), + .executableTarget(name: "subset_sum_i_naive", path: "chapter_backtracking", sources: ["subset_sum_i_naive.swift"]), + .executableTarget(name: "subset_sum_i", path: "chapter_backtracking", sources: ["subset_sum_i.swift"]), + .executableTarget(name: "subset_sum_ii", path: "chapter_backtracking", sources: ["subset_sum_ii.swift"]), + .executableTarget(name: "n_queens", path: "chapter_backtracking", sources: ["n_queens.swift"]), + // chapter_dynamic_programming + .executableTarget(name: "climbing_stairs_backtrack", path: "chapter_dynamic_programming", sources: ["climbing_stairs_backtrack.swift"]), + .executableTarget(name: "climbing_stairs_dfs", path: "chapter_dynamic_programming", sources: ["climbing_stairs_dfs.swift"]), + .executableTarget(name: "climbing_stairs_dfs_mem", path: "chapter_dynamic_programming", sources: ["climbing_stairs_dfs_mem.swift"]), + .executableTarget(name: "climbing_stairs_dp", path: "chapter_dynamic_programming", sources: ["climbing_stairs_dp.swift"]), + .executableTarget(name: "min_cost_climbing_stairs_dp", path: "chapter_dynamic_programming", sources: ["min_cost_climbing_stairs_dp.swift"]), + .executableTarget(name: "climbing_stairs_constraint_dp", path: "chapter_dynamic_programming", sources: ["climbing_stairs_constraint_dp.swift"]), + .executableTarget(name: "min_path_sum", path: "chapter_dynamic_programming", sources: ["min_path_sum.swift"]), + .executableTarget(name: "knapsack", path: "chapter_dynamic_programming", sources: ["knapsack.swift"]), + .executableTarget(name: "unbounded_knapsack", path: "chapter_dynamic_programming", sources: ["unbounded_knapsack.swift"]), + .executableTarget(name: "coin_change", path: "chapter_dynamic_programming", sources: ["coin_change.swift"]), + .executableTarget(name: "coin_change_ii", path: "chapter_dynamic_programming", sources: ["coin_change_ii.swift"]), + .executableTarget(name: "edit_distance", path: "chapter_dynamic_programming", sources: ["edit_distance.swift"]), + // chapter_greedy + .executableTarget(name: "coin_change_greedy", path: "chapter_greedy", sources: ["coin_change_greedy.swift"]), + .executableTarget(name: "fractional_knapsack", path: "chapter_greedy", sources: ["fractional_knapsack.swift"]), + .executableTarget(name: "max_capacity", path: "chapter_greedy", sources: ["max_capacity.swift"]), + .executableTarget(name: "max_product_cutting", path: "chapter_greedy", sources: ["max_product_cutting.swift"]), + ] +) diff --git a/en/codes/swift/chapter_array_and_linkedlist/array.swift b/en/codes/swift/chapter_array_and_linkedlist/array.swift new file mode 100644 index 000000000..0a513a17c --- /dev/null +++ b/en/codes/swift/chapter_array_and_linkedlist/array.swift @@ -0,0 +1,107 @@ +/** + * File: array.swift + * Created Time: 2023-01-05 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Random access to element */ +func randomAccess(nums: [Int]) -> Int { + // Randomly select a number in interval [0, nums.count) + let randomIndex = nums.indices.randomElement()! + // Retrieve and return the random element + let randomNum = nums[randomIndex] + return randomNum +} + +/* Extend array length */ +func extend(nums: [Int], enlarge: Int) -> [Int] { + // Initialize an array with extended length + var res = Array(repeating: 0, count: nums.count + enlarge) + // Copy all elements from the original array to the new array + for i in nums.indices { + res[i] = nums[i] + } + // Return the extended new array + return res +} + +/* Insert element num at index index in the array */ +func insert(nums: inout [Int], num: Int, index: Int) { + // Move all elements at and after index index backward by one position + for i in nums.indices.dropFirst(index).reversed() { + nums[i] = nums[i - 1] + } + // Assign num to the element at index index + nums[index] = num +} + +/* Remove the element at index index */ +func remove(nums: inout [Int], index: Int) { + // Move all elements after index index forward by one position + for i in nums.indices.dropFirst(index).dropLast() { + nums[i] = nums[i + 1] + } +} + +/* Traverse array */ +func traverse(nums: [Int]) { + var count = 0 + // Traverse array by index + for i in nums.indices { + count += nums[i] + } + // Direct traversal of array elements + for num in nums { + count += num + } + // Traverse simultaneously data index and elements + for (i, num) in nums.enumerated() { + count += nums[i] + count += num + } +} + +/* Find the specified element in the array */ +func find(nums: [Int], target: Int) -> Int { + for i in nums.indices { + if nums[i] == target { + return i + } + } + return -1 +} + +@main +enum _Array { + /* Driver Code */ + static func main() { + /* Initialize array */ + let arr = Array(repeating: 0, count: 5) + print("Array arr = \(arr)") + var nums = [1, 3, 2, 5, 4] + print("Array nums = \(nums)") + + /* Insert element */ + let randomNum = randomAccess(nums: nums) + print("Get random element \(randomNum) from nums") + + /* Traverse array */ + nums = extend(nums: nums, enlarge: 3) + print("Extend array length to 8, get nums = \(nums)") + + /* Insert element */ + insert(nums: &nums, num: 6, index: 3) + print("Insert number 6 at index 3, get nums = \(nums)") + + /* Remove element */ + remove(nums: &nums, index: 2) + print("Delete element at index 2, get nums = \(nums)") + + /* Traverse array */ + traverse(nums: nums) + + /* Find element */ + let index = find(nums: nums, target: 3) + print("Find element 3 in nums, index = \(index)") + } +} diff --git a/en/codes/swift/chapter_array_and_linkedlist/linked_list.swift b/en/codes/swift/chapter_array_and_linkedlist/linked_list.swift new file mode 100644 index 000000000..448623a55 --- /dev/null +++ b/en/codes/swift/chapter_array_and_linkedlist/linked_list.swift @@ -0,0 +1,90 @@ +/** + * File: linked_list.swift + * Created Time: 2023-01-08 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Insert node P after node n0 in the linked list */ +func insert(n0: ListNode, P: ListNode) { + let n1 = n0.next + P.next = n1 + n0.next = P +} + +/* Remove the first node after node n0 in the linked list */ +func remove(n0: ListNode) { + if n0.next == nil { + return + } + // n0 -> P -> n1 + let P = n0.next + let n1 = P?.next + n0.next = n1 +} + +/* Access the node at index index in the linked list */ +func access(head: ListNode, index: Int) -> ListNode? { + var head: ListNode? = head + for _ in 0 ..< index { + if head == nil { + return nil + } + head = head?.next + } + return head +} + +/* Find the first node with value target in the linked list */ +func find(head: ListNode, target: Int) -> Int { + var head: ListNode? = head + var index = 0 + while head != nil { + if head?.val == target { + return index + } + head = head?.next + index += 1 + } + return -1 +} + +@main +enum LinkedList { + /* Driver Code */ + static func main() { + /* Initialize linked list */ + // Initialize each node + let n0 = ListNode(x: 1) + let n1 = ListNode(x: 3) + let n2 = ListNode(x: 2) + let n3 = ListNode(x: 5) + let n4 = ListNode(x: 4) + // Build references between nodes + n0.next = n1 + n1.next = n2 + n2.next = n3 + n3.next = n4 + print("Initialized linked list is") + PrintUtil.printLinkedList(head: n0) + + /* Insert node */ + insert(n0: n0, P: ListNode(x: 0)) + print("Linked list after inserting node is") + PrintUtil.printLinkedList(head: n0) + + /* Remove node */ + remove(n0: n0) + print("Linked list after removing node is") + PrintUtil.printLinkedList(head: n0) + + /* Access node */ + let node = access(head: n0, index: 3) + print("Value of node at index 3 in linked list = \(node!.val)") + + /* Search node */ + let index = find(head: n0, target: 2) + print("Index of node with value 2 in linked list = \(index)") + } +} diff --git a/en/codes/swift/chapter_array_and_linkedlist/list.swift b/en/codes/swift/chapter_array_and_linkedlist/list.swift new file mode 100644 index 000000000..00ffef9a5 --- /dev/null +++ b/en/codes/swift/chapter_array_and_linkedlist/list.swift @@ -0,0 +1,63 @@ +/** + * File: list.swift + * Created Time: 2023-01-08 + * Author: nuomi1 (nuomi1@qq.com) + */ + +@main +enum List { + /* Driver Code */ + static func main() { + /* Initialize list */ + var nums = [1, 3, 2, 5, 4] + print("List nums = \(nums)") + + /* Update element */ + let num = nums[1] + print("Access element at index 1, get num = \(num)") + + /* Add elements at the end */ + nums[1] = 0 + print("Update element at index 1 to 0, get nums = \(nums)") + + /* Remove element */ + nums.removeAll() + print("After clearing list, nums = \(nums)") + + /* Direct traversal of list elements */ + nums.append(1) + nums.append(3) + nums.append(2) + nums.append(5) + nums.append(4) + print("After adding elements, nums = \(nums)") + + /* Sort list */ + nums.insert(6, at: 3) + print("Insert number 6 at index 3, get nums = \(nums)") + + /* Remove element */ + nums.remove(at: 3) + print("Delete element at index 3, get nums = \(nums)") + + /* Traverse list by index */ + var count = 0 + for i in nums.indices { + count += nums[i] + } + /* Directly traverse list elements */ + count = 0 + for x in nums { + count += x + } + + /* Concatenate two lists */ + let nums1 = [6, 8, 7, 10, 9] + nums.append(contentsOf: nums1) + print("After concatenating list nums1 to nums, get nums = \(nums)") + + /* Sort list */ + nums.sort() + print("After sorting list, nums = \(nums)") + } +} diff --git a/en/codes/swift/chapter_array_and_linkedlist/my_list.swift b/en/codes/swift/chapter_array_and_linkedlist/my_list.swift new file mode 100644 index 000000000..dcdfeb505 --- /dev/null +++ b/en/codes/swift/chapter_array_and_linkedlist/my_list.swift @@ -0,0 +1,146 @@ +/** + * File: my_list.swift + * Created Time: 2023-01-08 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* List class */ +class MyList { + private var arr: [Int] // Array (stores list elements) + private var _capacity: Int // List capacity + private var _size: Int // List length (current number of elements) + private let extendRatio: Int // Multiple by which the list capacity is extended each time + + /* Constructor */ + init() { + _capacity = 10 + _size = 0 + extendRatio = 2 + arr = Array(repeating: 0, count: _capacity) + } + + /* Get list length (current number of elements) */ + func size() -> Int { + _size + } + + /* Get list capacity */ + func capacity() -> Int { + _capacity + } + + /* Update element */ + func get(index: Int) -> Int { + // Throw error if index out of bounds, same below + if index < 0 || index >= size() { + fatalError("Index out of bounds") + } + return arr[index] + } + + /* Add elements at the end */ + func set(index: Int, num: Int) { + if index < 0 || index >= size() { + fatalError("Index out of bounds") + } + arr[index] = num + } + + /* Direct traversal of list elements */ + func add(num: Int) { + // When the number of elements exceeds capacity, trigger the extension mechanism + if size() == capacity() { + extendCapacity() + } + arr[size()] = num + // Update the number of elements + _size += 1 + } + + /* Sort list */ + func insert(index: Int, num: Int) { + if index < 0 || index >= size() { + fatalError("Index out of bounds") + } + // When the number of elements exceeds capacity, trigger the extension mechanism + if size() == capacity() { + extendCapacity() + } + // Move all elements after index index forward by one position + for j in (index ..< size()).reversed() { + arr[j + 1] = arr[j] + } + arr[index] = num + // Update the number of elements + _size += 1 + } + + /* Remove element */ + @discardableResult + func remove(index: Int) -> Int { + if index < 0 || index >= size() { + fatalError("Index out of bounds") + } + let num = arr[index] + // Move all elements after index forward by one position + for j in index ..< (size() - 1) { + arr[j] = arr[j + 1] + } + // Update the number of elements + _size -= 1 + // Return the removed element + return num + } + + /* Driver Code */ + func extendCapacity() { + // Create a new array with length extendRatio times the original array and copy the original array to the new array + arr = arr + Array(repeating: 0, count: capacity() * (extendRatio - 1)) + // Add elements at the end + _capacity = arr.count + } + + /* Convert list to array */ + func toArray() -> [Int] { + Array(arr.prefix(size())) + } +} + +@main +enum _MyList { + /* Driver Code */ + static func main() { + /* Initialize list */ + let nums = MyList() + /* Direct traversal of list elements */ + nums.add(num: 1) + nums.add(num: 3) + nums.add(num: 2) + nums.add(num: 5) + nums.add(num: 4) + print("List nums = \(nums.toArray()), capacity = \(nums.capacity()), length = \(nums.size())") + + /* Sort list */ + nums.insert(index: 3, num: 6) + print("Insert number 6 at index 3, get nums = \(nums.toArray())") + + /* Remove element */ + nums.remove(index: 3) + print("Delete element at index 3, get nums = \(nums.toArray())") + + /* Update element */ + let num = nums.get(index: 1) + print("Access element at index 1, get num = \(num)") + + /* Add elements at the end */ + nums.set(index: 1, num: 0) + print("Update element at index 1 to 0, get nums = \(nums.toArray())") + + /* Test capacity expansion mechanism */ + for i in 0 ..< 10 { + // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + nums.add(num: i) + } + print("After expansion, list nums = \(nums.toArray()), capacity = \(nums.capacity()), length = \(nums.size())") + } +} diff --git a/en/codes/swift/chapter_backtracking/n_queens.swift b/en/codes/swift/chapter_backtracking/n_queens.swift new file mode 100644 index 000000000..d429a5706 --- /dev/null +++ b/en/codes/swift/chapter_backtracking/n_queens.swift @@ -0,0 +1,67 @@ +/** + * File: n_queens.swift + * Created Time: 2023-05-14 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Backtracking algorithm: N queens */ +func backtrack(row: Int, n: Int, state: inout [[String]], res: inout [[[String]]], cols: inout [Bool], diags1: inout [Bool], diags2: inout [Bool]) { + // When all rows are placed, record the solution + if row == n { + res.append(state) + return + } + // Traverse all columns + for col in 0 ..< n { + // Calculate the main diagonal and anti-diagonal corresponding to this cell + let diag1 = row - col + n - 1 + let diag2 = row + col + // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + if !cols[col] && !diags1[diag1] && !diags2[diag2] { + // Attempt: place the queen in this cell + state[row][col] = "Q" + cols[col] = true + diags1[diag1] = true + diags2[diag2] = true + // Place the next row + backtrack(row: row + 1, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2) + // Backtrack: restore this cell to an empty cell + state[row][col] = "#" + cols[col] = false + diags1[diag1] = false + diags2[diag2] = false + } + } +} + +/* Solve N queens */ +func nQueens(n: Int) -> [[[String]]] { + // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + var state = Array(repeating: Array(repeating: "#", count: n), count: n) + var cols = Array(repeating: false, count: n) // Record whether there is a queen in the column + var diags1 = Array(repeating: false, count: 2 * n - 1) // Record whether there is a queen on the main diagonal + var diags2 = Array(repeating: false, count: 2 * n - 1) // Record whether there is a queen on the anti-diagonal + var res: [[[String]]] = [] + + backtrack(row: 0, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2) + + return res +} + +@main +enum NQueens { + /* Driver Code */ + static func main() { + let n = 4 + let res = nQueens(n: n) + + print("Input board size is \(n)") + print("Total queen placement solutions: \(res.count)") + for state in res { + print("--------------------") + for row in state { + print(row) + } + } + } +} diff --git a/en/codes/swift/chapter_backtracking/permutations_i.swift b/en/codes/swift/chapter_backtracking/permutations_i.swift new file mode 100644 index 000000000..780e2f014 --- /dev/null +++ b/en/codes/swift/chapter_backtracking/permutations_i.swift @@ -0,0 +1,50 @@ +/** + * File: permutations_i.swift + * Created Time: 2023-04-30 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Backtracking algorithm: Permutations I */ +func backtrack(state: inout [Int], choices: [Int], selected: inout [Bool], res: inout [[Int]]) { + // When the state length equals the number of elements, record the solution + if state.count == choices.count { + res.append(state) + return + } + // Traverse all choices + for (i, choice) in choices.enumerated() { + // Pruning: do not allow repeated selection of elements + if !selected[i] { + // Attempt: make choice, update state + selected[i] = true + state.append(choice) + // Proceed to the next round of selection + backtrack(state: &state, choices: choices, selected: &selected, res: &res) + // Backtrack: undo choice, restore to previous state + selected[i] = false + state.removeLast() + } + } +} + +/* Permutations I */ +func permutationsI(nums: [Int]) -> [[Int]] { + var state: [Int] = [] + var selected = Array(repeating: false, count: nums.count) + var res: [[Int]] = [] + backtrack(state: &state, choices: nums, selected: &selected, res: &res) + return res +} + +@main +enum PermutationsI { + /* Driver Code */ + static func main() { + let nums = [1, 2, 3] + + let res = permutationsI(nums: nums) + + print("Input array nums = \(nums)") + print("All permutations res = \(res)") + } +} diff --git a/en/codes/swift/chapter_backtracking/permutations_ii.swift b/en/codes/swift/chapter_backtracking/permutations_ii.swift new file mode 100644 index 000000000..7dd43c1bd --- /dev/null +++ b/en/codes/swift/chapter_backtracking/permutations_ii.swift @@ -0,0 +1,52 @@ +/** + * File: permutations_ii.swift + * Created Time: 2023-04-30 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Backtracking algorithm: Permutations II */ +func backtrack(state: inout [Int], choices: [Int], selected: inout [Bool], res: inout [[Int]]) { + // When the state length equals the number of elements, record the solution + if state.count == choices.count { + res.append(state) + return + } + // Traverse all choices + var duplicated: Set = [] + for (i, choice) in choices.enumerated() { + // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if !selected[i], !duplicated.contains(choice) { + // Attempt: make choice, update state + duplicated.insert(choice) // Record the selected element value + selected[i] = true + state.append(choice) + // Proceed to the next round of selection + backtrack(state: &state, choices: choices, selected: &selected, res: &res) + // Backtrack: undo choice, restore to previous state + selected[i] = false + state.removeLast() + } + } +} + +/* Permutations II */ +func permutationsII(nums: [Int]) -> [[Int]] { + var state: [Int] = [] + var selected = Array(repeating: false, count: nums.count) + var res: [[Int]] = [] + backtrack(state: &state, choices: nums, selected: &selected, res: &res) + return res +} + +@main +enum PermutationsII { + /* Driver Code */ + static func main() { + let nums = [1, 2, 3] + + let res = permutationsII(nums: nums) + + print("Input array nums = \(nums)") + print("All permutations res = \(res)") + } +} diff --git a/en/codes/swift/chapter_backtracking/preorder_traversal_i_compact.swift b/en/codes/swift/chapter_backtracking/preorder_traversal_i_compact.swift new file mode 100644 index 000000000..ac0e459ed --- /dev/null +++ b/en/codes/swift/chapter_backtracking/preorder_traversal_i_compact.swift @@ -0,0 +1,43 @@ +/** + * File: preorder_traversal_i_compact.swift + * Created Time: 2023-04-30 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +var res: [TreeNode] = [] + +/* Preorder traversal: Example 1 */ +func preOrder(root: TreeNode?) { + guard let root = root else { + return + } + if root.val == 7 { + // Record solution + res.append(root) + } + preOrder(root: root.left) + preOrder(root: root.right) +} + +@main +enum PreorderTraversalICompact { + /* Driver Code */ + static func main() { + let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7]) + print("\nInitialize binary tree") + PrintUtil.printTree(root: root) + + // Preorder traversal + res = [] + preOrder(root: root) + + print("\nOutput all nodes with value 7") + var vals: [Int] = [] + for node in res { + vals.append(node.val) + } + print(vals) + } +} diff --git a/en/codes/swift/chapter_backtracking/preorder_traversal_ii_compact.swift b/en/codes/swift/chapter_backtracking/preorder_traversal_ii_compact.swift new file mode 100644 index 000000000..c34fa5cfd --- /dev/null +++ b/en/codes/swift/chapter_backtracking/preorder_traversal_ii_compact.swift @@ -0,0 +1,51 @@ +/** + * File: preorder_traversal_ii_compact.swift + * Created Time: 2023-04-30 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +var path: [TreeNode] = [] +var res: [[TreeNode]] = [] + +/* Preorder traversal: Example 2 */ +func preOrder(root: TreeNode?) { + guard let root = root else { + return + } + // Attempt + path.append(root) + if root.val == 7 { + // Record solution + res.append(path) + } + preOrder(root: root.left) + preOrder(root: root.right) + // Backtrack + path.removeLast() +} + +@main +enum PreorderTraversalIICompact { + /* Driver Code */ + static func main() { + let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7]) + print("\nInitialize binary tree") + PrintUtil.printTree(root: root) + + // Preorder traversal + path = [] + res = [] + preOrder(root: root) + + print("\nOutput all paths from root node to node 7") + for path in res { + var vals: [Int] = [] + for node in path { + vals.append(node.val) + } + print(vals) + } + } +} diff --git a/en/codes/swift/chapter_backtracking/preorder_traversal_iii_compact.swift b/en/codes/swift/chapter_backtracking/preorder_traversal_iii_compact.swift new file mode 100644 index 000000000..da3410b03 --- /dev/null +++ b/en/codes/swift/chapter_backtracking/preorder_traversal_iii_compact.swift @@ -0,0 +1,52 @@ +/** + * File: preorder_traversal_iii_compact.swift + * Created Time: 2023-04-30 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +var path: [TreeNode] = [] +var res: [[TreeNode]] = [] + +/* Preorder traversal: Example 3 */ +func preOrder(root: TreeNode?) { + // Pruning + guard let root = root, root.val != 3 else { + return + } + // Attempt + path.append(root) + if root.val == 7 { + // Record solution + res.append(path) + } + preOrder(root: root.left) + preOrder(root: root.right) + // Backtrack + path.removeLast() +} + +@main +enum PreorderTraversalIIICompact { + /* Driver Code */ + static func main() { + let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7]) + print("\nInitialize binary tree") + PrintUtil.printTree(root: root) + + // Preorder traversal + path = [] + res = [] + preOrder(root: root) + + print("\nOutput all paths from root node to node 7, paths do not include nodes with value 3") + for path in res { + var vals: [Int] = [] + for node in path { + vals.append(node.val) + } + print(vals) + } + } +} diff --git a/en/codes/swift/chapter_backtracking/preorder_traversal_iii_template.swift b/en/codes/swift/chapter_backtracking/preorder_traversal_iii_template.swift new file mode 100644 index 000000000..b825cb000 --- /dev/null +++ b/en/codes/swift/chapter_backtracking/preorder_traversal_iii_template.swift @@ -0,0 +1,76 @@ +/** + * File: preorder_traversal_iii_template.swift + * Created Time: 2023-04-30 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Check if the current state is a solution */ +func isSolution(state: [TreeNode]) -> Bool { + !state.isEmpty && state.last!.val == 7 +} + +/* Record solution */ +func recordSolution(state: [TreeNode], res: inout [[TreeNode]]) { + res.append(state) +} + +/* Check if the choice is valid under the current state */ +func isValid(state: [TreeNode], choice: TreeNode?) -> Bool { + choice != nil && choice!.val != 3 +} + +/* Update state */ +func makeChoice(state: inout [TreeNode], choice: TreeNode) { + state.append(choice) +} + +/* Restore state */ +func undoChoice(state: inout [TreeNode], choice: TreeNode) { + state.removeLast() +} + +/* Backtracking algorithm: Example 3 */ +func backtrack(state: inout [TreeNode], choices: [TreeNode], res: inout [[TreeNode]]) { + // Check if it is a solution + if isSolution(state: state) { + recordSolution(state: state, res: &res) + } + // Traverse all choices + for choice in choices { + // Pruning: check if the choice is valid + if isValid(state: state, choice: choice) { + // Attempt: make choice, update state + makeChoice(state: &state, choice: choice) + // Proceed to the next round of selection + backtrack(state: &state, choices: [choice.left, choice.right].compactMap { $0 }, res: &res) + // Backtrack: undo choice, restore to previous state + undoChoice(state: &state, choice: choice) + } + } +} + +@main +enum PreorderTraversalIIITemplate { + /* Driver Code */ + static func main() { + let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7]) + print("\nInitialize binary tree") + PrintUtil.printTree(root: root) + + // Backtracking algorithm + var state: [TreeNode] = [] + var res: [[TreeNode]] = [] + backtrack(state: &state, choices: [root].compactMap { $0 }, res: &res) + + print("\nOutput all paths from root node to node 7, paths do not include nodes with value 3") + for path in res { + var vals: [Int] = [] + for node in path { + vals.append(node.val) + } + print(vals) + } + } +} diff --git a/en/codes/swift/chapter_backtracking/subset_sum_i.swift b/en/codes/swift/chapter_backtracking/subset_sum_i.swift new file mode 100644 index 000000000..77f710563 --- /dev/null +++ b/en/codes/swift/chapter_backtracking/subset_sum_i.swift @@ -0,0 +1,53 @@ +/** + * File: subset_sum_i.swift + * Created Time: 2023-07-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Backtracking algorithm: Subset sum I */ +func backtrack(state: inout [Int], target: Int, choices: [Int], start: Int, res: inout [[Int]]) { + // When the subset sum equals target, record the solution + if target == 0 { + res.append(state) + return + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + for i in choices.indices.dropFirst(start) { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if target - choices[i] < 0 { + break + } + // Attempt: make choice, update target, start + state.append(choices[i]) + // Proceed to the next round of selection + backtrack(state: &state, target: target - choices[i], choices: choices, start: i, res: &res) + // Backtrack: undo choice, restore to previous state + state.removeLast() + } +} + +/* Solve subset sum I */ +func subsetSumI(nums: [Int], target: Int) -> [[Int]] { + var state: [Int] = [] // State (subset) + let nums = nums.sorted() // Sort nums + let start = 0 // Start point for traversal + var res: [[Int]] = [] // Result list (subset list) + backtrack(state: &state, target: target, choices: nums, start: start, res: &res) + return res +} + +@main +enum SubsetSumI { + /* Driver Code */ + static func main() { + let nums = [3, 4, 5] + let target = 9 + + let res = subsetSumI(nums: nums, target: target) + + print("Input array nums = \(nums), target = \(target)") + print("All subsets with sum equal to \(target) res = \(res)") + } +} diff --git a/en/codes/swift/chapter_backtracking/subset_sum_i_naive.swift b/en/codes/swift/chapter_backtracking/subset_sum_i_naive.swift new file mode 100644 index 000000000..9a544722b --- /dev/null +++ b/en/codes/swift/chapter_backtracking/subset_sum_i_naive.swift @@ -0,0 +1,51 @@ +/** + * File: subset_sum_i_naive.swift + * Created Time: 2023-07-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Backtracking algorithm: Subset sum I */ +func backtrack(state: inout [Int], target: Int, total: Int, choices: [Int], res: inout [[Int]]) { + // When the subset sum equals target, record the solution + if total == target { + res.append(state) + return + } + // Traverse all choices + for i in choices.indices { + // Pruning: if the subset sum exceeds target, skip this choice + if total + choices[i] > target { + continue + } + // Attempt: make choice, update element sum total + state.append(choices[i]) + // Proceed to the next round of selection + backtrack(state: &state, target: target, total: total + choices[i], choices: choices, res: &res) + // Backtrack: undo choice, restore to previous state + state.removeLast() + } +} + +/* Solve subset sum I (including duplicate subsets) */ +func subsetSumINaive(nums: [Int], target: Int) -> [[Int]] { + var state: [Int] = [] // State (subset) + let total = 0 // Subset sum + var res: [[Int]] = [] // Result list (subset list) + backtrack(state: &state, target: target, total: total, choices: nums, res: &res) + return res +} + +@main +enum SubsetSumINaive { + /* Driver Code */ + static func main() { + let nums = [3, 4, 5] + let target = 9 + + let res = subsetSumINaive(nums: nums, target: target) + + print("Input array nums = \(nums), target = \(target)") + print("All subsets with sum equal to \(target) res = \(res)") + print("Please note that this method outputs results containing duplicate sets") + } +} diff --git a/en/codes/swift/chapter_backtracking/subset_sum_ii.swift b/en/codes/swift/chapter_backtracking/subset_sum_ii.swift new file mode 100644 index 000000000..6c67ea2cd --- /dev/null +++ b/en/codes/swift/chapter_backtracking/subset_sum_ii.swift @@ -0,0 +1,58 @@ +/** + * File: subset_sum_ii.swift + * Created Time: 2023-07-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Backtracking algorithm: Subset sum II */ +func backtrack(state: inout [Int], target: Int, choices: [Int], start: Int, res: inout [[Int]]) { + // When the subset sum equals target, record the solution + if target == 0 { + res.append(state) + return + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + for i in choices.indices.dropFirst(start) { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if target - choices[i] < 0 { + break + } + // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + if i > start, choices[i] == choices[i - 1] { + continue + } + // Attempt: make choice, update target, start + state.append(choices[i]) + // Proceed to the next round of selection + backtrack(state: &state, target: target - choices[i], choices: choices, start: i + 1, res: &res) + // Backtrack: undo choice, restore to previous state + state.removeLast() + } +} + +/* Solve subset sum II */ +func subsetSumII(nums: [Int], target: Int) -> [[Int]] { + var state: [Int] = [] // State (subset) + let nums = nums.sorted() // Sort nums + let start = 0 // Start point for traversal + var res: [[Int]] = [] // Result list (subset list) + backtrack(state: &state, target: target, choices: nums, start: start, res: &res) + return res +} + +@main +enum SubsetSumII { + /* Driver Code */ + static func main() { + let nums = [4, 4, 5] + let target = 9 + + let res = subsetSumII(nums: nums, target: target) + + print("Input array nums = \(nums), target = \(target)") + print("All subsets with sum equal to \(target) res = \(res)") + } +} diff --git a/en/codes/swift/chapter_computational_complexity/iteration.swift b/en/codes/swift/chapter_computational_complexity/iteration.swift new file mode 100644 index 000000000..84c9e891c --- /dev/null +++ b/en/codes/swift/chapter_computational_complexity/iteration.swift @@ -0,0 +1,75 @@ +/** + * File: iteration.swift + * Created Time: 2023-09-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* for loop */ +func forLoop(n: Int) -> Int { + var res = 0 + // Sum 1, 2, ..., n-1, n + for i in 1 ... n { + res += i + } + return res +} + +/* while loop */ +func whileLoop(n: Int) -> Int { + var res = 0 + var i = 1 // Initialize condition variable + // Sum 1, 2, ..., n-1, n + while i <= n { + res += i + i += 1 // Update condition variable + } + return res +} + +/* while loop (two updates) */ +func whileLoopII(n: Int) -> Int { + var res = 0 + var i = 1 // Initialize condition variable + // Sum 1, 4, 10, ... + while i <= n { + res += i + // Update condition variable + i += 1 + i *= 2 + } + return res +} + +/* Nested for loop */ +func nestedForLoop(n: Int) -> String { + var res = "" + // Loop i = 1, 2, ..., n-1, n + for i in 1 ... n { + // Loop j = 1, 2, ..., n-1, n + for j in 1 ... n { + res.append("(\(i), \(j)), ") + } + } + return res +} + +@main +enum Iteration { + /* Driver Code */ + static func main() { + let n = 5 + var res = 0 + + res = forLoop(n: n) + print("\nFor loop sum result res = \(res)") + + res = whileLoop(n: n) + print("\nWhile loop sum result res = \(res)") + + res = whileLoopII(n: n) + print("\nWhile loop (two updates) sum result res = \(res)") + + let resStr = nestedForLoop(n: n) + print("\nNested for loop traversal result \(resStr)") + } +} diff --git a/en/codes/swift/chapter_computational_complexity/recursion.swift b/en/codes/swift/chapter_computational_complexity/recursion.swift new file mode 100644 index 000000000..060473760 --- /dev/null +++ b/en/codes/swift/chapter_computational_complexity/recursion.swift @@ -0,0 +1,79 @@ +/** + * File: recursion.swift + * Created Time: 2023-09-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Recursion */ +func recur(n: Int) -> Int { + // Termination condition + if n == 1 { + return 1 + } + // Recurse: recursive call + let res = recur(n: n - 1) + // Return: return result + return n + res +} + +/* Simulate recursion using iteration */ +func forLoopRecur(n: Int) -> Int { + // Use an explicit stack to simulate the system call stack + var stack: [Int] = [] + var res = 0 + // Recurse: recursive call + for i in (1 ... n).reversed() { + // Simulate "recurse" with "push" + stack.append(i) + } + // Return: return result + while !stack.isEmpty { + // Simulate "return" with "pop" + res += stack.removeLast() + } + // res = 1+2+3+...+n + return res +} + +/* Tail recursion */ +func tailRecur(n: Int, res: Int) -> Int { + // Termination condition + if n == 0 { + return res + } + // Tail recursive call + return tailRecur(n: n - 1, res: res + n) +} + +/* Fibonacci sequence: recursion */ +func fib(n: Int) -> Int { + // Termination condition f(1) = 0, f(2) = 1 + if n == 1 || n == 2 { + return n - 1 + } + // Recursive call f(n) = f(n-1) + f(n-2) + let res = fib(n: n - 1) + fib(n: n - 2) + // Return result f(n) + return res +} + +@main +enum Recursion { + /* Driver Code */ + static func main() { + let n = 5 + var res = 0 + + res = recursion.recur(n: n) + print("\nRecursion sum result res = \(res)") + + res = recursion.forLoopRecur(n: n) + print("\nUsing iteration to simulate recursion sum result res = \(res)") + + res = recursion.tailRecur(n: n, res: 0) + print("\nTail recursion sum result res = \(res)") + + res = recursion.fib(n: n) + print("\nThe \(n)th Fibonacci number is \(res)") + } +} diff --git a/en/codes/swift/chapter_computational_complexity/space_complexity.swift b/en/codes/swift/chapter_computational_complexity/space_complexity.swift new file mode 100644 index 000000000..c4cdf0416 --- /dev/null +++ b/en/codes/swift/chapter_computational_complexity/space_complexity.swift @@ -0,0 +1,98 @@ +/** + * File: space_complexity.swift + * Created Time: 2023-01-01 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Function */ +@discardableResult +func function() -> Int { + // Perform some operations + return 0 +} + +/* Constant order */ +func constant(n: Int) { + // Constants, variables, objects occupy O(1) space + let a = 0 + var b = 0 + let nums = Array(repeating: 0, count: 10000) + let node = ListNode(x: 0) + // Variables in the loop occupy O(1) space + for _ in 0 ..< n { + let c = 0 + } + // Functions in the loop occupy O(1) space + for _ in 0 ..< n { + function() + } +} + +/* Linear order */ +func linear(n: Int) { + // Array of length n uses O(n) space + let nums = Array(repeating: 0, count: n) + // A list of length n occupies O(n) space + let nodes = (0 ..< n).map { ListNode(x: $0) } + // A hash table of length n occupies O(n) space + let map = Dictionary(uniqueKeysWithValues: (0 ..< n).map { ($0, "\($0)") }) +} + +/* Linear order (recursive implementation) */ +func linearRecur(n: Int) { + print("Recursion n = \(n)") + if n == 1 { + return + } + linearRecur(n: n - 1) +} + +/* Exponential order */ +func quadratic(n: Int) { + // 2D list uses O(n^2) space + let numList = Array(repeating: Array(repeating: 0, count: n), count: n) +} + +/* Quadratic order (recursive implementation) */ +@discardableResult +func quadraticRecur(n: Int) -> Int { + if n <= 0 { + return 0 + } + // Array nums has length n, n-1, ..., 2, 1 + let nums = Array(repeating: 0, count: n) + print("In recursion n = \(n), nums length = \(nums.count)") + return quadraticRecur(n: n - 1) +} + +/* Driver Code */ +func buildTree(n: Int) -> TreeNode? { + if n == 0 { + return nil + } + let root = TreeNode(x: 0) + root.left = buildTree(n: n - 1) + root.right = buildTree(n: n - 1) + return root +} + +@main +enum SpaceComplexity { + /* Driver Code */ + static func main() { + let n = 5 + // Constant order + constant(n: n) + // Linear order + linear(n: n) + linearRecur(n: n) + // Exponential order + quadratic(n: n) + quadraticRecur(n: n) + // Exponential order + let root = buildTree(n: n) + PrintUtil.printTree(root: root) + } +} diff --git a/en/codes/swift/chapter_computational_complexity/time_complexity.swift b/en/codes/swift/chapter_computational_complexity/time_complexity.swift new file mode 100644 index 000000000..877e1cd11 --- /dev/null +++ b/en/codes/swift/chapter_computational_complexity/time_complexity.swift @@ -0,0 +1,172 @@ +/** + * File: time_complexity.swift + * Created Time: 2022-12-26 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Constant order */ +func constant(n: Int) -> Int { + var count = 0 + let size = 100_000 + for _ in 0 ..< size { + count += 1 + } + return count +} + +/* Linear order */ +func linear(n: Int) -> Int { + var count = 0 + for _ in 0 ..< n { + count += 1 + } + return count +} + +/* Linear order (traversing array) */ +func arrayTraversal(nums: [Int]) -> Int { + var count = 0 + // Number of iterations is proportional to the array length + for _ in nums { + count += 1 + } + return count +} + +/* Exponential order */ +func quadratic(n: Int) -> Int { + var count = 0 + // Number of iterations is quadratically related to the data size n + for _ in 0 ..< n { + for _ in 0 ..< n { + count += 1 + } + } + return count +} + +/* Quadratic order (bubble sort) */ +func bubbleSort(nums: inout [Int]) -> Int { + var count = 0 // Counter + // Outer loop: unsorted range is [0, i] + for i in nums.indices.dropFirst().reversed() { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for j in 0 ..< i { + if nums[j] > nums[j + 1] { + // Swap nums[j] and nums[j + 1] + let tmp = nums[j] + nums[j] = nums[j + 1] + nums[j + 1] = tmp + count += 3 // Element swap includes 3 unit operations + } + } + } + return count +} + +/* Exponential order (loop implementation) */ +func exponential(n: Int) -> Int { + var count = 0 + var base = 1 + // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + for _ in 0 ..< n { + for _ in 0 ..< base { + count += 1 + } + base *= 2 + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count +} + +/* Exponential order (recursive implementation) */ +func expRecur(n: Int) -> Int { + if n == 1 { + return 1 + } + return expRecur(n: n - 1) + expRecur(n: n - 1) + 1 +} + +/* Logarithmic order (loop implementation) */ +func logarithmic(n: Int) -> Int { + var count = 0 + var n = n + while n > 1 { + n = n / 2 + count += 1 + } + return count +} + +/* Logarithmic order (recursive implementation) */ +func logRecur(n: Int) -> Int { + if n <= 1 { + return 0 + } + return logRecur(n: n / 2) + 1 +} + +/* Linearithmic order */ +func linearLogRecur(n: Int) -> Int { + if n <= 1 { + return 1 + } + var count = linearLogRecur(n: n / 2) + linearLogRecur(n: n / 2) + for _ in stride(from: 0, to: n, by: 1) { + count += 1 + } + return count +} + +/* Factorial order (recursive implementation) */ +func factorialRecur(n: Int) -> Int { + if n == 0 { + return 1 + } + var count = 0 + // Split from 1 into n + for _ in 0 ..< n { + count += factorialRecur(n: n - 1) + } + return count +} + +@main +enum TimeComplexity { + /* Driver Code */ + static func main() { + // You can modify n to run and observe the trend of the number of operations for various complexities + let n = 8 + print("Input data size n = \(n)") + + var count = constant(n: n) + print("Constant-time operations count = \(count)") + + count = linear(n: n) + print("Linear-time operations count = \(count)") + count = arrayTraversal(nums: Array(repeating: 0, count: n)) + print("Linear-time (array traversal) operations count = \(count)") + + count = quadratic(n: n) + print("Quadratic-time operations count = \(count)") + var nums = Array(stride(from: n, to: 0, by: -1)) // [n,n-1,...,2,1] + count = bubbleSort(nums: &nums) + print("Quadratic-time (bubble sort) operations count = \(count)") + + count = exponential(n: n) + print("Exponential-time (iterative) operations count = \(count)") + count = expRecur(n: n) + print("Exponential-time (recursive) operations count = \(count)") + + count = logarithmic(n: n) + print("Logarithmic-time (iterative) operations count = \(count)") + count = logRecur(n: n) + print("Logarithmic-time (recursive) operations count = \(count)") + + count = linearLogRecur(n: n) + print("Linearithmic-time (recursive) operations count = \(count)") + + count = factorialRecur(n: n) + print("Factorial-time (recursive) operations count = \(count)") + } +} diff --git a/en/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift b/en/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift new file mode 100644 index 000000000..18eb82bfe --- /dev/null +++ b/en/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift @@ -0,0 +1,40 @@ +/** + * File: worst_best_time_complexity.swift + * Created Time: 2022-12-26 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +func randomNumbers(n: Int) -> [Int] { + // Generate array nums = { 1, 2, 3, ..., n } + var nums = Array(1 ... n) + // Randomly shuffle array elements + nums.shuffle() + return nums +} + +/* Find the index of number 1 in array nums */ +func findOne(nums: [Int]) -> Int { + for i in nums.indices { + // When element 1 is at the head of the array, best time complexity O(1) is achieved + // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + if nums[i] == 1 { + return i + } + } + return -1 +} + +@main +enum WorstBestTimeComplexity { + /* Driver Code */ + static func main() { + for _ in 0 ..< 10 { + let n = 100 + let nums = randomNumbers(n: n) + let index = findOne(nums: nums) + print("Array [ 1, 2, ..., n ] after shuffling = \(nums)") + print("Index of number 1 is \(index)") + } + } +} diff --git a/en/codes/swift/chapter_divide_and_conquer/binary_search_recur.swift b/en/codes/swift/chapter_divide_and_conquer/binary_search_recur.swift new file mode 100644 index 000000000..591f8db05 --- /dev/null +++ b/en/codes/swift/chapter_divide_and_conquer/binary_search_recur.swift @@ -0,0 +1,44 @@ +/** + * File: binary_search_recur.swift + * Created Time: 2023-09-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Binary search: problem f(i, j) */ +func dfs(nums: [Int], target: Int, i: Int, j: Int) -> Int { + // If the interval is empty, it means there is no target element, return -1 + if i > j { + return -1 + } + // Calculate the midpoint index m + let m = (i + j) / 2 + if nums[m] < target { + // Recursion subproblem f(m+1, j) + return dfs(nums: nums, target: target, i: m + 1, j: j) + } else if nums[m] > target { + // Recursion subproblem f(i, m-1) + return dfs(nums: nums, target: target, i: i, j: m - 1) + } else { + // Found the target element, return its index + return m + } +} + +/* Binary search */ +func binarySearch(nums: [Int], target: Int) -> Int { + // Solve the problem f(0, n-1) + dfs(nums: nums, target: target, i: nums.startIndex, j: nums.endIndex - 1) +} + +@main +enum BinarySearchRecur { + /* Driver Code */ + static func main() { + let target = 6 + let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] + + // Binary search (closed interval on both sides) + let index = binarySearch(nums: nums, target: target) + print("Index of target element 6 = \(index)") + } +} diff --git a/en/codes/swift/chapter_divide_and_conquer/build_tree.swift b/en/codes/swift/chapter_divide_and_conquer/build_tree.swift new file mode 100644 index 000000000..9f22e3890 --- /dev/null +++ b/en/codes/swift/chapter_divide_and_conquer/build_tree.swift @@ -0,0 +1,47 @@ +/** + * File: build_tree.swift + * Created Time: 2023-09-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Build binary tree: divide and conquer */ +func dfs(preorder: [Int], inorderMap: [Int: Int], i: Int, l: Int, r: Int) -> TreeNode? { + // Terminate when the subtree interval is empty + if r - l < 0 { + return nil + } + // Initialize the root node + let root = TreeNode(x: preorder[i]) + // Query m to divide the left and right subtrees + let m = inorderMap[preorder[i]]! + // Subproblem: build the left subtree + root.left = dfs(preorder: preorder, inorderMap: inorderMap, i: i + 1, l: l, r: m - 1) + // Subproblem: build the right subtree + root.right = dfs(preorder: preorder, inorderMap: inorderMap, i: i + 1 + m - l, l: m + 1, r: r) + // Return the root node + return root +} + +/* Build binary tree */ +func buildTree(preorder: [Int], inorder: [Int]) -> TreeNode? { + // Initialize hash map, storing the mapping from inorder elements to indices + let inorderMap = inorder.enumerated().reduce(into: [:]) { $0[$1.element] = $1.offset } + return dfs(preorder: preorder, inorderMap: inorderMap, i: inorder.startIndex, l: inorder.startIndex, r: inorder.endIndex - 1) +} + +@main +enum BuildTree { + /* Driver Code */ + static func main() { + let preorder = [3, 9, 2, 1, 7] + let inorder = [9, 3, 1, 2, 7] + print("Pre-order traversal = \(preorder)") + print("In-order traversal = \(inorder)") + + let root = buildTree(preorder: preorder, inorder: inorder) + print("The constructed binary tree is:") + PrintUtil.printTree(root: root) + } +} diff --git a/en/codes/swift/chapter_divide_and_conquer/hanota.swift b/en/codes/swift/chapter_divide_and_conquer/hanota.swift new file mode 100644 index 000000000..001b8c656 --- /dev/null +++ b/en/codes/swift/chapter_divide_and_conquer/hanota.swift @@ -0,0 +1,58 @@ +/** + * File: hanota.swift + * Created Time: 2023-09-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Move a disk */ +func move(src: inout [Int], tar: inout [Int]) { + // Take out a disk from the top of src + let pan = src.popLast()! + // Place the disk on top of tar + tar.append(pan) +} + +/* Solve the Tower of Hanoi problem f(i) */ +func dfs(i: Int, src: inout [Int], buf: inout [Int], tar: inout [Int]) { + // If there is only one disk left in src, move it directly to tar + if i == 1 { + move(src: &src, tar: &tar) + return + } + // Subproblem f(i-1): move the top i-1 disks from src to buf using tar + dfs(i: i - 1, src: &src, buf: &tar, tar: &buf) + // Subproblem f(1): move the remaining disk from src to tar + move(src: &src, tar: &tar) + // Subproblem f(i-1): move the top i-1 disks from buf to tar using src + dfs(i: i - 1, src: &buf, buf: &src, tar: &tar) +} + +/* Solve the Tower of Hanoi problem */ +func solveHanota(A: inout [Int], B: inout [Int], C: inout [Int]) { + let n = A.count + // The tail of the list is the top of the rod + // Move top n disks from src to C using B + dfs(i: n, src: &A, buf: &B, tar: &C) +} + +@main +enum Hanota { + /* Driver Code */ + static func main() { + // The tail of the list is the top of the rod + var A = [5, 4, 3, 2, 1] + var B: [Int] = [] + var C: [Int] = [] + print("In initial state:") + print("A = \(A)") + print("B = \(B)") + print("C = \(C)") + + solveHanota(A: &A, B: &B, C: &C) + + print("After disk movement is complete:") + print("A = \(A)") + print("B = \(B)") + print("C = \(C)") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_backtrack.swift b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_backtrack.swift new file mode 100644 index 000000000..1b8adf98a --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_backtrack.swift @@ -0,0 +1,44 @@ +/** + * File: climbing_stairs_backtrack.swift + * Created Time: 2023-07-15 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Backtracking */ +func backtrack(choices: [Int], state: Int, n: Int, res: inout [Int]) { + // When climbing to the n-th stair, add 1 to the solution count + if state == n { + res[0] += 1 + } + // Traverse all choices + for choice in choices { + // Pruning: not allowed to go beyond the n-th stair + if state + choice > n { + continue + } + // Attempt: make choice, update state + backtrack(choices: choices, state: state + choice, n: n, res: &res) + // Backtrack + } +} + +/* Climbing stairs: Backtracking */ +func climbingStairsBacktrack(n: Int) -> Int { + let choices = [1, 2] // Can choose to climb up 1 or 2 stairs + let state = 0 // Start climbing from the 0-th stair + var res: [Int] = [] + res.append(0) // Use res[0] to record the solution count + backtrack(choices: choices, state: state, n: n, res: &res) + return res[0] +} + +@main +enum ClimbingStairsBacktrack { + /* Driver Code */ + static func main() { + let n = 9 + + let res = climbingStairsBacktrack(n: n) + print("Climbing \(n) stairs has \(res) solutions") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_constraint_dp.swift b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_constraint_dp.swift new file mode 100644 index 000000000..d8c0c56a7 --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_constraint_dp.swift @@ -0,0 +1,36 @@ +/** + * File: climbing_stairs_constraint_dp.swift + * Created Time: 2023-07-15 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Climbing stairs with constraint: Dynamic programming */ +func climbingStairsConstraintDP(n: Int) -> Int { + if n == 1 || n == 2 { + return 1 + } + // Initialize dp table, used to store solutions to subproblems + var dp = Array(repeating: Array(repeating: 0, count: 3), count: n + 1) + // Initial state: preset the solution to the smallest subproblem + dp[1][1] = 1 + dp[1][2] = 0 + dp[2][1] = 0 + dp[2][2] = 1 + // State transition: gradually solve larger subproblems from smaller ones + for i in 3 ... n { + dp[i][1] = dp[i - 1][2] + dp[i][2] = dp[i - 2][1] + dp[i - 2][2] + } + return dp[n][1] + dp[n][2] +} + +@main +enum ClimbingStairsConstraintDP { + /* Driver Code */ + static func main() { + let n = 9 + + let res = climbingStairsConstraintDP(n: n) + print("Climbing \(n) stairs has \(res) solutions") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs.swift b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs.swift new file mode 100644 index 000000000..3eb993ec9 --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs.swift @@ -0,0 +1,32 @@ +/** + * File: climbing_stairs_dfs.swift + * Created Time: 2023-07-15 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Search */ +func dfs(i: Int) -> Int { + // Known dp[1] and dp[2], return them + if i == 1 || i == 2 { + return i + } + // dp[i] = dp[i-1] + dp[i-2] + let count = dfs(i: i - 1) + dfs(i: i - 2) + return count +} + +/* Climbing stairs: Search */ +func climbingStairsDFS(n: Int) -> Int { + dfs(i: n) +} + +@main +enum ClimbingStairsDFS { + /* Driver Code */ + static func main() { + let n = 9 + + let res = climbingStairsDFS(n: n) + print("Climbing \(n) stairs has \(res) solutions") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs_mem.swift b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs_mem.swift new file mode 100644 index 000000000..bf16ac5f7 --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dfs_mem.swift @@ -0,0 +1,40 @@ +/** + * File: climbing_stairs_dfs_mem.swift + * Created Time: 2023-07-15 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Memoization search */ +func dfs(i: Int, mem: inout [Int]) -> Int { + // Known dp[1] and dp[2], return them + if i == 1 || i == 2 { + return i + } + // If record dp[i] exists, return it directly + if mem[i] != -1 { + return mem[i] + } + // dp[i] = dp[i-1] + dp[i-2] + let count = dfs(i: i - 1, mem: &mem) + dfs(i: i - 2, mem: &mem) + // Record dp[i] + mem[i] = count + return count +} + +/* Climbing stairs: Memoization search */ +func climbingStairsDFSMem(n: Int) -> Int { + // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + var mem = Array(repeating: -1, count: n + 1) + return dfs(i: n, mem: &mem) +} + +@main +enum ClimbingStairsDFSMem { + /* Driver Code */ + static func main() { + let n = 9 + + let res = climbingStairsDFSMem(n: n) + print("Climbing \(n) stairs has \(res) solutions") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dp.swift b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dp.swift new file mode 100644 index 000000000..2161e2b3b --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/climbing_stairs_dp.swift @@ -0,0 +1,49 @@ +/** + * File: climbing_stairs_dp.swift + * Created Time: 2023-07-15 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Climbing stairs: Dynamic programming */ +func climbingStairsDP(n: Int) -> Int { + if n == 1 || n == 2 { + return n + } + // Initialize dp table, used to store solutions to subproblems + var dp = Array(repeating: 0, count: n + 1) + // Initial state: preset the solution to the smallest subproblem + dp[1] = 1 + dp[2] = 2 + // State transition: gradually solve larger subproblems from smaller ones + for i in 3 ... n { + dp[i] = dp[i - 1] + dp[i - 2] + } + return dp[n] +} + +/* Climbing stairs: Space-optimized dynamic programming */ +func climbingStairsDPComp(n: Int) -> Int { + if n == 1 || n == 2 { + return n + } + var a = 1 + var b = 2 + for _ in 3 ... n { + (a, b) = (b, a + b) + } + return b +} + +@main +enum ClimbingStairsDP { + /* Driver Code */ + static func main() { + let n = 9 + + var res = climbingStairsDP(n: n) + print("Climbing \(n) stairs has \(res) solutions") + + res = climbingStairsDPComp(n: n) + print("Climbing \(n) stairs has \(res) solutions") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/coin_change.swift b/en/codes/swift/chapter_dynamic_programming/coin_change.swift new file mode 100644 index 000000000..9220a6abd --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/coin_change.swift @@ -0,0 +1,69 @@ +/** + * File: coin_change.swift + * Created Time: 2023-07-15 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Coin change: Dynamic programming */ +func coinChangeDP(coins: [Int], amt: Int) -> Int { + let n = coins.count + let MAX = amt + 1 + // Initialize dp table + var dp = Array(repeating: Array(repeating: 0, count: amt + 1), count: n + 1) + // State transition: first row and first column + for a in 1 ... amt { + dp[0][a] = MAX + } + // State transition: rest of the rows and columns + for i in 1 ... n { + for a in 1 ... amt { + if coins[i - 1] > a { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a] + } else { + // The smaller value between not selecting and selecting coin i + dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1) + } + } + } + return dp[n][amt] != MAX ? dp[n][amt] : -1 +} + +/* Coin change: Space-optimized dynamic programming */ +func coinChangeDPComp(coins: [Int], amt: Int) -> Int { + let n = coins.count + let MAX = amt + 1 + // Initialize dp table + var dp = Array(repeating: MAX, count: amt + 1) + dp[0] = 0 + // State transition + for i in 1 ... n { + for a in 1 ... amt { + if coins[i - 1] > a { + // If exceeds target amount, don't select coin i + dp[a] = dp[a] + } else { + // The smaller value between not selecting and selecting coin i + dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1) + } + } + } + return dp[amt] != MAX ? dp[amt] : -1 +} + +@main +enum CoinChange { + /* Driver Code */ + static func main() { + let coins = [1, 2, 5] + let amt = 4 + + // Dynamic programming + var res = coinChangeDP(coins: coins, amt: amt) + print("Minimum coins needed to make target amount is \(res)") + + // Space-optimized dynamic programming + res = coinChangeDPComp(coins: coins, amt: amt) + print("Minimum coins needed to make target amount is \(res)") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/coin_change_ii.swift b/en/codes/swift/chapter_dynamic_programming/coin_change_ii.swift new file mode 100644 index 000000000..bd962268f --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/coin_change_ii.swift @@ -0,0 +1,67 @@ +/** + * File: coin_change_ii.swift + * Created Time: 2023-07-16 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Coin change II: Dynamic programming */ +func coinChangeIIDP(coins: [Int], amt: Int) -> Int { + let n = coins.count + // Initialize dp table + var dp = Array(repeating: Array(repeating: 0, count: amt + 1), count: n + 1) + // Initialize first column + for i in 0 ... n { + dp[i][0] = 1 + } + // State transition + for i in 1 ... n { + for a in 1 ... amt { + if coins[i - 1] > a { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a] + } else { + // Sum of the two options: not selecting and selecting coin i + dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]] + } + } + } + return dp[n][amt] +} + +/* Coin change II: Space-optimized dynamic programming */ +func coinChangeIIDPComp(coins: [Int], amt: Int) -> Int { + let n = coins.count + // Initialize dp table + var dp = Array(repeating: 0, count: amt + 1) + dp[0] = 1 + // State transition + for i in 1 ... n { + for a in 1 ... amt { + if coins[i - 1] > a { + // If exceeds target amount, don't select coin i + dp[a] = dp[a] + } else { + // Sum of the two options: not selecting and selecting coin i + dp[a] = dp[a] + dp[a - coins[i - 1]] + } + } + } + return dp[amt] +} + +@main +enum CoinChangeII { + /* Driver Code */ + static func main() { + let coins = [1, 2, 5] + let amt = 5 + + // Dynamic programming + var res = coinChangeIIDP(coins: coins, amt: amt) + print("Number of coin combinations to make target amount is \(res)") + + // Space-optimized dynamic programming + res = coinChangeIIDPComp(coins: coins, amt: amt) + print("Number of coin combinations to make target amount is \(res)") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/edit_distance.swift b/en/codes/swift/chapter_dynamic_programming/edit_distance.swift new file mode 100644 index 000000000..2b1fc4cd4 --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/edit_distance.swift @@ -0,0 +1,147 @@ +/** + * File: edit_distance.swift + * Created Time: 2023-07-16 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Edit distance: Brute-force search */ +func editDistanceDFS(s: String, t: String, i: Int, j: Int) -> Int { + // If both s and t are empty, return 0 + if i == 0, j == 0 { + return 0 + } + // If s is empty, return length of t + if i == 0 { + return j + } + // If t is empty, return length of s + if j == 0 { + return i + } + // If two characters are equal, skip both characters + if s.utf8CString[i - 1] == t.utf8CString[j - 1] { + return editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1) + } + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + let insert = editDistanceDFS(s: s, t: t, i: i, j: j - 1) + let delete = editDistanceDFS(s: s, t: t, i: i - 1, j: j) + let replace = editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1) + // Return minimum edit steps + return min(min(insert, delete), replace) + 1 +} + +/* Edit distance: Memoization search */ +func editDistanceDFSMem(s: String, t: String, mem: inout [[Int]], i: Int, j: Int) -> Int { + // If both s and t are empty, return 0 + if i == 0, j == 0 { + return 0 + } + // If s is empty, return length of t + if i == 0 { + return j + } + // If t is empty, return length of s + if j == 0 { + return i + } + // If there's a record, return it directly + if mem[i][j] != -1 { + return mem[i][j] + } + // If two characters are equal, skip both characters + if s.utf8CString[i - 1] == t.utf8CString[j - 1] { + return editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1) + } + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + let insert = editDistanceDFS(s: s, t: t, i: i, j: j - 1) + let delete = editDistanceDFS(s: s, t: t, i: i - 1, j: j) + let replace = editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1) + // Record and return minimum edit steps + mem[i][j] = min(min(insert, delete), replace) + 1 + return mem[i][j] +} + +/* Edit distance: Dynamic programming */ +func editDistanceDP(s: String, t: String) -> Int { + let n = s.utf8CString.count + let m = t.utf8CString.count + var dp = Array(repeating: Array(repeating: 0, count: m + 1), count: n + 1) + // State transition: first row and first column + for i in 1 ... n { + dp[i][0] = i + } + for j in 1 ... m { + dp[0][j] = j + } + // State transition: rest of the rows and columns + for i in 1 ... n { + for j in 1 ... m { + if s.utf8CString[i - 1] == t.utf8CString[j - 1] { + // If two characters are equal, skip both characters + dp[i][j] = dp[i - 1][j - 1] + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1 + } + } + } + return dp[n][m] +} + +/* Edit distance: Space-optimized dynamic programming */ +func editDistanceDPComp(s: String, t: String) -> Int { + let n = s.utf8CString.count + let m = t.utf8CString.count + var dp = Array(repeating: 0, count: m + 1) + // State transition: first row + for j in 1 ... m { + dp[j] = j + } + // State transition: rest of the rows + for i in 1 ... n { + // State transition: first column + var leftup = dp[0] // Temporarily store dp[i-1, j-1] + dp[0] = i + // State transition: rest of the columns + for j in 1 ... m { + let temp = dp[j] + if s.utf8CString[i - 1] == t.utf8CString[j - 1] { + // If two characters are equal, skip both characters + dp[j] = leftup + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1 + } + leftup = temp // Update for next round's dp[i-1, j-1] + } + } + return dp[m] +} + +@main +enum EditDistance { + /* Driver Code */ + static func main() { + let s = "bag" + let t = "pack" + let n = s.utf8CString.count + let m = t.utf8CString.count + + // Brute-force search + var res = editDistanceDFS(s: s, t: t, i: n, j: m) + print("Changing \(s) to \(t) requires minimum \(res) edits") + + // Memoization search + var mem = Array(repeating: Array(repeating: -1, count: m + 1), count: n + 1) + res = editDistanceDFSMem(s: s, t: t, mem: &mem, i: n, j: m) + print("Changing \(s) to \(t) requires minimum \(res) edits") + + // Dynamic programming + res = editDistanceDP(s: s, t: t) + print("Changing \(s) to \(t) requires minimum \(res) edits") + + // Space-optimized dynamic programming + res = editDistanceDPComp(s: s, t: t) + print("Changing \(s) to \(t) requires minimum \(res) edits") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/knapsack.swift b/en/codes/swift/chapter_dynamic_programming/knapsack.swift new file mode 100644 index 000000000..a7b1908d7 --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/knapsack.swift @@ -0,0 +1,110 @@ +/** + * File: knapsack.swift + * Created Time: 2023-07-15 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* 0-1 knapsack: Brute-force search */ +func knapsackDFS(wgt: [Int], val: [Int], i: Int, c: Int) -> Int { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if i == 0 || c == 0 { + return 0 + } + // If exceeds knapsack capacity, can only choose not to put it in + if wgt[i - 1] > c { + return knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c) + } + // Calculate the maximum value of not putting in and putting in item i + let no = knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c) + let yes = knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c - wgt[i - 1]) + val[i - 1] + // Return the larger value of the two options + return max(no, yes) +} + +/* 0-1 knapsack: Memoization search */ +func knapsackDFSMem(wgt: [Int], val: [Int], mem: inout [[Int]], i: Int, c: Int) -> Int { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if i == 0 || c == 0 { + return 0 + } + // If there's a record, return it directly + if mem[i][c] != -1 { + return mem[i][c] + } + // If exceeds knapsack capacity, can only choose not to put it in + if wgt[i - 1] > c { + return knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: i - 1, c: c) + } + // Calculate the maximum value of not putting in and putting in item i + let no = knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: i - 1, c: c) + let yes = knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: i - 1, c: c - wgt[i - 1]) + val[i - 1] + // Record and return the larger value of the two options + mem[i][c] = max(no, yes) + return mem[i][c] +} + +/* 0-1 knapsack: Dynamic programming */ +func knapsackDP(wgt: [Int], val: [Int], cap: Int) -> Int { + let n = wgt.count + // Initialize dp table + var dp = Array(repeating: Array(repeating: 0, count: cap + 1), count: n + 1) + // State transition + for i in 1 ... n { + for c in 1 ... cap { + if wgt[i - 1] > c { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c] + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]) + } + } + } + return dp[n][cap] +} + +/* 0-1 knapsack: Space-optimized dynamic programming */ +func knapsackDPComp(wgt: [Int], val: [Int], cap: Int) -> Int { + let n = wgt.count + // Initialize dp table + var dp = Array(repeating: 0, count: cap + 1) + // State transition + for i in 1 ... n { + // Traverse in reverse order + for c in (1 ... cap).reversed() { + if wgt[i - 1] <= c { + // The larger value between not selecting and selecting item i + dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) + } + } + } + return dp[cap] +} + +@main +enum Knapsack { + /* Driver Code */ + static func main() { + let wgt = [10, 20, 30, 40, 50] + let val = [50, 120, 150, 210, 240] + let cap = 50 + let n = wgt.count + + // Brute-force search + var res = knapsackDFS(wgt: wgt, val: val, i: n, c: cap) + print("Maximum item value not exceeding knapsack capacity is \(res)") + + // Memoization search + var mem = Array(repeating: Array(repeating: -1, count: cap + 1), count: n + 1) + res = knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: n, c: cap) + print("Maximum item value not exceeding knapsack capacity is \(res)") + + // Dynamic programming + res = knapsackDP(wgt: wgt, val: val, cap: cap) + print("Maximum item value not exceeding knapsack capacity is \(res)") + + // Space-optimized dynamic programming + res = knapsackDPComp(wgt: wgt, val: val, cap: cap) + print("Maximum item value not exceeding knapsack capacity is \(res)") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/min_cost_climbing_stairs_dp.swift b/en/codes/swift/chapter_dynamic_programming/min_cost_climbing_stairs_dp.swift new file mode 100644 index 000000000..d6a7df175 --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/min_cost_climbing_stairs_dp.swift @@ -0,0 +1,51 @@ +/** + * File: min_cost_climbing_stairs_dp.swift + * Created Time: 2023-07-15 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Minimum cost climbing stairs: Dynamic programming */ +func minCostClimbingStairsDP(cost: [Int]) -> Int { + let n = cost.count - 1 + if n == 1 || n == 2 { + return cost[n] + } + // Initialize dp table, used to store solutions to subproblems + var dp = Array(repeating: 0, count: n + 1) + // Initial state: preset the solution to the smallest subproblem + dp[1] = cost[1] + dp[2] = cost[2] + // State transition: gradually solve larger subproblems from smaller ones + for i in 3 ... n { + dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] + } + return dp[n] +} + +/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +func minCostClimbingStairsDPComp(cost: [Int]) -> Int { + let n = cost.count - 1 + if n == 1 || n == 2 { + return cost[n] + } + var (a, b) = (cost[1], cost[2]) + for i in 3 ... n { + (a, b) = (b, min(a, b) + cost[i]) + } + return b +} + +@main +enum MinCostClimbingStairsDP { + /* Driver Code */ + static func main() { + let cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1] + print("Input stair cost list is \(cost)") + + var res = minCostClimbingStairsDP(cost: cost) + print("Minimum cost to climb stairs is \(res)") + + res = minCostClimbingStairsDPComp(cost: cost) + print("Minimum cost to climb stairs is \(res)") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/min_path_sum.swift b/en/codes/swift/chapter_dynamic_programming/min_path_sum.swift new file mode 100644 index 000000000..1f1c3b3f3 --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/min_path_sum.swift @@ -0,0 +1,123 @@ +/** + * File: min_path_sum.swift + * Created Time: 2023-07-15 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Minimum path sum: Brute-force search */ +func minPathSumDFS(grid: [[Int]], i: Int, j: Int) -> Int { + // If it's the top-left cell, terminate the search + if i == 0, j == 0 { + return grid[0][0] + } + // If row or column index is out of bounds, return +∞ cost + if i < 0 || j < 0 { + return .max + } + // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + let up = minPathSumDFS(grid: grid, i: i - 1, j: j) + let left = minPathSumDFS(grid: grid, i: i, j: j - 1) + // Return the minimum path cost from top-left to (i, j) + return min(left, up) + grid[i][j] +} + +/* Minimum path sum: Memoization search */ +func minPathSumDFSMem(grid: [[Int]], mem: inout [[Int]], i: Int, j: Int) -> Int { + // If it's the top-left cell, terminate the search + if i == 0, j == 0 { + return grid[0][0] + } + // If row or column index is out of bounds, return +∞ cost + if i < 0 || j < 0 { + return .max + } + // If there's a record, return it directly + if mem[i][j] != -1 { + return mem[i][j] + } + // Minimum path cost for left and upper cells + let up = minPathSumDFSMem(grid: grid, mem: &mem, i: i - 1, j: j) + let left = minPathSumDFSMem(grid: grid, mem: &mem, i: i, j: j - 1) + // Record and return the minimum path cost from top-left to (i, j) + mem[i][j] = min(left, up) + grid[i][j] + return mem[i][j] +} + +/* Minimum path sum: Dynamic programming */ +func minPathSumDP(grid: [[Int]]) -> Int { + let n = grid.count + let m = grid[0].count + // Initialize dp table + var dp = Array(repeating: Array(repeating: 0, count: m), count: n) + dp[0][0] = grid[0][0] + // State transition: first row + for j in 1 ..< m { + dp[0][j] = dp[0][j - 1] + grid[0][j] + } + // State transition: first column + for i in 1 ..< n { + dp[i][0] = dp[i - 1][0] + grid[i][0] + } + // State transition: rest of the rows and columns + for i in 1 ..< n { + for j in 1 ..< m { + dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j] + } + } + return dp[n - 1][m - 1] +} + +/* Minimum path sum: Space-optimized dynamic programming */ +func minPathSumDPComp(grid: [[Int]]) -> Int { + let n = grid.count + let m = grid[0].count + // Initialize dp table + var dp = Array(repeating: 0, count: m) + // State transition: first row + dp[0] = grid[0][0] + for j in 1 ..< m { + dp[j] = dp[j - 1] + grid[0][j] + } + // State transition: rest of the rows + for i in 1 ..< n { + // State transition: first column + dp[0] = dp[0] + grid[i][0] + // State transition: rest of the columns + for j in 1 ..< m { + dp[j] = min(dp[j - 1], dp[j]) + grid[i][j] + } + } + return dp[m - 1] +} + +@main +enum MinPathSum { + /* Driver Code */ + static func main() { + let grid = [ + [1, 3, 1, 5], + [2, 2, 4, 2], + [5, 3, 2, 1], + [4, 3, 5, 2], + ] + let n = grid.count + let m = grid[0].count + + // Brute-force search + var res = minPathSumDFS(grid: grid, i: n - 1, j: m - 1) + print("Minimum path sum from top-left to bottom-right is \(res)") + + // Memoization search + var mem = Array(repeating: Array(repeating: -1, count: m), count: n) + res = minPathSumDFSMem(grid: grid, mem: &mem, i: n - 1, j: m - 1) + print("Minimum path sum from top-left to bottom-right is \(res)") + + // Dynamic programming + res = minPathSumDP(grid: grid) + print("Minimum path sum from top-left to bottom-right is \(res)") + + // Space-optimized dynamic programming + res = minPathSumDPComp(grid: grid) + print("Minimum path sum from top-left to bottom-right is \(res)") + } +} diff --git a/en/codes/swift/chapter_dynamic_programming/unbounded_knapsack.swift b/en/codes/swift/chapter_dynamic_programming/unbounded_knapsack.swift new file mode 100644 index 000000000..a5049bfd3 --- /dev/null +++ b/en/codes/swift/chapter_dynamic_programming/unbounded_knapsack.swift @@ -0,0 +1,63 @@ +/** + * File: unbounded_knapsack.swift + * Created Time: 2023-07-15 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Unbounded knapsack: Dynamic programming */ +func unboundedKnapsackDP(wgt: [Int], val: [Int], cap: Int) -> Int { + let n = wgt.count + // Initialize dp table + var dp = Array(repeating: Array(repeating: 0, count: cap + 1), count: n + 1) + // State transition + for i in 1 ... n { + for c in 1 ... cap { + if wgt[i - 1] > c { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c] + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]) + } + } + } + return dp[n][cap] +} + +/* Unbounded knapsack: Space-optimized dynamic programming */ +func unboundedKnapsackDPComp(wgt: [Int], val: [Int], cap: Int) -> Int { + let n = wgt.count + // Initialize dp table + var dp = Array(repeating: 0, count: cap + 1) + // State transition + for i in 1 ... n { + for c in 1 ... cap { + if wgt[i - 1] > c { + // If exceeds knapsack capacity, don't select item i + dp[c] = dp[c] + } else { + // The larger value between not selecting and selecting item i + dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) + } + } + } + return dp[cap] +} + +@main +enum UnboundedKnapsack { + /* Driver Code */ + static func main() { + let wgt = [1, 2, 3] + let val = [5, 11, 15] + let cap = 4 + + // Dynamic programming + var res = unboundedKnapsackDP(wgt: wgt, val: val, cap: cap) + print("Maximum item value not exceeding knapsack capacity is \(res)") + + // Space-optimized dynamic programming + res = unboundedKnapsackDPComp(wgt: wgt, val: val, cap: cap) + print("Maximum item value not exceeding knapsack capacity is \(res)") + } +} diff --git a/en/codes/swift/chapter_graph/graph_adjacency_list.swift b/en/codes/swift/chapter_graph/graph_adjacency_list.swift new file mode 100644 index 000000000..463609865 --- /dev/null +++ b/en/codes/swift/chapter_graph/graph_adjacency_list.swift @@ -0,0 +1,121 @@ +/** + * File: graph_adjacency_list.swift + * Created Time: 2023-02-01 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Undirected graph class based on adjacency list */ +public class GraphAdjList { + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + public private(set) var adjList: [Vertex: [Vertex]] + + /* Constructor */ + public init(edges: [[Vertex]]) { + adjList = [:] + // Add all vertices and edges + for edge in edges { + addVertex(vet: edge[0]) + addVertex(vet: edge[1]) + addEdge(vet1: edge[0], vet2: edge[1]) + } + } + + /* Get the number of vertices */ + public func size() -> Int { + adjList.count + } + + /* Add edge */ + public func addEdge(vet1: Vertex, vet2: Vertex) { + if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { + fatalError("Invalid parameter") + } + // Add edge vet1 - vet2 + adjList[vet1]?.append(vet2) + adjList[vet2]?.append(vet1) + } + + /* Remove edge */ + public func removeEdge(vet1: Vertex, vet2: Vertex) { + if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { + fatalError("Invalid parameter") + } + // Remove edge vet1 - vet2 + adjList[vet1]?.removeAll { $0 == vet2 } + adjList[vet2]?.removeAll { $0 == vet1 } + } + + /* Add vertex */ + public func addVertex(vet: Vertex) { + if adjList[vet] != nil { + return + } + // Add a new linked list in the adjacency list + adjList[vet] = [] + } + + /* Remove vertex */ + public func removeVertex(vet: Vertex) { + if adjList[vet] == nil { + fatalError("Invalid parameter") + } + // Remove the linked list corresponding to vertex vet in the adjacency list + adjList.removeValue(forKey: vet) + // Traverse the linked lists of other vertices and remove all edges containing vet + for key in adjList.keys { + adjList[key]?.removeAll { $0 == vet } + } + } + + /* Print adjacency list */ + public func print() { + Swift.print("Adjacency list =") + for (vertex, list) in adjList { + let list = list.map { $0.val } + Swift.print("\(vertex.val): \(list),") + } + } +} + +#if !TARGET + +@main +enum GraphAdjacencyList { + /* Driver Code */ + static func main() { + /* Add edge */ + let v = Vertex.valsToVets(vals: [1, 3, 2, 5, 4]) + let edges = [[v[0], v[1]], [v[0], v[3]], [v[1], v[2]], [v[2], v[3]], [v[2], v[4]], [v[3], v[4]]] + let graph = GraphAdjList(edges: edges) + print("\nAfter initialization, graph is") + graph.print() + + /* Add edge */ + // Vertices 1, 3 are v[0], v[1] + graph.addEdge(vet1: v[0], vet2: v[2]) + print("\nAfter adding edge 1-2, graph is") + graph.print() + + /* Remove edge */ + // Vertex 3 is v[1] + graph.removeEdge(vet1: v[0], vet2: v[1]) + print("\nAfter removing edge 1-3, graph is") + graph.print() + + /* Add vertex */ + let v5 = Vertex(val: 6) + graph.addVertex(vet: v5) + print("\nAfter adding vertex 6, graph is") + graph.print() + + /* Remove vertex */ + // Vertex 3 is v[1] + graph.removeVertex(vet: v[1]) + print("\nAfter removing vertex 3, graph is") + graph.print() + } +} + +#endif diff --git a/en/codes/swift/chapter_graph/graph_adjacency_list_target.swift b/en/codes/swift/chapter_graph/graph_adjacency_list_target.swift new file mode 100644 index 000000000..463609865 --- /dev/null +++ b/en/codes/swift/chapter_graph/graph_adjacency_list_target.swift @@ -0,0 +1,121 @@ +/** + * File: graph_adjacency_list.swift + * Created Time: 2023-02-01 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Undirected graph class based on adjacency list */ +public class GraphAdjList { + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + public private(set) var adjList: [Vertex: [Vertex]] + + /* Constructor */ + public init(edges: [[Vertex]]) { + adjList = [:] + // Add all vertices and edges + for edge in edges { + addVertex(vet: edge[0]) + addVertex(vet: edge[1]) + addEdge(vet1: edge[0], vet2: edge[1]) + } + } + + /* Get the number of vertices */ + public func size() -> Int { + adjList.count + } + + /* Add edge */ + public func addEdge(vet1: Vertex, vet2: Vertex) { + if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { + fatalError("Invalid parameter") + } + // Add edge vet1 - vet2 + adjList[vet1]?.append(vet2) + adjList[vet2]?.append(vet1) + } + + /* Remove edge */ + public func removeEdge(vet1: Vertex, vet2: Vertex) { + if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { + fatalError("Invalid parameter") + } + // Remove edge vet1 - vet2 + adjList[vet1]?.removeAll { $0 == vet2 } + adjList[vet2]?.removeAll { $0 == vet1 } + } + + /* Add vertex */ + public func addVertex(vet: Vertex) { + if adjList[vet] != nil { + return + } + // Add a new linked list in the adjacency list + adjList[vet] = [] + } + + /* Remove vertex */ + public func removeVertex(vet: Vertex) { + if adjList[vet] == nil { + fatalError("Invalid parameter") + } + // Remove the linked list corresponding to vertex vet in the adjacency list + adjList.removeValue(forKey: vet) + // Traverse the linked lists of other vertices and remove all edges containing vet + for key in adjList.keys { + adjList[key]?.removeAll { $0 == vet } + } + } + + /* Print adjacency list */ + public func print() { + Swift.print("Adjacency list =") + for (vertex, list) in adjList { + let list = list.map { $0.val } + Swift.print("\(vertex.val): \(list),") + } + } +} + +#if !TARGET + +@main +enum GraphAdjacencyList { + /* Driver Code */ + static func main() { + /* Add edge */ + let v = Vertex.valsToVets(vals: [1, 3, 2, 5, 4]) + let edges = [[v[0], v[1]], [v[0], v[3]], [v[1], v[2]], [v[2], v[3]], [v[2], v[4]], [v[3], v[4]]] + let graph = GraphAdjList(edges: edges) + print("\nAfter initialization, graph is") + graph.print() + + /* Add edge */ + // Vertices 1, 3 are v[0], v[1] + graph.addEdge(vet1: v[0], vet2: v[2]) + print("\nAfter adding edge 1-2, graph is") + graph.print() + + /* Remove edge */ + // Vertex 3 is v[1] + graph.removeEdge(vet1: v[0], vet2: v[1]) + print("\nAfter removing edge 1-3, graph is") + graph.print() + + /* Add vertex */ + let v5 = Vertex(val: 6) + graph.addVertex(vet: v5) + print("\nAfter adding vertex 6, graph is") + graph.print() + + /* Remove vertex */ + // Vertex 3 is v[1] + graph.removeVertex(vet: v[1]) + print("\nAfter removing vertex 3, graph is") + graph.print() + } +} + +#endif diff --git a/en/codes/swift/chapter_graph/graph_adjacency_matrix.swift b/en/codes/swift/chapter_graph/graph_adjacency_matrix.swift new file mode 100644 index 000000000..a199d8120 --- /dev/null +++ b/en/codes/swift/chapter_graph/graph_adjacency_matrix.swift @@ -0,0 +1,130 @@ +/** + * File: graph_adjacency_matrix.swift + * Created Time: 2023-02-01 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Undirected graph class based on adjacency matrix */ +class GraphAdjMat { + private var vertices: [Int] // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + private var adjMat: [[Int]] // Adjacency matrix, where the row and column indices correspond to the "vertex index" + + /* Constructor */ + init(vertices: [Int], edges: [[Int]]) { + self.vertices = [] + adjMat = [] + // Add vertex + for val in vertices { + addVertex(val: val) + } + // Add edge + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + for e in edges { + addEdge(i: e[0], j: e[1]) + } + } + + /* Get the number of vertices */ + func size() -> Int { + vertices.count + } + + /* Add vertex */ + func addVertex(val: Int) { + let n = size() + // Add the value of the new vertex to the vertex list + vertices.append(val) + // Add a row to the adjacency matrix + let newRow = Array(repeating: 0, count: n) + adjMat.append(newRow) + // Add a column to the adjacency matrix + for i in adjMat.indices { + adjMat[i].append(0) + } + } + + /* Remove vertex */ + func removeVertex(index: Int) { + if index >= size() { + fatalError("Out of bounds") + } + // Remove the vertex at index from the vertex list + vertices.remove(at: index) + // Remove the row at index from the adjacency matrix + adjMat.remove(at: index) + // Remove the column at index from the adjacency matrix + for i in adjMat.indices { + adjMat[i].remove(at: index) + } + } + + /* Add edge */ + // Parameters i, j correspond to the vertices element indices + func addEdge(i: Int, j: Int) { + // Handle index out of bounds and equality + if i < 0 || j < 0 || i >= size() || j >= size() || i == j { + fatalError("Out of bounds") + } + // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i) + adjMat[i][j] = 1 + adjMat[j][i] = 1 + } + + /* Remove edge */ + // Parameters i, j correspond to the vertices element indices + func removeEdge(i: Int, j: Int) { + // Handle index out of bounds and equality + if i < 0 || j < 0 || i >= size() || j >= size() || i == j { + fatalError("Out of bounds") + } + adjMat[i][j] = 0 + adjMat[j][i] = 0 + } + + /* Print adjacency matrix */ + func print() { + Swift.print("Vertex list = ", terminator: "") + Swift.print(vertices) + Swift.print("Adjacency matrix =") + PrintUtil.printMatrix(matrix: adjMat) + } +} + +@main +enum GraphAdjacencyMatrix { + /* Driver Code */ + static func main() { + /* Add edge */ + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + let vertices = [1, 3, 2, 5, 4] + let edges = [[0, 1], [1, 2], [2, 3], [0, 3], [2, 4], [3, 4]] + let graph = GraphAdjMat(vertices: vertices, edges: edges) + print("\nAfter initialization, graph is") + graph.print() + + /* Add edge */ + // Add vertex + graph.addEdge(i: 0, j: 2) + print("\nAfter adding edge 1-2, graph is") + graph.print() + + /* Remove edge */ + // Vertices 1, 3 have indices 0, 1 respectively + graph.removeEdge(i: 0, j: 1) + print("\nAfter removing edge 1-3, graph is") + graph.print() + + /* Add vertex */ + graph.addVertex(val: 6) + print("\nAfter adding vertex 6, graph is") + graph.print() + + /* Remove vertex */ + // Vertex 3 has index 1 + graph.removeVertex(index: 1) + print("\nAfter removing vertex 3, graph is") + graph.print() + } +} diff --git a/en/codes/swift/chapter_graph/graph_bfs.swift b/en/codes/swift/chapter_graph/graph_bfs.swift new file mode 100644 index 000000000..9bce713a3 --- /dev/null +++ b/en/codes/swift/chapter_graph/graph_bfs.swift @@ -0,0 +1,56 @@ +/** + * File: graph_bfs.swift + * Created Time: 2023-02-21 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import graph_adjacency_list_target +import utils + +/* Breadth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +func graphBFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] { + // Vertex traversal sequence + var res: [Vertex] = [] + // Hash set for recording vertices that have been visited + var visited: Set = [startVet] + // Queue used to implement BFS + var que: [Vertex] = [startVet] + // Starting from vertex vet, loop until all vertices are visited + while !que.isEmpty { + let vet = que.removeFirst() // Dequeue the front vertex + res.append(vet) // Record visited vertex + // Traverse all adjacent vertices of this vertex + for adjVet in graph.adjList[vet] ?? [] { + if visited.contains(adjVet) { + continue // Skip vertices that have been visited + } + que.append(adjVet) // Only enqueue unvisited vertices + visited.insert(adjVet) // Mark this vertex as visited + } + } + // Return vertex traversal sequence + return res +} + +@main +enum GraphBFS { + /* Driver Code */ + static func main() { + /* Add edge */ + let v = Vertex.valsToVets(vals: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + let edges = [ + [v[0], v[1]], [v[0], v[3]], [v[1], v[2]], [v[1], v[4]], + [v[2], v[5]], [v[3], v[4]], [v[3], v[6]], [v[4], v[5]], + [v[4], v[7]], [v[5], v[8]], [v[6], v[7]], [v[7], v[8]], + ] + let graph = GraphAdjList(edges: edges) + print("\nAfter initialization, graph is") + graph.print() + + /* Breadth-first traversal */ + let res = graphBFS(graph: graph, startVet: v[0]) + print("\nBreadth-first traversal (BFS) vertex sequence is") + print(Vertex.vetsToVals(vets: res)) + } +} diff --git a/en/codes/swift/chapter_graph/graph_dfs.swift b/en/codes/swift/chapter_graph/graph_dfs.swift new file mode 100644 index 000000000..359bce6c7 --- /dev/null +++ b/en/codes/swift/chapter_graph/graph_dfs.swift @@ -0,0 +1,54 @@ +/** + * File: graph_dfs.swift + * Created Time: 2023-02-21 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import graph_adjacency_list_target +import utils + +/* Depth-first traversal helper function */ +func dfs(graph: GraphAdjList, visited: inout Set, res: inout [Vertex], vet: Vertex) { + res.append(vet) // Record visited vertex + visited.insert(vet) // Mark this vertex as visited + // Traverse all adjacent vertices of this vertex + for adjVet in graph.adjList[vet] ?? [] { + if visited.contains(adjVet) { + continue // Skip vertices that have been visited + } + // Recursively visit adjacent vertices + dfs(graph: graph, visited: &visited, res: &res, vet: adjVet) + } +} + +/* Depth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +func graphDFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] { + // Vertex traversal sequence + var res: [Vertex] = [] + // Hash set for recording vertices that have been visited + var visited: Set = [] + dfs(graph: graph, visited: &visited, res: &res, vet: startVet) + return res +} + +@main +enum GraphDFS { + /* Driver Code */ + static func main() { + /* Add edge */ + let v = Vertex.valsToVets(vals: [0, 1, 2, 3, 4, 5, 6]) + let edges = [ + [v[0], v[1]], [v[0], v[3]], [v[1], v[2]], + [v[2], v[5]], [v[4], v[5]], [v[5], v[6]], + ] + let graph = GraphAdjList(edges: edges) + print("\nAfter initialization, graph is") + graph.print() + + /* Depth-first traversal */ + let res = graphDFS(graph: graph, startVet: v[0]) + print("\nDepth-first traversal (DFS) vertex sequence is") + print(Vertex.vetsToVals(vets: res)) + } +} diff --git a/en/codes/swift/chapter_greedy/coin_change_greedy.swift b/en/codes/swift/chapter_greedy/coin_change_greedy.swift new file mode 100644 index 000000000..f88d36af2 --- /dev/null +++ b/en/codes/swift/chapter_greedy/coin_change_greedy.swift @@ -0,0 +1,54 @@ +/** + * File: coin_change_greedy.swift + * Created Time: 2023-09-03 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Coin change: Greedy algorithm */ +func coinChangeGreedy(coins: [Int], amt: Int) -> Int { + // Assume coins list is sorted + var i = coins.count - 1 + var count = 0 + var amt = amt + // Loop to make greedy choices until no remaining amount + while amt > 0 { + // Find the coin that is less than and closest to the remaining amount + while i > 0 && coins[i] > amt { + i -= 1 + } + // Choose coins[i] + amt -= coins[i] + count += 1 + } + // If no feasible solution is found, return -1 + return amt == 0 ? count : -1 +} + +@main +enum CoinChangeGreedy { + /* Driver Code */ + static func main() { + // Greedy algorithm: Can guarantee finding the global optimal solution + var coins = [1, 5, 10, 20, 50, 100] + var amt = 186 + var res = coinChangeGreedy(coins: coins, amt: amt) + print("\ncoins = \(coins), amount = \(amt)") + print("Minimum coins needed to make \(amt) is \(res)") + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = [1, 20, 50] + amt = 60 + res = coinChangeGreedy(coins: coins, amt: amt) + print("\ncoins = \(coins), amount = \(amt)") + print("Minimum coins needed to make \(amt) is \(res)") + print("Actually the minimum number needed is 3, i.e., 20 + 20 + 20") + + // Greedy algorithm: Cannot guarantee finding the global optimal solution + coins = [1, 49, 50] + amt = 98 + res = coinChangeGreedy(coins: coins, amt: amt) + print("\ncoins = \(coins), amount = \(amt)") + print("Minimum coins needed to make \(amt) is \(res)") + print("Actually the minimum number needed is 2, i.e., 49 + 49") + } +} diff --git a/en/codes/swift/chapter_greedy/fractional_knapsack.swift b/en/codes/swift/chapter_greedy/fractional_knapsack.swift new file mode 100644 index 000000000..8defc2985 --- /dev/null +++ b/en/codes/swift/chapter_greedy/fractional_knapsack.swift @@ -0,0 +1,57 @@ +/** + * File: fractional_knapsack.swift + * Created Time: 2023-09-03 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Item */ +class Item { + var w: Int // Item weight + var v: Int // Item value + + init(w: Int, v: Int) { + self.w = w + self.v = v + } +} + +/* Fractional knapsack: Greedy algorithm */ +func fractionalKnapsack(wgt: [Int], val: [Int], cap: Int) -> Double { + // Create item list with two attributes: weight, value + var items = zip(wgt, val).map { Item(w: $0, v: $1) } + // Sort by unit value item.v / item.w from high to low + items.sort { -(Double($0.v) / Double($0.w)) < -(Double($1.v) / Double($1.w)) } + // Loop for greedy selection + var res = 0.0 + var cap = cap + for item in items { + if item.w <= cap { + // If remaining capacity is sufficient, put the entire current item into the knapsack + res += Double(item.v) + cap -= item.w + } else { + // If remaining capacity is insufficient, put part of the current item into the knapsack + res += Double(item.v) / Double(item.w) * Double(cap) + // No remaining capacity, so break out of the loop + break + } + } + return res +} + +@main +enum FractionalKnapsack { + /* Driver Code */ + static func main() { + // Item weight + let wgt = [10, 20, 30, 40, 50] + // Item value + let val = [50, 120, 150, 210, 240] + // Knapsack capacity + let cap = 50 + + // Greedy algorithm + let res = fractionalKnapsack(wgt: wgt, val: val, cap: cap) + print("Maximum item value not exceeding knapsack capacity is \(res)") + } +} diff --git a/en/codes/swift/chapter_greedy/max_capacity.swift b/en/codes/swift/chapter_greedy/max_capacity.swift new file mode 100644 index 000000000..e6fa56edd --- /dev/null +++ b/en/codes/swift/chapter_greedy/max_capacity.swift @@ -0,0 +1,38 @@ +/** + * File: max_capacity.swift + * Created Time: 2023-09-03 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Max capacity: Greedy algorithm */ +func maxCapacity(ht: [Int]) -> Int { + // Initialize i, j to be at both ends of the array + var i = ht.startIndex, j = ht.endIndex - 1 + // Initial max capacity is 0 + var res = 0 + // Loop for greedy selection until the two boards meet + while i < j { + // Update max capacity + let cap = min(ht[i], ht[j]) * (j - i) + res = max(res, cap) + // Move the shorter board inward + if ht[i] < ht[j] { + i += 1 + } else { + j -= 1 + } + } + return res +} + +@main +enum MaxCapacity { + /* Driver Code */ + static func main() { + let ht = [3, 8, 5, 2, 7, 7, 3, 4] + + // Greedy algorithm + let res = maxCapacity(ht: ht) + print("Maximum capacity is \(res)") + } +} diff --git a/en/codes/swift/chapter_greedy/max_product_cutting.swift b/en/codes/swift/chapter_greedy/max_product_cutting.swift new file mode 100644 index 000000000..f70f17c72 --- /dev/null +++ b/en/codes/swift/chapter_greedy/max_product_cutting.swift @@ -0,0 +1,43 @@ +/** + * File: max_product_cutting.swift + * Created Time: 2023-09-03 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import Foundation + +func pow(_ x: Int, _ y: Int) -> Int { + Int(Double(truncating: pow(Decimal(x), y) as NSDecimalNumber)) +} + +/* Max product cutting: Greedy algorithm */ +func maxProductCutting(n: Int) -> Int { + // When n <= 3, must cut out a 1 + if n <= 3 { + return 1 * (n - 1) + } + // Greedily cut out 3, a is the number of 3s, b is the remainder + let a = n / 3 + let b = n % 3 + if b == 1 { + // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + return pow(3, a - 1) * 2 * 2 + } + if b == 2 { + // When the remainder is 2, do nothing + return pow(3, a) * 2 + } + // When the remainder is 0, do nothing + return pow(3, a) +} + +@main +enum MaxProductCutting { + static func main() { + let n = 58 + + // Greedy algorithm + let res = maxProductCutting(n: n) + print("Maximum cutting product is \(res)") + } +} diff --git a/en/codes/swift/chapter_hashing/array_hash_map.swift b/en/codes/swift/chapter_hashing/array_hash_map.swift new file mode 100644 index 000000000..d70a742e1 --- /dev/null +++ b/en/codes/swift/chapter_hashing/array_hash_map.swift @@ -0,0 +1,110 @@ +/** + * File: array_hash_map.swift + * Created Time: 2023-01-16 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Hash table based on array implementation */ +class ArrayHashMap { + private var buckets: [Pair?] + + init() { + // Initialize array with 100 buckets + buckets = Array(repeating: nil, count: 100) + } + + /* Hash function */ + private func hashFunc(key: Int) -> Int { + let index = key % 100 + return index + } + + /* Query operation */ + func get(key: Int) -> String? { + let index = hashFunc(key: key) + let pair = buckets[index] + return pair?.val + } + + /* Add operation */ + func put(key: Int, val: String) { + let pair = Pair(key: key, val: val) + let index = hashFunc(key: key) + buckets[index] = pair + } + + /* Remove operation */ + func remove(key: Int) { + let index = hashFunc(key: key) + // Set to nil to delete + buckets[index] = nil + } + + /* Get all key-value pairs */ + func pairSet() -> [Pair] { + buckets.compactMap { $0 } + } + + /* Get all keys */ + func keySet() -> [Int] { + buckets.compactMap { $0?.key } + } + + /* Get all values */ + func valueSet() -> [String] { + buckets.compactMap { $0?.val } + } + + /* Print hash table */ + func print() { + for pair in pairSet() { + Swift.print("\(pair.key) -> \(pair.val)") + } + } +} + +@main +enum _ArrayHashMap { + /* Driver Code */ + static func main() { + /* Initialize hash table */ + let map = ArrayHashMap() + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.put(key: 12836, val: "Xiao Ha") + map.put(key: 15937, val: "Xiao Luo") + map.put(key: 16750, val: "Xiao Suan") + map.put(key: 13276, val: "Xiao Fa") + map.put(key: 10583, val: "Xiao Ya") + print("\nAfter adding is complete, hash table is\nKey -> Value") + map.print() + + /* Query operation */ + // Input key into hash table to get value + let name = map.get(key: 15937)! + print("\nInput student ID 15937, found name \(name)") + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(key: 10583) + print("\nAfter removing 10583, hash table is\nKey -> Value") + map.print() + + /* Traverse hash table */ + print("\nTraverse key-value pairs Key->Value") + for pair in map.pairSet() { + print("\(pair.key) -> \(pair.val)") + } + print("\nTraverse keys only Key") + for key in map.keySet() { + print(key) + } + print("\nTraverse values only Value") + for val in map.valueSet() { + print(val) + } + } +} diff --git a/en/codes/swift/chapter_hashing/built_in_hash.swift b/en/codes/swift/chapter_hashing/built_in_hash.swift new file mode 100644 index 000000000..e4946b345 --- /dev/null +++ b/en/codes/swift/chapter_hashing/built_in_hash.swift @@ -0,0 +1,37 @@ +/** + * File: built_in_hash.swift + * Created Time: 2023-07-01 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +@main +enum BuiltInHash { + /* Driver Code */ + static func main() { + let num = 3 + let hashNum = num.hashValue + print("Hash value of integer \(num) is \(hashNum)") + + let bol = true + let hashBol = bol.hashValue + print("Hash value of boolean \(bol) is \(hashBol)") + + let dec = 3.14159 + let hashDec = dec.hashValue + print("Hash value of decimal \(dec) is \(hashDec)") + + let str = "Hello Algo" + let hashStr = str.hashValue + print("Hash value of string \(str) is \(hashStr)") + + let arr = [AnyHashable(12836), AnyHashable("Xiao Ha")] + let hashTup = arr.hashValue + print("Hash value of array \(arr) is \(hashTup)") + + let obj = ListNode(x: 0) + let hashObj = obj.hashValue + print("Hash value of node object \(obj) is \(hashObj)") + } +} diff --git a/en/codes/swift/chapter_hashing/hash_map.swift b/en/codes/swift/chapter_hashing/hash_map.swift new file mode 100644 index 000000000..1e35ce043 --- /dev/null +++ b/en/codes/swift/chapter_hashing/hash_map.swift @@ -0,0 +1,51 @@ +/** + * File: hash_map.swift + * Created Time: 2023-01-16 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +@main +enum HashMap { + /* Driver Code */ + static func main() { + /* Initialize hash table */ + var map: [Int: String] = [:] + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map[12836] = "Xiao Ha" + map[15937] = "Xiao Luo" + map[16750] = "Xiao Suan" + map[13276] = "Xiao Fa" + map[10583] = "Xiao Ya" + print("\nAfter adding is complete, hash table is\nKey -> Value") + PrintUtil.printHashMap(map: map) + + /* Query operation */ + // Input key into hash table to get value + let name = map[15937]! + print("\nInput student ID 15937, found name \(name)") + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.removeValue(forKey: 10583) + print("\nAfter removing 10583, hash table is\nKey -> Value") + PrintUtil.printHashMap(map: map) + + /* Traverse hash table */ + print("\nTraverse key-value pairs Key->Value") + for (key, value) in map { + print("\(key) -> \(value)") + } + print("\nTraverse keys only Key") + for key in map.keys { + print(key) + } + print("\nTraverse values only Value") + for value in map.values { + print(value) + } + } +} diff --git a/en/codes/swift/chapter_hashing/hash_map_chaining.swift b/en/codes/swift/chapter_hashing/hash_map_chaining.swift new file mode 100644 index 000000000..ab6687f0b --- /dev/null +++ b/en/codes/swift/chapter_hashing/hash_map_chaining.swift @@ -0,0 +1,138 @@ +/** + * File: hash_map_chaining.swift + * Created Time: 2023-06-28 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Hash table with separate chaining */ +class HashMapChaining { + var size: Int // Number of key-value pairs + var capacity: Int // Hash table capacity + var loadThres: Double // Load factor threshold for triggering expansion + var extendRatio: Int // Expansion multiplier + var buckets: [[Pair]] // Bucket array + + /* Constructor */ + init() { + size = 0 + capacity = 4 + loadThres = 2.0 / 3.0 + extendRatio = 2 + buckets = Array(repeating: [], count: capacity) + } + + /* Hash function */ + func hashFunc(key: Int) -> Int { + key % capacity + } + + /* Load factor */ + func loadFactor() -> Double { + Double(size) / Double(capacity) + } + + /* Query operation */ + func get(key: Int) -> String? { + let index = hashFunc(key: key) + let bucket = buckets[index] + // Traverse bucket, if key is found, return corresponding val + for pair in bucket { + if pair.key == key { + return pair.val + } + } + // Return nil if key not found + return nil + } + + /* Add operation */ + func put(key: Int, val: String) { + // When load factor exceeds threshold, perform expansion + if loadFactor() > loadThres { + extend() + } + let index = hashFunc(key: key) + let bucket = buckets[index] + // Traverse bucket, if specified key is encountered, update corresponding val and return + for pair in bucket { + if pair.key == key { + pair.val = val + return + } + } + // If key does not exist, append key-value pair to the end + let pair = Pair(key: key, val: val) + buckets[index].append(pair) + size += 1 + } + + /* Remove operation */ + func remove(key: Int) { + let index = hashFunc(key: key) + let bucket = buckets[index] + // Traverse bucket and remove key-value pair from it + for (pairIndex, pair) in bucket.enumerated() { + if pair.key == key { + buckets[index].remove(at: pairIndex) + size -= 1 + break + } + } + } + + /* Expand hash table */ + func extend() { + // Temporarily store the original hash table + let bucketsTmp = buckets + // Initialize expanded new hash table + capacity *= extendRatio + buckets = Array(repeating: [], count: capacity) + size = 0 + // Move key-value pairs from original hash table to new hash table + for bucket in bucketsTmp { + for pair in bucket { + put(key: pair.key, val: pair.val) + } + } + } + + /* Print hash table */ + func print() { + for bucket in buckets { + let res = bucket.map { "\($0.key) -> \($0.val)" } + Swift.print(res) + } + } +} + +@main +enum _HashMapChaining { + /* Driver Code */ + static func main() { + /* Initialize hash table */ + let map = HashMapChaining() + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.put(key: 12836, val: "Xiao Ha") + map.put(key: 15937, val: "Xiao Luo") + map.put(key: 16750, val: "Xiao Suan") + map.put(key: 13276, val: "Xiao Fa") + map.put(key: 10583, val: "Xiao Ya") + print("\nAfter adding is complete, hash table is\nKey -> Value") + map.print() + + /* Query operation */ + // Input key into hash table to get value + let name = map.get(key: 13276) + print("\nInput student ID 13276, found name \(name!)") + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(key: 12836) + print("\nAfter removing 12836, hash table is\nKey -> Value") + map.print() + } +} diff --git a/en/codes/swift/chapter_hashing/hash_map_open_addressing.swift b/en/codes/swift/chapter_hashing/hash_map_open_addressing.swift new file mode 100644 index 000000000..7d5f21297 --- /dev/null +++ b/en/codes/swift/chapter_hashing/hash_map_open_addressing.swift @@ -0,0 +1,164 @@ +/** + * File: hash_map_open_addressing.swift + * Created Time: 2023-06-28 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Hash table with open addressing */ +class HashMapOpenAddressing { + var size: Int // Number of key-value pairs + var capacity: Int // Hash table capacity + var loadThres: Double // Load factor threshold for triggering expansion + var extendRatio: Int // Expansion multiplier + var buckets: [Pair?] // Bucket array + var TOMBSTONE: Pair // Removal marker + + /* Constructor */ + init() { + size = 0 + capacity = 4 + loadThres = 2.0 / 3.0 + extendRatio = 2 + buckets = Array(repeating: nil, count: capacity) + TOMBSTONE = Pair(key: -1, val: "-1") + } + + /* Hash function */ + func hashFunc(key: Int) -> Int { + key % capacity + } + + /* Load factor */ + func loadFactor() -> Double { + Double(size) / Double(capacity) + } + + /* Search for bucket index corresponding to key */ + func findBucket(key: Int) -> Int { + var index = hashFunc(key: key) + var firstTombstone = -1 + // Linear probing, break when encountering an empty bucket + while buckets[index] != nil { + // If key is encountered, return the corresponding bucket index + if buckets[index]!.key == key { + // If a removal marker was encountered before, move the key-value pair to that index + if firstTombstone != -1 { + buckets[firstTombstone] = buckets[index] + buckets[index] = TOMBSTONE + return firstTombstone // Return the moved bucket index + } + return index // Return bucket index + } + // Record the first removal marker encountered + if firstTombstone == -1 && buckets[index] == TOMBSTONE { + firstTombstone = index + } + // Calculate bucket index, wrap around to the head if past the tail + index = (index + 1) % capacity + } + // If key does not exist, return the index for insertion + return firstTombstone == -1 ? index : firstTombstone + } + + /* Query operation */ + func get(key: Int) -> String? { + // Search for bucket index corresponding to key + let index = findBucket(key: key) + // If key-value pair is found, return corresponding val + if buckets[index] != nil, buckets[index] != TOMBSTONE { + return buckets[index]!.val + } + // If key-value pair does not exist, return null + return nil + } + + /* Add operation */ + func put(key: Int, val: String) { + // When load factor exceeds threshold, perform expansion + if loadFactor() > loadThres { + extend() + } + // Search for bucket index corresponding to key + let index = findBucket(key: key) + // If key-value pair is found, overwrite val and return + if buckets[index] != nil, buckets[index] != TOMBSTONE { + buckets[index]!.val = val + return + } + // If key-value pair does not exist, add the key-value pair + buckets[index] = Pair(key: key, val: val) + size += 1 + } + + /* Remove operation */ + func remove(key: Int) { + // Search for bucket index corresponding to key + let index = findBucket(key: key) + // If key-value pair is found, overwrite it with removal marker + if buckets[index] != nil, buckets[index] != TOMBSTONE { + buckets[index] = TOMBSTONE + size -= 1 + } + } + + /* Expand hash table */ + func extend() { + // Temporarily store the original hash table + let bucketsTmp = buckets + // Initialize expanded new hash table + capacity *= extendRatio + buckets = Array(repeating: nil, count: capacity) + size = 0 + // Move key-value pairs from original hash table to new hash table + for pair in bucketsTmp { + if let pair, pair != TOMBSTONE { + put(key: pair.key, val: pair.val) + } + } + } + + /* Print hash table */ + func print() { + for pair in buckets { + if pair == nil { + Swift.print("null") + } else if pair == TOMBSTONE { + Swift.print("TOMBSTONE") + } else { + Swift.print("\(pair!.key) -> \(pair!.val)") + } + } + } +} + +@main +enum _HashMapOpenAddressing { + /* Driver Code */ + static func main() { + /* Initialize hash table */ + let map = HashMapOpenAddressing() + + /* Add operation */ + // Add key-value pair (key, value) to the hash table + map.put(key: 12836, val: "Xiao Ha") + map.put(key: 15937, val: "Xiao Luo") + map.put(key: 16750, val: "Xiao Suan") + map.put(key: 13276, val: "Xiao Fa") + map.put(key: 10583, val: "Xiao Ya") + print("\nAfter adding is complete, hash table is\nKey -> Value") + map.print() + + /* Query operation */ + // Input key into hash table to get value + let name = map.get(key: 13276) + print("\nInput student ID 13276, found name \(name!)") + + /* Remove operation */ + // Remove key-value pair (key, value) from hash table + map.remove(key: 16750) + print("\nAfter removing 16750, hash table is\nKey -> Value") + map.print() + } +} diff --git a/en/codes/swift/chapter_hashing/simple_hash.swift b/en/codes/swift/chapter_hashing/simple_hash.swift new file mode 100644 index 000000000..d69c4d7f7 --- /dev/null +++ b/en/codes/swift/chapter_hashing/simple_hash.swift @@ -0,0 +1,73 @@ +/** + * File: simple_hash.swift + * Created Time: 2023-07-01 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Additive hash */ +func addHash(key: String) -> Int { + var hash = 0 + let MODULUS = 1_000_000_007 + for c in key { + for scalar in c.unicodeScalars { + hash = (hash + Int(scalar.value)) % MODULUS + } + } + return hash +} + +/* Multiplicative hash */ +func mulHash(key: String) -> Int { + var hash = 0 + let MODULUS = 1_000_000_007 + for c in key { + for scalar in c.unicodeScalars { + hash = (31 * hash + Int(scalar.value)) % MODULUS + } + } + return hash +} + +/* XOR hash */ +func xorHash(key: String) -> Int { + var hash = 0 + let MODULUS = 1_000_000_007 + for c in key { + for scalar in c.unicodeScalars { + hash ^= Int(scalar.value) + } + } + return hash & MODULUS +} + +/* Rotational hash */ +func rotHash(key: String) -> Int { + var hash = 0 + let MODULUS = 1_000_000_007 + for c in key { + for scalar in c.unicodeScalars { + hash = ((hash << 4) ^ (hash >> 28) ^ Int(scalar.value)) % MODULUS + } + } + return hash +} + +@main +enum SimpleHash { + /* Driver Code */ + static func main() { + let key = "Hello Algo" + + var hash = addHash(key: key) + print("Additive hash value is \(hash)") + + hash = mulHash(key: key) + print("Multiplicative hash value is \(hash)") + + hash = xorHash(key: key) + print("XOR hash value is \(hash)") + + hash = rotHash(key: key) + print("Rotational hash value is \(hash)") + } +} diff --git a/en/codes/swift/chapter_heap/heap.swift b/en/codes/swift/chapter_heap/heap.swift new file mode 100644 index 000000000..2b8c13f7a --- /dev/null +++ b/en/codes/swift/chapter_heap/heap.swift @@ -0,0 +1,62 @@ +/** + * File: heap.swift + * Created Time: 2024-03-17 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import HeapModule +import utils + +func testPush(heap: inout Heap, val: Int) { + heap.insert(val) + print("\nAfter element \(val) pushes to heap\n") + PrintUtil.printHeap(queue: heap.unordered) +} + +func testPop(heap: inout Heap) { + let val = heap.removeMax() + print("\nAfter heap top element \(val) pops from heap\n") + PrintUtil.printHeap(queue: heap.unordered) +} + +@main +enum _Heap { + /* Driver Code */ + static func main() { + /* Initialize heap */ + // Swift's Heap type supports both max heap and min heap + var heap = Heap() + + /* Element enters heap */ + testPush(heap: &heap, val: 1) + testPush(heap: &heap, val: 3) + testPush(heap: &heap, val: 2) + testPush(heap: &heap, val: 5) + testPush(heap: &heap, val: 4) + + /* Check if heap is empty */ + let peek = heap.max() + print("\nHeap top element is \(peek!)\n") + + /* Time complexity is O(n), not O(nlogn) */ + testPop(heap: &heap) + testPop(heap: &heap) + testPop(heap: &heap) + testPop(heap: &heap) + testPop(heap: &heap) + + /* Get heap size */ + let size = heap.count + print("\nHeap size is \(size)\n") + + /* Check if heap is empty */ + let isEmpty = heap.isEmpty + print("\nIs heap empty \(isEmpty)\n") + + /* Input list and build heap */ + // Time complexity is O(n), not O(nlogn) + let heap2 = Heap([1, 3, 2, 5, 4]) + print("\nAfter input list and build heap") + PrintUtil.printHeap(queue: heap2.unordered) + } +} diff --git a/en/codes/swift/chapter_heap/my_heap.swift b/en/codes/swift/chapter_heap/my_heap.swift new file mode 100644 index 000000000..86e00d07e --- /dev/null +++ b/en/codes/swift/chapter_heap/my_heap.swift @@ -0,0 +1,163 @@ +/** + * File: my_heap.swift + * Created Time: 2023-01-28 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Max heap */ +class MaxHeap { + private var maxHeap: [Int] + + /* Constructor, build heap based on input list */ + init(nums: [Int]) { + // Add list elements to heap as is + maxHeap = nums + // Heapify all nodes except leaf nodes + for i in (0 ... parent(i: size() - 1)).reversed() { + siftDown(i: i) + } + } + + /* Get index of left child node */ + private func left(i: Int) -> Int { + 2 * i + 1 + } + + /* Get index of right child node */ + private func right(i: Int) -> Int { + 2 * i + 2 + } + + /* Get index of parent node */ + private func parent(i: Int) -> Int { + (i - 1) / 2 // Floor division + } + + /* Swap elements */ + private func swap(i: Int, j: Int) { + maxHeap.swapAt(i, j) + } + + /* Get heap size */ + func size() -> Int { + maxHeap.count + } + + /* Check if heap is empty */ + func isEmpty() -> Bool { + size() == 0 + } + + /* Access top element */ + func peek() -> Int { + maxHeap[0] + } + + /* Element enters heap */ + func push(val: Int) { + // Add node + maxHeap.append(val) + // Heapify from bottom to top + siftUp(i: size() - 1) + } + + /* Starting from node i, heapify from bottom to top */ + private func siftUp(i: Int) { + var i = i + while true { + // Get parent node of node i + let p = parent(i: i) + // When "crossing root node" or "node needs no repair", end heapify + if p < 0 || maxHeap[i] <= maxHeap[p] { + break + } + // Swap two nodes + swap(i: i, j: p) + // Loop upward heapify + i = p + } + } + + /* Element exits heap */ + func pop() -> Int { + // Handle empty case + if isEmpty() { + fatalError("Heap is empty") + } + // Delete node + swap(i: 0, j: size() - 1) + // Remove node + let val = maxHeap.remove(at: size() - 1) + // Return top element + siftDown(i: 0) + // Return heap top element + return val + } + + /* Starting from node i, heapify from top to bottom */ + private func siftDown(i: Int) { + var i = i + while true { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + let l = left(i: i) + let r = right(i: i) + var ma = i + if l < size(), maxHeap[l] > maxHeap[ma] { + ma = l + } + if r < size(), maxHeap[r] > maxHeap[ma] { + ma = r + } + // Swap two nodes + if ma == i { + break + } + // Swap two nodes + swap(i: i, j: ma) + // Loop downwards heapification + i = ma + } + } + + /* Driver Code */ + func print() { + let queue = maxHeap + PrintUtil.printHeap(queue: queue) + } +} + +@main +enum MyHeap { + /* Driver Code */ + static func main() { + /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + let maxHeap = MaxHeap(nums: [9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]) + print("\nAfter inputting list and building heap") + maxHeap.print() + + /* Check if heap is empty */ + var peek = maxHeap.peek() + print("\nHeap top element is \(peek)") + + /* Element enters heap */ + let val = 7 + maxHeap.push(val: val) + print("\nAfter element \(val) pushes to heap") + maxHeap.print() + + /* Time complexity is O(n), not O(nlogn) */ + peek = maxHeap.pop() + print("\nAfter heap top element \(peek) pops from heap") + maxHeap.print() + + /* Get heap size */ + let size = maxHeap.size() + print("\nHeap size is \(size)") + + /* Check if heap is empty */ + let isEmpty = maxHeap.isEmpty() + print("\nIs heap empty \(isEmpty)") + } +} diff --git a/en/codes/swift/chapter_heap/top_k.swift b/en/codes/swift/chapter_heap/top_k.swift new file mode 100644 index 000000000..9581e791d --- /dev/null +++ b/en/codes/swift/chapter_heap/top_k.swift @@ -0,0 +1,36 @@ +/** + * File: top_k.swift + * Created Time: 2023-07-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import HeapModule +import utils + +/* Find the largest k elements in array based on heap */ +func topKHeap(nums: [Int], k: Int) -> [Int] { + // Initialize min heap and build heap with first k elements + var heap = Heap(nums.prefix(k)) + // Starting from the (k+1)th element, maintain heap length as k + for i in nums.indices.dropFirst(k) { + // If current element is greater than top element, top element exits heap, current element enters heap + if nums[i] > heap.min()! { + _ = heap.removeMin() + heap.insert(nums[i]) + } + } + return heap.unordered +} + +@main +enum TopK { + /* Driver Code */ + static func main() { + let nums = [1, 7, 6, 3, 2] + let k = 3 + + let res = topKHeap(nums: nums, k: k) + print("The largest \(k) elements are") + PrintUtil.printHeap(queue: res) + } +} diff --git a/en/codes/swift/chapter_searching/binary_search.swift b/en/codes/swift/chapter_searching/binary_search.swift new file mode 100644 index 000000000..e473e8e9f --- /dev/null +++ b/en/codes/swift/chapter_searching/binary_search.swift @@ -0,0 +1,62 @@ +/** + * File: binary_search.swift + * Created Time: 2023-01-28 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Binary search (closed interval on both sides) */ +func binarySearch(nums: [Int], target: Int) -> Int { + // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + var i = nums.startIndex + var j = nums.endIndex - 1 + // Loop, exit when the search interval is empty (empty when i > j) + while i <= j { + let m = i + (j - i) / 2 // Calculate the midpoint index m + if nums[m] < target { // This means target is in the interval [m+1, j] + i = m + 1 + } else if nums[m] > target { // This means target is in the interval [i, m-1] + j = m - 1 + } else { // Found the target element, return its index + return m + } + } + // Target element not found, return -1 + return -1 +} + +/* Binary search (left-closed right-open interval) */ +func binarySearchLCRO(nums: [Int], target: Int) -> Int { + // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + var i = nums.startIndex + var j = nums.endIndex + // Loop, exit when the search interval is empty (empty when i = j) + while i < j { + let m = i + (j - i) / 2 // Calculate the midpoint index m + if nums[m] < target { // This means target is in the interval [m+1, j) + i = m + 1 + } else if nums[m] > target { // This means target is in the interval [i, m) + j = m + } else { // Found the target element, return its index + return m + } + } + // Target element not found, return -1 + return -1 +} + +@main +enum BinarySearch { + /* Driver Code */ + static func main() { + let target = 6 + let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] + + /* Binary search (closed interval on both sides) */ + var index = binarySearch(nums: nums, target: target) + print("Index of target element 6 = \(index)") + + /* Binary search (left-closed right-open interval) */ + index = binarySearchLCRO(nums: nums, target: target) + print("Index of target element 6 = \(index)") + } +} diff --git a/en/codes/swift/chapter_searching/binary_search_edge.swift b/en/codes/swift/chapter_searching/binary_search_edge.swift new file mode 100644 index 000000000..702e0dc41 --- /dev/null +++ b/en/codes/swift/chapter_searching/binary_search_edge.swift @@ -0,0 +1,51 @@ +/** + * File: binary_search_edge.swift + * Created Time: 2023-08-06 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import binary_search_insertion_target + +/* Binary search for the leftmost target */ +func binarySearchLeftEdge(nums: [Int], target: Int) -> Int { + // Equivalent to finding the insertion point of target + let i = binarySearchInsertion(nums: nums, target: target) + // Target not found, return -1 + if i == nums.endIndex || nums[i] != target { + return -1 + } + // Found target, return index i + return i +} + +/* Binary search for the rightmost target */ +func binarySearchRightEdge(nums: [Int], target: Int) -> Int { + // Convert to finding the leftmost target + 1 + let i = binarySearchInsertion(nums: nums, target: target + 1) + // j points to the rightmost target, i points to the first element greater than target + let j = i - 1 + // Target not found, return -1 + if j == -1 || nums[j] != target { + return -1 + } + // Found target, return index j + return j +} + +@main +enum BinarySearchEdge { + /* Driver Code */ + static func main() { + // Array with duplicate elements + let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] + print("\nArray nums = \(nums)") + + // Binary search left and right boundaries + for target in [6, 7] { + var index = binarySearchLeftEdge(nums: nums, target: target) + print("Leftmost element \(target) index is \(index)") + index = binarySearchRightEdge(nums: nums, target: target) + print("Rightmost element \(target) index is \(index)") + } + } +} diff --git a/en/codes/swift/chapter_searching/binary_search_insertion.swift b/en/codes/swift/chapter_searching/binary_search_insertion.swift new file mode 100644 index 000000000..9d6b0f7ff --- /dev/null +++ b/en/codes/swift/chapter_searching/binary_search_insertion.swift @@ -0,0 +1,71 @@ +/** + * File: binary_search_insertion.swift + * Created Time: 2023-08-06 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Binary search for insertion point (no duplicate elements) */ +func binarySearchInsertionSimple(nums: [Int], target: Int) -> Int { + // Initialize closed interval [0, n-1] + var i = nums.startIndex + var j = nums.endIndex - 1 + while i <= j { + let m = i + (j - i) / 2 // Calculate the midpoint index m + if nums[m] < target { + i = m + 1 // target is in the interval [m+1, j] + } else if nums[m] > target { + j = m - 1 // target is in the interval [i, m-1] + } else { + return m // Found target, return insertion point m + } + } + // Target not found, return insertion point i + return i +} + +/* Binary search for insertion point (with duplicate elements) */ +public func binarySearchInsertion(nums: [Int], target: Int) -> Int { + // Initialize closed interval [0, n-1] + var i = nums.startIndex + var j = nums.endIndex - 1 + while i <= j { + let m = i + (j - i) / 2 // Calculate the midpoint index m + if nums[m] < target { + i = m + 1 // target is in the interval [m+1, j] + } else if nums[m] > target { + j = m - 1 // target is in the interval [i, m-1] + } else { + j = m - 1 // The first element less than target is in the interval [i, m-1] + } + } + // Return insertion point i + return i +} + +#if !TARGET + +@main +enum BinarySearchInsertion { + /* Driver Code */ + static func main() { + // Array without duplicate elements + var nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] + print("\nArray nums = \(nums)") + // Binary search for insertion point + for target in [6, 9] { + let index = binarySearchInsertionSimple(nums: nums, target: target) + print("Insertion point index for element \(target) is \(index)") + } + + // Array with duplicate elements + nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] + print("\nArray nums = \(nums)") + // Binary search for insertion point + for target in [2, 6, 20] { + let index = binarySearchInsertion(nums: nums, target: target) + print("Insertion point index for element \(target) is \(index)") + } + } +} + +#endif diff --git a/en/codes/swift/chapter_searching/binary_search_insertion_target.swift b/en/codes/swift/chapter_searching/binary_search_insertion_target.swift new file mode 100644 index 000000000..9d6b0f7ff --- /dev/null +++ b/en/codes/swift/chapter_searching/binary_search_insertion_target.swift @@ -0,0 +1,71 @@ +/** + * File: binary_search_insertion.swift + * Created Time: 2023-08-06 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Binary search for insertion point (no duplicate elements) */ +func binarySearchInsertionSimple(nums: [Int], target: Int) -> Int { + // Initialize closed interval [0, n-1] + var i = nums.startIndex + var j = nums.endIndex - 1 + while i <= j { + let m = i + (j - i) / 2 // Calculate the midpoint index m + if nums[m] < target { + i = m + 1 // target is in the interval [m+1, j] + } else if nums[m] > target { + j = m - 1 // target is in the interval [i, m-1] + } else { + return m // Found target, return insertion point m + } + } + // Target not found, return insertion point i + return i +} + +/* Binary search for insertion point (with duplicate elements) */ +public func binarySearchInsertion(nums: [Int], target: Int) -> Int { + // Initialize closed interval [0, n-1] + var i = nums.startIndex + var j = nums.endIndex - 1 + while i <= j { + let m = i + (j - i) / 2 // Calculate the midpoint index m + if nums[m] < target { + i = m + 1 // target is in the interval [m+1, j] + } else if nums[m] > target { + j = m - 1 // target is in the interval [i, m-1] + } else { + j = m - 1 // The first element less than target is in the interval [i, m-1] + } + } + // Return insertion point i + return i +} + +#if !TARGET + +@main +enum BinarySearchInsertion { + /* Driver Code */ + static func main() { + // Array without duplicate elements + var nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35] + print("\nArray nums = \(nums)") + // Binary search for insertion point + for target in [6, 9] { + let index = binarySearchInsertionSimple(nums: nums, target: target) + print("Insertion point index for element \(target) is \(index)") + } + + // Array with duplicate elements + nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15] + print("\nArray nums = \(nums)") + // Binary search for insertion point + for target in [2, 6, 20] { + let index = binarySearchInsertion(nums: nums, target: target) + print("Insertion point index for element \(target) is \(index)") + } + } +} + +#endif diff --git a/en/codes/swift/chapter_searching/hashing_search.swift b/en/codes/swift/chapter_searching/hashing_search.swift new file mode 100644 index 000000000..44d4df989 --- /dev/null +++ b/en/codes/swift/chapter_searching/hashing_search.swift @@ -0,0 +1,50 @@ +/** + * File: hashing_search.swift + * Created Time: 2023-01-28 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Hash search (array) */ +func hashingSearchArray(map: [Int: Int], target: Int) -> Int { + // Hash table's key: target element, value: index + // If this key does not exist in the hash table, return -1 + return map[target, default: -1] +} + +/* Hash search (linked list) */ +func hashingSearchLinkedList(map: [Int: ListNode], target: Int) -> ListNode? { + // Hash table key: target node value, value: node object + // If key is not in hash table, return null + return map[target] +} + +@main +enum HashingSearch { + /* Driver Code */ + static func main() { + let target = 3 + + /* Hash search (array) */ + let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] + // Initialize hash table + var map: [Int: Int] = [:] + for i in nums.indices { + map[nums[i]] = i // key: element, value: index + } + let index = hashingSearchArray(map: map, target: target) + print("Index of target element 3 = \(index)") + + /* Hash search (linked list) */ + var head = ListNode.arrToLinkedList(arr: nums) + // Initialize hash table + var map1: [Int: ListNode] = [:] + while head != nil { + map1[head!.val] = head! // key: node value, value: node + head = head?.next + } + let node = hashingSearchLinkedList(map: map1, target: target) + print("Node object corresponding to target node value 3 is \(node!)") + } +} diff --git a/en/codes/swift/chapter_searching/linear_search.swift b/en/codes/swift/chapter_searching/linear_search.swift new file mode 100644 index 000000000..fc0cb8acb --- /dev/null +++ b/en/codes/swift/chapter_searching/linear_search.swift @@ -0,0 +1,53 @@ +/** + * File: linear_search.swift + * Created Time: 2023-01-28 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Linear search (array) */ +func linearSearchArray(nums: [Int], target: Int) -> Int { + // Traverse array + for i in nums.indices { + // Found the target element, return its index + if nums[i] == target { + return i + } + } + // Target element not found, return -1 + return -1 +} + +/* Linear search (linked list) */ +func linearSearchLinkedList(head: ListNode?, target: Int) -> ListNode? { + var head = head + // Traverse the linked list + while head != nil { + // Found the target node, return it + if head?.val == target { + return head + } + head = head?.next + } + // Target node not found, return null + return nil +} + +@main +enum LinearSearch { + /* Driver Code */ + static func main() { + let target = 3 + + /* Perform linear search in array */ + let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8] + let index = linearSearchArray(nums: nums, target: target) + print("Index of target element 3 = \(index)") + + /* Perform linear search in linked list */ + let head = ListNode.arrToLinkedList(arr: nums) + let node = linearSearchLinkedList(head: head, target: target) + print("Node object corresponding to target node value 3 is \(node!)") + } +} diff --git a/en/codes/swift/chapter_searching/two_sum.swift b/en/codes/swift/chapter_searching/two_sum.swift new file mode 100644 index 000000000..defd97bb7 --- /dev/null +++ b/en/codes/swift/chapter_searching/two_sum.swift @@ -0,0 +1,49 @@ +/** + * File: two_sum.swift + * Created Time: 2023-01-03 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Method 1: Brute force enumeration */ +func twoSumBruteForce(nums: [Int], target: Int) -> [Int] { + // Two nested loops, time complexity is O(n^2) + for i in nums.indices.dropLast() { + for j in nums.indices.dropFirst(i + 1) { + if nums[i] + nums[j] == target { + return [i, j] + } + } + } + return [0] +} + +/* Method 2: Auxiliary hash table */ +func twoSumHashTable(nums: [Int], target: Int) -> [Int] { + // Auxiliary hash table, space complexity is O(n) + var dic: [Int: Int] = [:] + // Single loop, time complexity is O(n) + for i in nums.indices { + if let j = dic[target - nums[i]] { + return [j, i] + } + dic[nums[i]] = i + } + return [0] +} + +@main +enum LeetcodeTwoSum { + /* Driver Code */ + static func main() { + // ======= Test Case ======= + let nums = [2, 7, 11, 15] + let target = 13 + // ====== Driver Code ====== + // Method 1 + var res = twoSumBruteForce(nums: nums, target: target) + print("Method 1 res = \(res)") + // Method 2 + res = twoSumHashTable(nums: nums, target: target) + print("Method 2 res = \(res)") + } +} diff --git a/en/codes/swift/chapter_sorting/bubble_sort.swift b/en/codes/swift/chapter_sorting/bubble_sort.swift new file mode 100644 index 000000000..5b18b7528 --- /dev/null +++ b/en/codes/swift/chapter_sorting/bubble_sort.swift @@ -0,0 +1,51 @@ +/** + * File: bubble_sort.swift + * Created Time: 2023-01-29 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Bubble sort */ +func bubbleSort(nums: inout [Int]) { + // Outer loop: unsorted range is [0, i] + for i in nums.indices.dropFirst().reversed() { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for j in 0 ..< i { + if nums[j] > nums[j + 1] { + // Swap nums[j] and nums[j + 1] + nums.swapAt(j, j + 1) + } + } + } +} + +/* Bubble sort (flag optimization) */ +func bubbleSortWithFlag(nums: inout [Int]) { + // Outer loop: unsorted range is [0, i] + for i in nums.indices.dropFirst().reversed() { + var flag = false // Initialize flag + for j in 0 ..< i { + if nums[j] > nums[j + 1] { + // Swap nums[j] and nums[j + 1] + nums.swapAt(j, j + 1) + flag = true // Record element swap + } + } + if !flag { // No elements were swapped in this round of "bubbling", exit directly + break + } + } +} + +@main +enum BubbleSort { + /* Driver Code */ + static func main() { + var nums = [4, 1, 3, 1, 5, 2] + bubbleSort(nums: &nums) + print("After bubble sort, nums = \(nums)") + + var nums1 = [4, 1, 3, 1, 5, 2] + bubbleSortWithFlag(nums: &nums1) + print("After bubble sort, nums1 = \(nums1)") + } +} diff --git a/en/codes/swift/chapter_sorting/bucket_sort.swift b/en/codes/swift/chapter_sorting/bucket_sort.swift new file mode 100644 index 000000000..d29b24938 --- /dev/null +++ b/en/codes/swift/chapter_sorting/bucket_sort.swift @@ -0,0 +1,43 @@ +/** + * File: bucket_sort.swift + * Created Time: 2023-03-27 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Bucket sort */ +func bucketSort(nums: inout [Double]) { + // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + let k = nums.count / 2 + var buckets = (0 ..< k).map { _ in [Double]() } + // 1. Distribute array elements into various buckets + for num in nums { + // Input data range is [0, 1), use num * k to map to index range [0, k-1] + let i = Int(num * Double(k)) + // Add num to bucket i + buckets[i].append(num) + } + // 2. Sort each bucket + for i in buckets.indices { + // Use built-in sorting function, can also replace with other sorting algorithms + buckets[i].sort() + } + // 3. Traverse buckets to merge results + var i = nums.startIndex + for bucket in buckets { + for num in bucket { + nums[i] = num + i += 1 + } + } +} + +@main +enum BucketSort { + /* Driver Code */ + static func main() { + // Assume input data is floating point, interval [0, 1) + var nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37] + bucketSort(nums: &nums) + print("After bucket sort, nums = \(nums)") + } +} diff --git a/en/codes/swift/chapter_sorting/counting_sort.swift b/en/codes/swift/chapter_sorting/counting_sort.swift new file mode 100644 index 000000000..329852c98 --- /dev/null +++ b/en/codes/swift/chapter_sorting/counting_sort.swift @@ -0,0 +1,70 @@ +/** + * File: counting_sort.swift + * Created Time: 2023-03-22 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Counting sort */ +// Simple implementation, cannot be used for sorting objects +func countingSortNaive(nums: inout [Int]) { + // 1. Count the maximum element m in the array + let m = nums.max()! + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + var counter = Array(repeating: 0, count: m + 1) + for num in nums { + counter[num] += 1 + } + // 3. Traverse counter, filling each element back into the original array nums + var i = 0 + for num in 0 ..< m + 1 { + for _ in 0 ..< counter[num] { + nums[i] = num + i += 1 + } + } +} + +/* Counting sort */ +// Complete implementation, can sort objects and is a stable sort +func countingSort(nums: inout [Int]) { + // 1. Count the maximum element m in the array + let m = nums.max()! + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + var counter = Array(repeating: 0, count: m + 1) + for num in nums { + counter[num] += 1 + } + // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + // counter[num]-1 is the last index where num appears in res + for i in 0 ..< m { + counter[i + 1] += counter[i] + } + // 4. Traverse nums in reverse order, placing each element into the result array res + // Initialize the array res to record results + var res = Array(repeating: 0, count: nums.count) + for i in nums.indices.reversed() { + let num = nums[i] + res[counter[num] - 1] = num // Place num at the corresponding index + counter[num] -= 1 // Decrement the prefix sum by 1, getting the next index to place num + } + // Use result array res to overwrite the original array nums + for i in nums.indices { + nums[i] = res[i] + } +} + +@main +enum CountingSort { + /* Driver Code */ + static func main() { + var nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] + countingSortNaive(nums: &nums) + print("After counting sort (cannot sort objects), nums = \(nums)") + + var nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4] + countingSort(nums: &nums1) + print("After counting sort, nums1 = \(nums1)") + } +} diff --git a/en/codes/swift/chapter_sorting/heap_sort.swift b/en/codes/swift/chapter_sorting/heap_sort.swift new file mode 100644 index 000000000..cce644c80 --- /dev/null +++ b/en/codes/swift/chapter_sorting/heap_sort.swift @@ -0,0 +1,55 @@ +/** + * File: heap_sort.swift + * Created Time: 2023-05-28 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Heap length is n, start heapifying node i, from top to bottom */ +func siftDown(nums: inout [Int], n: Int, i: Int) { + var i = i + while true { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + let l = 2 * i + 1 + let r = 2 * i + 2 + var ma = i + if l < n, nums[l] > nums[ma] { + ma = l + } + if r < n, nums[r] > nums[ma] { + ma = r + } + // Swap two nodes + if ma == i { + break + } + // Swap two nodes + nums.swapAt(i, ma) + // Loop downwards heapification + i = ma + } +} + +/* Heap sort */ +func heapSort(nums: inout [Int]) { + // Build heap operation: heapify all nodes except leaves + for i in stride(from: nums.count / 2 - 1, through: 0, by: -1) { + siftDown(nums: &nums, n: nums.count, i: i) + } + // Extract the largest element from the heap and repeat for n-1 rounds + for i in nums.indices.dropFirst().reversed() { + // Delete node + nums.swapAt(0, i) + // Start heapifying the root node, from top to bottom + siftDown(nums: &nums, n: i, i: 0) + } +} + +@main +enum HeapSort { + /* Driver Code */ + static func main() { + var nums = [4, 1, 3, 1, 5, 2] + heapSort(nums: &nums) + print("After heap sort, nums = \(nums)") + } +} diff --git a/en/codes/swift/chapter_sorting/insertion_sort.swift b/en/codes/swift/chapter_sorting/insertion_sort.swift new file mode 100644 index 000000000..6b9f40be5 --- /dev/null +++ b/en/codes/swift/chapter_sorting/insertion_sort.swift @@ -0,0 +1,30 @@ +/** + * File: insertion_sort.swift + * Created Time: 2023-01-29 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Insertion sort */ +func insertionSort(nums: inout [Int]) { + // Outer loop: sorted interval is [0, i-1] + for i in nums.indices.dropFirst() { + let base = nums[i] + var j = i - 1 + // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + while j >= 0, nums[j] > base { + nums[j + 1] = nums[j] // Move nums[j] to the right by one position + j -= 1 + } + nums[j + 1] = base // Assign base to the correct position + } +} + +@main +enum InsertionSort { + /* Driver Code */ + static func main() { + var nums = [4, 1, 3, 1, 5, 2] + insertionSort(nums: &nums) + print("After insertion sort, nums = \(nums)") + } +} diff --git a/en/codes/swift/chapter_sorting/merge_sort.swift b/en/codes/swift/chapter_sorting/merge_sort.swift new file mode 100644 index 000000000..580c58f7e --- /dev/null +++ b/en/codes/swift/chapter_sorting/merge_sort.swift @@ -0,0 +1,65 @@ +/** + * File: merge_sort.swift + * Created Time: 2023-01-29 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Merge left subarray and right subarray */ +func merge(nums: inout [Int], left: Int, mid: Int, right: Int) { + // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + // Create a temporary array tmp to store the merged results + var tmp = Array(repeating: 0, count: right - left + 1) + // Initialize the start indices of the left and right subarrays + var i = left, j = mid + 1, k = 0 + // While both subarrays still have elements, compare and copy the smaller element into the temporary array + while i <= mid, j <= right { + if nums[i] <= nums[j] { + tmp[k] = nums[i] + i += 1 + } else { + tmp[k] = nums[j] + j += 1 + } + k += 1 + } + // Copy the remaining elements of the left and right subarrays into the temporary array + while i <= mid { + tmp[k] = nums[i] + i += 1 + k += 1 + } + while j <= right { + tmp[k] = nums[j] + j += 1 + k += 1 + } + // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + for k in tmp.indices { + nums[left + k] = tmp[k] + } +} + +/* Merge sort */ +func mergeSort(nums: inout [Int], left: Int, right: Int) { + // Termination condition + if left >= right { // Terminate recursion when subarray length is 1 + return + } + // Divide and conquer stage + let mid = left + (right - left) / 2 // Calculate midpoint + mergeSort(nums: &nums, left: left, right: mid) // Recursively process the left subarray + mergeSort(nums: &nums, left: mid + 1, right: right) // Recursively process the right subarray + // Merge stage + merge(nums: &nums, left: left, mid: mid, right: right) +} + +@main +enum MergeSort { + /* Driver Code */ + static func main() { + /* Merge sort */ + var nums = [7, 3, 2, 6, 0, 1, 5, 4] + mergeSort(nums: &nums, left: nums.startIndex, right: nums.endIndex - 1) + print("After merge sort, nums = \(nums)") + } +} diff --git a/en/codes/swift/chapter_sorting/quick_sort.swift b/en/codes/swift/chapter_sorting/quick_sort.swift new file mode 100644 index 000000000..326be2507 --- /dev/null +++ b/en/codes/swift/chapter_sorting/quick_sort.swift @@ -0,0 +1,114 @@ +/** + * File: quick_sort.swift + * Created Time: 2023-01-29 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Quick sort class */ +/* Sentinel partition */ +func partition(nums: inout [Int], left: Int, right: Int) -> Int { + // Use nums[left] as the pivot + var i = left + var j = right + while i < j { + while i < j, nums[j] >= nums[left] { + j -= 1 // Search from right to left for the first element smaller than the pivot + } + while i < j, nums[i] <= nums[left] { + i += 1 // Search from left to right for the first element greater than the pivot + } + nums.swapAt(i, j) // Swap these two elements + } + nums.swapAt(i, left) // Swap the pivot to the boundary between the two subarrays + return i // Return the index of the pivot +} + +/* Quick sort */ +func quickSort(nums: inout [Int], left: Int, right: Int) { + // Terminate recursion when subarray length is 1 + if left >= right { + return + } + // Sentinel partition + let pivot = partition(nums: &nums, left: left, right: right) + // Recursively process the left subarray and right subarray + quickSort(nums: &nums, left: left, right: pivot - 1) + quickSort(nums: &nums, left: pivot + 1, right: right) +} + +/* Quick sort class (median pivot optimization) */ +/* Select the median of three candidate elements */ +func medianThree(nums: [Int], left: Int, mid: Int, right: Int) -> Int { + let l = nums[left] + let m = nums[mid] + let r = nums[right] + if (l <= m && m <= r) || (r <= m && m <= l) { + return mid // m is between l and r + } + if (m <= l && l <= r) || (r <= l && l <= m) { + return left // l is between m and r + } + return right +} + +/* Sentinel partition (median of three) */ +func partitionMedian(nums: inout [Int], left: Int, right: Int) -> Int { + // Select the median of three candidate elements + let med = medianThree(nums: nums, left: left, mid: left + (right - left) / 2, right: right) + // Swap the median to the array's leftmost position + nums.swapAt(left, med) + return partition(nums: &nums, left: left, right: right) +} + +/* Quick sort (recursion depth optimization) */ +func quickSortMedian(nums: inout [Int], left: Int, right: Int) { + // Terminate recursion when subarray length is 1 + if left >= right { + return + } + // Sentinel partition + let pivot = partitionMedian(nums: &nums, left: left, right: right) + // Recursively process the left subarray and right subarray + quickSortMedian(nums: &nums, left: left, right: pivot - 1) + quickSortMedian(nums: &nums, left: pivot + 1, right: right) +} + +/* Quick sort (recursion depth optimization) */ +func quickSortTailCall(nums: inout [Int], left: Int, right: Int) { + var left = left + var right = right + // Terminate when subarray length is 1 + while left < right { + // Sentinel partition operation + let pivot = partition(nums: &nums, left: left, right: right) + // Perform quick sort on the shorter of the two subarrays + if (pivot - left) < (right - pivot) { + quickSortTailCall(nums: &nums, left: left, right: pivot - 1) // Recursively sort the left subarray + left = pivot + 1 // Remaining unsorted interval is [pivot + 1, right] + } else { + quickSortTailCall(nums: &nums, left: pivot + 1, right: right) // Recursively sort the right subarray + right = pivot - 1 // Remaining unsorted interval is [left, pivot - 1] + } + } +} + +@main +enum QuickSort { + /* Driver Code */ + static func main() { + /* Quick sort */ + var nums = [2, 4, 1, 0, 3, 5] + quickSort(nums: &nums, left: nums.startIndex, right: nums.endIndex - 1) + print("After quick sort, nums = \(nums)") + + /* Quick sort (recursion depth optimization) */ + var nums1 = [2, 4, 1, 0, 3, 5] + quickSortMedian(nums: &nums1, left: nums1.startIndex, right: nums1.endIndex - 1) + print("After quick sort (median pivot optimization), nums1 = \(nums1)") + + /* Quick sort (recursion depth optimization) */ + var nums2 = [2, 4, 1, 0, 3, 5] + quickSortTailCall(nums: &nums2, left: nums2.startIndex, right: nums2.endIndex - 1) + print("After quick sort (recursion depth optimization), nums2 = \(nums2)") + } +} diff --git a/en/codes/swift/chapter_sorting/radix_sort.swift b/en/codes/swift/chapter_sorting/radix_sort.swift new file mode 100644 index 000000000..b677448bc --- /dev/null +++ b/en/codes/swift/chapter_sorting/radix_sort.swift @@ -0,0 +1,79 @@ +/** + * File: radix_sort.swift + * Created Time: 2023-01-29 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Get the k-th digit of element num, where exp = 10^(k-1) */ +func digit(num: Int, exp: Int) -> Int { + // Passing exp instead of k can avoid repeated expensive exponentiation here + (num / exp) % 10 +} + +/* Counting sort (based on nums k-th digit) */ +func countingSortDigit(nums: inout [Int], exp: Int) { + // Decimal digit range is 0~9, therefore need a bucket array of length 10 + var counter = Array(repeating: 0, count: 10) + // Count the occurrence of digits 0~9 + for i in nums.indices { + let d = digit(num: nums[i], exp: exp) // Get the k-th digit of nums[i], noted as d + counter[d] += 1 // Count the occurrence of digit d + } + // Calculate prefix sum, converting "occurrence count" into "array index" + for i in 1 ..< 10 { + counter[i] += counter[i - 1] + } + // Traverse in reverse, based on bucket statistics, place each element into res + var res = Array(repeating: 0, count: nums.count) + for i in nums.indices.reversed() { + let d = digit(num: nums[i], exp: exp) + let j = counter[d] - 1 // Get the index j for d in the array + res[j] = nums[i] // Place the current element at index j + counter[d] -= 1 // Decrease the count of d by 1 + } + // Use result to overwrite the original array nums + for i in nums.indices { + nums[i] = res[i] + } +} + +/* Radix sort */ +func radixSort(nums: inout [Int]) { + // Get the maximum element of the array, used to determine the maximum number of digits + var m = Int.min + for num in nums { + if num > m { + m = num + } + } + // Traverse from the lowest to the highest digit + for exp in sequence(first: 1, next: { m >= ($0 * 10) ? $0 * 10 : nil }) { + // Perform counting sort on the k-th digit of array elements + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // i.e., exp = 10^(k-1) + countingSortDigit(nums: &nums, exp: exp) + } +} + +@main +enum RadixSort { + /* Driver Code */ + static func main() { + // Radix sort + var nums = [ + 10_546_151, + 35_663_510, + 42_865_989, + 34_862_445, + 81_883_077, + 88_906_420, + 72_429_244, + 30_524_779, + 82_060_337, + 63_832_996, + ] + radixSort(nums: &nums) + print("After radix sort, nums = \(nums)") + } +} diff --git a/en/codes/swift/chapter_sorting/selection_sort.swift b/en/codes/swift/chapter_sorting/selection_sort.swift new file mode 100644 index 000000000..3424d3c93 --- /dev/null +++ b/en/codes/swift/chapter_sorting/selection_sort.swift @@ -0,0 +1,31 @@ +/** + * File: selection_sort.swift + * Created Time: 2023-05-28 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Selection sort */ +func selectionSort(nums: inout [Int]) { + // Outer loop: unsorted interval is [i, n-1] + for i in nums.indices.dropLast() { + // Inner loop: find the smallest element within the unsorted interval + var k = i + for j in nums.indices.dropFirst(i + 1) { + if nums[j] < nums[k] { + k = j // Record the index of the smallest element + } + } + // Swap the smallest element with the first element of the unsorted interval + nums.swapAt(i, k) + } +} + +@main +enum SelectionSort { + /* Driver Code */ + static func main() { + var nums = [4, 1, 3, 1, 5, 2] + selectionSort(nums: &nums) + print("After selection sort, nums = \(nums)") + } +} diff --git a/en/codes/swift/chapter_stack_and_queue/array_deque.swift b/en/codes/swift/chapter_stack_and_queue/array_deque.swift new file mode 100644 index 000000000..822948eec --- /dev/null +++ b/en/codes/swift/chapter_stack_and_queue/array_deque.swift @@ -0,0 +1,148 @@ +/** + * File: array_deque.swift + * Created Time: 2023-02-22 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Double-ended queue based on circular array implementation */ +class ArrayDeque { + private var nums: [Int] // Array for storing double-ended queue elements + private var front: Int // Front pointer, points to the front of the queue element + private var _size: Int // Double-ended queue length + + /* Constructor */ + init(capacity: Int) { + nums = Array(repeating: 0, count: capacity) + front = 0 + _size = 0 + } + + /* Get the capacity of the double-ended queue */ + func capacity() -> Int { + nums.count + } + + /* Get the length of the double-ended queue */ + func size() -> Int { + _size + } + + /* Check if the double-ended queue is empty */ + func isEmpty() -> Bool { + size() == 0 + } + + /* Calculate circular array index */ + private func index(i: Int) -> Int { + // Use modulo operation to wrap the array head and tail together + // When i passes the tail of the array, return to the head + // When i passes the head of the array, return to the tail + (i + capacity()) % capacity() + } + + /* Front of the queue enqueue */ + func pushFirst(num: Int) { + if size() == capacity() { + print("Double-ended queue is full") + return + } + // Use modulo operation to wrap front around to the tail after passing the head of the array + // Add num to the front of the queue + front = index(i: front - 1) + // Add num to front of queue + nums[front] = num + _size += 1 + } + + /* Rear of the queue enqueue */ + func pushLast(num: Int) { + if size() == capacity() { + print("Double-ended queue is full") + return + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + let rear = index(i: front + size()) + // Front pointer moves one position backward + nums[rear] = num + _size += 1 + } + + /* Rear of the queue dequeue */ + func popFirst() -> Int { + let num = peekFirst() + // Move front pointer backward by one position + front = index(i: front + 1) + _size -= 1 + return num + } + + /* Access rear of the queue element */ + func popLast() -> Int { + let num = peekLast() + _size -= 1 + return num + } + + /* Return list for printing */ + func peekFirst() -> Int { + if isEmpty() { + fatalError("Deque is empty") + } + return nums[front] + } + + /* Driver Code */ + func peekLast() -> Int { + if isEmpty() { + fatalError("Deque is empty") + } + // Initialize double-ended queue + let last = index(i: front + size() - 1) + return nums[last] + } + + /* Return array for printing */ + func toArray() -> [Int] { + // Elements enqueue + (front ..< front + size()).map { nums[index(i: $0)] } + } +} + +@main +enum _ArrayDeque { + /* Driver Code */ + static func main() { + /* Get the length of the double-ended queue */ + let deque = ArrayDeque(capacity: 10) + deque.pushLast(num: 3) + deque.pushLast(num: 2) + deque.pushLast(num: 5) + print("Deque deque = \(deque.toArray())") + + /* Update element */ + let peekFirst = deque.peekFirst() + print("Front element peekFirst = \(peekFirst)") + let peekLast = deque.peekLast() + print("Rear element peekLast = \(peekLast)") + + /* Elements enqueue */ + deque.pushLast(num: 4) + print("After element 4 enqueues at rear, deque = \(deque.toArray())") + deque.pushFirst(num: 1) + print("After element 1 enqueues at front, deque = \(deque.toArray())") + + /* Element dequeue */ + let popLast = deque.popLast() + print("Dequeue rear element = \(popLast), after rear dequeue deque = \(deque.toArray())") + let popFirst = deque.popFirst() + print("Dequeue front element = \(popFirst), after front dequeue deque = \(deque.toArray())") + + /* Get the length of the double-ended queue */ + let size = deque.size() + print("Deque length size = \(size)") + + /* Check if the double-ended queue is empty */ + let isEmpty = deque.isEmpty() + print("Is deque empty = \(isEmpty)") + } +} diff --git a/en/codes/swift/chapter_stack_and_queue/array_queue.swift b/en/codes/swift/chapter_stack_and_queue/array_queue.swift new file mode 100644 index 000000000..c3ae8a828 --- /dev/null +++ b/en/codes/swift/chapter_stack_and_queue/array_queue.swift @@ -0,0 +1,113 @@ +/** + * File: array_queue.swift + * Created Time: 2023-01-11 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Queue based on circular array implementation */ +class ArrayQueue { + private var nums: [Int] // Array for storing queue elements + private var front: Int // Front pointer, points to the front of the queue element + private var _size: Int // Queue length + + init(capacity: Int) { + // Initialize array + nums = Array(repeating: 0, count: capacity) + front = 0 + _size = 0 + } + + /* Get the capacity of the queue */ + func capacity() -> Int { + nums.count + } + + /* Get the length of the queue */ + func size() -> Int { + _size + } + + /* Check if the queue is empty */ + func isEmpty() -> Bool { + size() == 0 + } + + /* Enqueue */ + func push(num: Int) { + if size() == capacity() { + print("Queue is full") + return + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Add num to the rear of the queue + let rear = (front + size()) % capacity() + // Front pointer moves one position backward + nums[rear] = num + _size += 1 + } + + /* Dequeue */ + @discardableResult + func pop() -> Int { + let num = peek() + // Move front pointer backward by one position, if it passes the tail, return to array head + front = (front + 1) % capacity() + _size -= 1 + return num + } + + /* Return list for printing */ + func peek() -> Int { + if isEmpty() { + fatalError("Queue is empty") + } + return nums[front] + } + + /* Return array */ + func toArray() -> [Int] { + // Elements enqueue + (front ..< front + size()).map { nums[$0 % capacity()] } + } +} + +@main +enum _ArrayQueue { + /* Driver Code */ + static func main() { + /* Access front of the queue element */ + let capacity = 10 + let queue = ArrayQueue(capacity: capacity) + + /* Elements enqueue */ + queue.push(num: 1) + queue.push(num: 3) + queue.push(num: 2) + queue.push(num: 5) + queue.push(num: 4) + print("Queue queue = \(queue.toArray())") + + /* Return list for printing */ + let peek = queue.peek() + print("Front element peek = \(peek)") + + /* Element dequeue */ + let pop = queue.pop() + print("Dequeue element pop = \(pop), after dequeue queue = \(queue.toArray())") + + /* Get the length of the queue */ + let size = queue.size() + print("Queue length size = \(size)") + + /* Check if the queue is empty */ + let isEmpty = queue.isEmpty() + print("Is queue empty = \(isEmpty)") + + /* Test circular array */ + for i in 0 ..< 10 { + queue.push(num: i) + queue.pop() + print("After round \(i) enqueue + dequeue, queue = \(queue.toArray())") + } + } +} diff --git a/en/codes/swift/chapter_stack_and_queue/array_stack.swift b/en/codes/swift/chapter_stack_and_queue/array_stack.swift new file mode 100644 index 000000000..2d184cf39 --- /dev/null +++ b/en/codes/swift/chapter_stack_and_queue/array_stack.swift @@ -0,0 +1,85 @@ +/** + * File: array_stack.swift + * Created Time: 2023-01-09 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Stack based on array implementation */ +class ArrayStack { + private var stack: [Int] + + init() { + // Initialize list (dynamic array) + stack = [] + } + + /* Get the length of the stack */ + func size() -> Int { + stack.count + } + + /* Check if the stack is empty */ + func isEmpty() -> Bool { + stack.isEmpty + } + + /* Push */ + func push(num: Int) { + stack.append(num) + } + + /* Pop */ + @discardableResult + func pop() -> Int { + if isEmpty() { + fatalError("Stack is empty") + } + return stack.removeLast() + } + + /* Return list for printing */ + func peek() -> Int { + if isEmpty() { + fatalError("Stack is empty") + } + return stack.last! + } + + /* Convert List to Array and return */ + func toArray() -> [Int] { + stack + } +} + +@main +enum _ArrayStack { + /* Driver Code */ + static func main() { + /* Access top of the stack element */ + let stack = ArrayStack() + + /* Elements push onto stack */ + stack.push(num: 1) + stack.push(num: 3) + stack.push(num: 2) + stack.push(num: 5) + stack.push(num: 4) + print("Stack stack = \(stack.toArray())") + + /* Return list for printing */ + let peek = stack.peek() + print("Top element peek = \(peek)") + + /* Element pop from stack */ + let pop = stack.pop() + print("Pop element pop = \(pop), after pop stack = \(stack.toArray())") + + /* Get the length of the stack */ + let size = stack.size() + print("Stack length size = \(size)") + + /* Check if empty */ + let isEmpty = stack.isEmpty() + print("Is stack empty = \(isEmpty)") + } +} diff --git a/en/codes/swift/chapter_stack_and_queue/deque.swift b/en/codes/swift/chapter_stack_and_queue/deque.swift new file mode 100644 index 000000000..b825c685b --- /dev/null +++ b/en/codes/swift/chapter_stack_and_queue/deque.swift @@ -0,0 +1,44 @@ +/** + * File: deque.swift + * Created Time: 2023-01-14 + * Author: nuomi1 (nuomi1@qq.com) + */ + +@main +enum Deque { + /* Driver Code */ + static func main() { + /* Get the length of the double-ended queue */ + // Swift has no built-in deque class, can use Array as deque + var deque: [Int] = [] + + /* Elements enqueue */ + deque.append(2) + deque.append(5) + deque.append(4) + deque.insert(3, at: 0) + deque.insert(1, at: 0) + print("Deque deque = \(deque)") + + /* Update element */ + let peekFirst = deque.first! + print("Front element peekFirst = \(peekFirst)") + let peekLast = deque.last! + print("Rear element peekLast = \(peekLast)") + + /* Element dequeue */ + // When simulating with Array, popFirst complexity is O(n) + let popFirst = deque.removeFirst() + print("Dequeue front element popFirst = \(popFirst), after front dequeue deque = \(deque)") + let popLast = deque.removeLast() + print("Dequeue rear element popLast = \(popLast), after rear dequeue deque = \(deque)") + + /* Get the length of the double-ended queue */ + let size = deque.count + print("Deque length size = \(size)") + + /* Check if the double-ended queue is empty */ + let isEmpty = deque.isEmpty + print("Is deque empty = \(isEmpty)") + } +} diff --git a/en/codes/swift/chapter_stack_and_queue/linkedlist_deque.swift b/en/codes/swift/chapter_stack_and_queue/linkedlist_deque.swift new file mode 100644 index 000000000..5619b84c5 --- /dev/null +++ b/en/codes/swift/chapter_stack_and_queue/linkedlist_deque.swift @@ -0,0 +1,180 @@ +/** + * File: linkedlist_deque.swift + * Created Time: 2023-02-22 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Doubly linked list node */ +class ListNode { + var val: Int // Node value + var next: ListNode? // Successor node reference + weak var prev: ListNode? // Predecessor node reference + + init(val: Int) { + self.val = val + } +} + +/* Double-ended queue based on doubly linked list implementation */ +class LinkedListDeque { + private var front: ListNode? // Head node front + private var rear: ListNode? // Tail node rear + private var _size: Int // Length of the double-ended queue + + init() { + _size = 0 + } + + /* Get the length of the double-ended queue */ + func size() -> Int { + _size + } + + /* Check if the double-ended queue is empty */ + func isEmpty() -> Bool { + size() == 0 + } + + /* Enqueue operation */ + private func push(num: Int, isFront: Bool) { + let node = ListNode(val: num) + // If the linked list is empty, make both front and rear point to node + if isEmpty() { + front = node + rear = node + } + // Front of the queue enqueue operation + else if isFront { + // Add node to the head of the linked list + front?.prev = node + node.next = front + front = node // Update head node + } + // Rear of the queue enqueue operation + else { + // Add node to the tail of the linked list + rear?.next = node + node.prev = rear + rear = node // Update tail node + } + _size += 1 // Update queue length + } + + /* Front of the queue enqueue */ + func pushFirst(num: Int) { + push(num: num, isFront: true) + } + + /* Rear of the queue enqueue */ + func pushLast(num: Int) { + push(num: num, isFront: false) + } + + /* Dequeue operation */ + private func pop(isFront: Bool) -> Int { + if isEmpty() { + fatalError("Deque is empty") + } + let val: Int + // Temporarily store head node value + if isFront { + val = front!.val // Delete head node + // Delete head node + let fNext = front?.next + if fNext != nil { + fNext?.prev = nil + front?.next = nil + } + front = fNext // Update head node + } + // Temporarily store tail node value + else { + val = rear!.val // Delete tail node + // Update tail node + let rPrev = rear?.prev + if rPrev != nil { + rPrev?.next = nil + rear?.prev = nil + } + rear = rPrev // Update tail node + } + _size -= 1 // Update queue length + return val + } + + /* Rear of the queue dequeue */ + func popFirst() -> Int { + pop(isFront: true) + } + + /* Access rear of the queue element */ + func popLast() -> Int { + pop(isFront: false) + } + + /* Return list for printing */ + func peekFirst() -> Int { + if isEmpty() { + fatalError("Deque is empty") + } + return front!.val + } + + /* Driver Code */ + func peekLast() -> Int { + if isEmpty() { + fatalError("Deque is empty") + } + return rear!.val + } + + /* Return array for printing */ + func toArray() -> [Int] { + var node = front + var res = Array(repeating: 0, count: size()) + for i in res.indices { + res[i] = node!.val + node = node?.next + } + return res + } +} + +@main +enum _LinkedListDeque { + /* Driver Code */ + static func main() { + /* Get the length of the double-ended queue */ + let deque = LinkedListDeque() + deque.pushLast(num: 3) + deque.pushLast(num: 2) + deque.pushLast(num: 5) + print("Deque deque = \(deque.toArray())") + + /* Update element */ + let peekFirst = deque.peekFirst() + print("Front element peekFirst = \(peekFirst)") + let peekLast = deque.peekLast() + print("Rear element peekLast = \(peekLast)") + + /* Elements enqueue */ + deque.pushLast(num: 4) + print("After element 4 enqueues at rear, deque = \(deque.toArray())") + deque.pushFirst(num: 1) + print("After element 1 enqueues at front, deque = \(deque.toArray())") + + /* Element dequeue */ + let popLast = deque.popLast() + print("Dequeue rear element = \(popLast), after rear dequeue deque = \(deque.toArray())") + let popFirst = deque.popFirst() + print("Dequeue front element = \(popFirst), after front dequeue deque = \(deque.toArray())") + + /* Get the length of the double-ended queue */ + let size = deque.size() + print("Deque length size = \(size)") + + /* Check if the double-ended queue is empty */ + let isEmpty = deque.isEmpty() + print("Is deque empty = \(isEmpty)") + } +} diff --git a/en/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift b/en/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift new file mode 100644 index 000000000..ab57d8d63 --- /dev/null +++ b/en/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift @@ -0,0 +1,107 @@ +/** + * File: linkedlist_queue.swift + * Created Time: 2023-01-11 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Queue based on linked list implementation */ +class LinkedListQueue { + private var front: ListNode? // Head node + private var rear: ListNode? // Tail node + private var _size: Int + + init() { + _size = 0 + } + + /* Get the length of the queue */ + func size() -> Int { + _size + } + + /* Check if the queue is empty */ + func isEmpty() -> Bool { + size() == 0 + } + + /* Enqueue */ + func push(num: Int) { + // Add num after the tail node + let node = ListNode(x: num) + // If the queue is empty, make both front and rear point to the node + if front == nil { + front = node + rear = node + } + // If the queue is not empty, add the node after the tail node + else { + rear?.next = node + rear = node + } + _size += 1 + } + + /* Dequeue */ + @discardableResult + func pop() -> Int { + let num = peek() + // Delete head node + front = front?.next + _size -= 1 + return num + } + + /* Return list for printing */ + func peek() -> Int { + if isEmpty() { + fatalError("Queue is empty") + } + return front!.val + } + + /* Convert linked list to Array and return */ + func toArray() -> [Int] { + var node = front + var res = Array(repeating: 0, count: size()) + for i in res.indices { + res[i] = node!.val + node = node?.next + } + return res + } +} + +@main +enum _LinkedListQueue { + /* Driver Code */ + static func main() { + /* Access front of the queue element */ + let queue = LinkedListQueue() + + /* Elements enqueue */ + queue.push(num: 1) + queue.push(num: 3) + queue.push(num: 2) + queue.push(num: 5) + queue.push(num: 4) + print("Queue queue = \(queue.toArray())") + + /* Return list for printing */ + let peek = queue.peek() + print("Front element peek = \(peek)") + + /* Element dequeue */ + let pop = queue.pop() + print("Dequeue element pop = \(pop), after dequeue queue = \(queue.toArray())") + + /* Get the length of the queue */ + let size = queue.size() + print("Queue length size = \(size)") + + /* Check if the queue is empty */ + let isEmpty = queue.isEmpty() + print("Is queue empty = \(isEmpty)") + } +} diff --git a/en/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift b/en/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift new file mode 100644 index 000000000..eefcc86a3 --- /dev/null +++ b/en/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift @@ -0,0 +1,96 @@ +/** + * File: linkedlist_stack.swift + * Created Time: 2023-01-09 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Stack based on linked list implementation */ +class LinkedListStack { + private var _peek: ListNode? // Use head node as stack top + private var _size: Int // Stack length + + init() { + _size = 0 + } + + /* Get the length of the stack */ + func size() -> Int { + _size + } + + /* Check if the stack is empty */ + func isEmpty() -> Bool { + size() == 0 + } + + /* Push */ + func push(num: Int) { + let node = ListNode(x: num) + node.next = _peek + _peek = node + _size += 1 + } + + /* Pop */ + @discardableResult + func pop() -> Int { + let num = peek() + _peek = _peek?.next + _size -= 1 + return num + } + + /* Return list for printing */ + func peek() -> Int { + if isEmpty() { + fatalError("Stack is empty") + } + return _peek!.val + } + + /* Convert List to Array and return */ + func toArray() -> [Int] { + var node = _peek + var res = Array(repeating: 0, count: size()) + for i in res.indices.reversed() { + res[i] = node!.val + node = node?.next + } + return res + } +} + +@main +enum _LinkedListStack { + /* Driver Code */ + static func main() { + /* Access top of the stack element */ + let stack = LinkedListStack() + + /* Elements push onto stack */ + stack.push(num: 1) + stack.push(num: 3) + stack.push(num: 2) + stack.push(num: 5) + stack.push(num: 4) + print("Stack stack = \(stack.toArray())") + + /* Return list for printing */ + let peek = stack.peek() + print("Top element peek = \(peek)") + + /* Element pop from stack */ + let pop = stack.pop() + print("Pop element pop = \(pop), after pop stack = \(stack.toArray())") + + /* Get the length of the stack */ + let size = stack.size() + print("Stack length size = \(size)") + + /* Check if empty */ + let isEmpty = stack.isEmpty() + print("Is stack empty = \(isEmpty)") + } +} diff --git a/en/codes/swift/chapter_stack_and_queue/queue.swift b/en/codes/swift/chapter_stack_and_queue/queue.swift new file mode 100644 index 000000000..3c1270346 --- /dev/null +++ b/en/codes/swift/chapter_stack_and_queue/queue.swift @@ -0,0 +1,40 @@ +/** + * File: queue.swift + * Created Time: 2023-01-11 + * Author: nuomi1 (nuomi1@qq.com) + */ + +@main +enum Queue { + /* Driver Code */ + static func main() { + /* Access front of the queue element */ + // Swift has no built-in queue class, can use Array as queue + var queue: [Int] = [] + + /* Elements enqueue */ + queue.append(1) + queue.append(3) + queue.append(2) + queue.append(5) + queue.append(4) + print("Queue queue = \(queue)") + + /* Return list for printing */ + let peek = queue.first! + print("Front element peek = \(peek)") + + /* Element dequeue */ + // When simulating with Array, pop complexity is O(n) + let pool = queue.removeFirst() + print("Dequeue element pop = \(pool), after dequeue queue = \(queue)") + + /* Get the length of the queue */ + let size = queue.count + print("Queue length size = \(size)") + + /* Check if the queue is empty */ + let isEmpty = queue.isEmpty + print("Is queue empty = \(isEmpty)") + } +} diff --git a/en/codes/swift/chapter_stack_and_queue/stack.swift b/en/codes/swift/chapter_stack_and_queue/stack.swift new file mode 100644 index 000000000..359491d06 --- /dev/null +++ b/en/codes/swift/chapter_stack_and_queue/stack.swift @@ -0,0 +1,39 @@ +/** + * File: stack.swift + * Created Time: 2023-01-09 + * Author: nuomi1 (nuomi1@qq.com) + */ + +@main +enum Stack { + /* Driver Code */ + static func main() { + /* Access top of the stack element */ + // Swift has no built-in stack class, can use Array as stack + var stack: [Int] = [] + + /* Elements push onto stack */ + stack.append(1) + stack.append(3) + stack.append(2) + stack.append(5) + stack.append(4) + print("Stack stack = \(stack)") + + /* Return list for printing */ + let peek = stack.last! + print("Top element peek = \(peek)") + + /* Element pop from stack */ + let pop = stack.removeLast() + print("Pop element pop = \(pop), after pop stack = \(stack)") + + /* Get the length of the stack */ + let size = stack.count + print("Stack length size = \(size)") + + /* Check if empty */ + let isEmpty = stack.isEmpty + print("Is stack empty = \(isEmpty)") + } +} diff --git a/en/codes/swift/chapter_tree/array_binary_tree.swift b/en/codes/swift/chapter_tree/array_binary_tree.swift new file mode 100644 index 000000000..0dea813fa --- /dev/null +++ b/en/codes/swift/chapter_tree/array_binary_tree.swift @@ -0,0 +1,141 @@ +/** + * File: array_binary_tree.swift + * Created Time: 2023-07-23 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Binary tree class represented by array */ +class ArrayBinaryTree { + private var tree: [Int?] + + /* Constructor */ + init(arr: [Int?]) { + tree = arr + } + + /* List capacity */ + func size() -> Int { + tree.count + } + + /* Get value of node at index i */ + func val(i: Int) -> Int? { + // If index out of bounds, return null to represent empty position + if i < 0 || i >= size() { + return nil + } + return tree[i] + } + + /* Get index of left child node of node at index i */ + func left(i: Int) -> Int { + 2 * i + 1 + } + + /* Get index of right child node of node at index i */ + func right(i: Int) -> Int { + 2 * i + 2 + } + + /* Get index of parent node of node at index i */ + func parent(i: Int) -> Int { + (i - 1) / 2 + } + + /* Level-order traversal */ + func levelOrder() -> [Int] { + var res: [Int] = [] + // Traverse array directly + for i in 0 ..< size() { + if let val = val(i: i) { + res.append(val) + } + } + return res + } + + /* Depth-first traversal */ + private func dfs(i: Int, order: String, res: inout [Int]) { + // If empty position, return + guard let val = val(i: i) else { + return + } + // Preorder traversal + if order == "pre" { + res.append(val) + } + dfs(i: left(i: i), order: order, res: &res) + // Inorder traversal + if order == "in" { + res.append(val) + } + dfs(i: right(i: i), order: order, res: &res) + // Postorder traversal + if order == "post" { + res.append(val) + } + } + + /* Preorder traversal */ + func preOrder() -> [Int] { + var res: [Int] = [] + dfs(i: 0, order: "pre", res: &res) + return res + } + + /* Inorder traversal */ + func inOrder() -> [Int] { + var res: [Int] = [] + dfs(i: 0, order: "in", res: &res) + return res + } + + /* Postorder traversal */ + func postOrder() -> [Int] { + var res: [Int] = [] + dfs(i: 0, order: "post", res: &res) + return res + } +} + +@main +enum _ArrayBinaryTree { + /* Driver Code */ + static func main() { + // Initialize binary tree + // Here we use a function to generate a binary tree directly from an array + let arr = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] + + let root = TreeNode.listToTree(arr: arr) + print("\nInitialize binary tree\n") + print("Array representation of binary tree:") + print(arr) + print("Linked list representation of binary tree:") + PrintUtil.printTree(root: root) + + // Binary tree class represented by array + let abt = ArrayBinaryTree(arr: arr) + + // Access node + let i = 1 + let l = abt.left(i: i) + let r = abt.right(i: i) + let p = abt.parent(i: i) + print("\nCurrent node index is \(i), value is \(abt.val(i: i) as Any)") + print("Its left child index is \(l), value is \(abt.val(i: l) as Any)") + print("Its right child index is \(r), value is \(abt.val(i: r) as Any)") + print("Its parent node index is \(p), value is \(abt.val(i: p) as Any)") + + // Traverse tree + var res = abt.levelOrder() + print("\nLevel-order traversal is: \(res)") + res = abt.preOrder() + print("Pre-order traversal is: \(res)") + res = abt.inOrder() + print("In-order traversal is: \(res)") + res = abt.postOrder() + print("Post-order traversal is: \(res)") + } +} diff --git a/en/codes/swift/chapter_tree/avl_tree.swift b/en/codes/swift/chapter_tree/avl_tree.swift new file mode 100644 index 000000000..fd70a8c63 --- /dev/null +++ b/en/codes/swift/chapter_tree/avl_tree.swift @@ -0,0 +1,230 @@ +/** + * File: avl_tree.swift + * Created Time: 2023-01-28 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* AVL tree */ +class AVLTree { + fileprivate var root: TreeNode? // Root node + + init() {} + + /* Get node height */ + func height(node: TreeNode?) -> Int { + // Empty node height is -1, leaf node height is 0 + node?.height ?? -1 + } + + /* Update node height */ + private func updateHeight(node: TreeNode?) { + // Node height equals the height of the tallest subtree + 1 + node?.height = max(height(node: node?.left), height(node: node?.right)) + 1 + } + + /* Get balance factor */ + func balanceFactor(node: TreeNode?) -> Int { + // Empty node balance factor is 0 + guard let node = node else { return 0 } + // Node balance factor = left subtree height - right subtree height + return height(node: node.left) - height(node: node.right) + } + + /* Right rotation operation */ + private func rightRotate(node: TreeNode?) -> TreeNode? { + let child = node?.left + let grandChild = child?.right + // Using child as pivot, rotate node to the right + child?.right = node + node?.left = grandChild + // Update node height + updateHeight(node: node) + updateHeight(node: child) + // Return root node of subtree after rotation + return child + } + + /* Left rotation operation */ + private func leftRotate(node: TreeNode?) -> TreeNode? { + let child = node?.right + let grandChild = child?.left + // Using child as pivot, rotate node to the left + child?.left = node + node?.right = grandChild + // Update node height + updateHeight(node: node) + updateHeight(node: child) + // Return root node of subtree after rotation + return child + } + + /* Perform rotation operation to restore balance to this subtree */ + private func rotate(node: TreeNode?) -> TreeNode? { + // Get balance factor of node + let balanceFactor = balanceFactor(node: node) + // Left-leaning tree + if balanceFactor > 1 { + if self.balanceFactor(node: node?.left) >= 0 { + // Right rotation + return rightRotate(node: node) + } else { + // First left rotation then right rotation + node?.left = leftRotate(node: node?.left) + return rightRotate(node: node) + } + } + // Right-leaning tree + if balanceFactor < -1 { + if self.balanceFactor(node: node?.right) <= 0 { + // Left rotation + return leftRotate(node: node) + } else { + // First right rotation then left rotation + node?.right = rightRotate(node: node?.right) + return leftRotate(node: node) + } + } + // Balanced tree, no rotation needed, return directly + return node + } + + /* Insert node */ + func insert(val: Int) { + root = insertHelper(node: root, val: val) + } + + /* Recursively insert node (helper method) */ + private func insertHelper(node: TreeNode?, val: Int) -> TreeNode? { + var node = node + if node == nil { + return TreeNode(x: val) + } + /* 1. Find insertion position and insert node */ + if val < node!.val { + node?.left = insertHelper(node: node?.left, val: val) + } else if val > node!.val { + node?.right = insertHelper(node: node?.right, val: val) + } else { + return node // Duplicate node not inserted, return directly + } + updateHeight(node: node) // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = rotate(node: node) + // Return root node of subtree + return node + } + + /* Remove node */ + func remove(val: Int) { + root = removeHelper(node: root, val: val) + } + + /* Recursively delete node (helper method) */ + private func removeHelper(node: TreeNode?, val: Int) -> TreeNode? { + var node = node + if node == nil { + return nil + } + /* 1. Find node and delete */ + if val < node!.val { + node?.left = removeHelper(node: node?.left, val: val) + } else if val > node!.val { + node?.right = removeHelper(node: node?.right, val: val) + } else { + if node?.left == nil || node?.right == nil { + let child = node?.left ?? node?.right + // Number of child nodes = 0, delete node directly and return + if child == nil { + return nil + } + // Number of child nodes = 1, delete node directly + else { + node = child + } + } else { + // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + var temp = node?.right + while temp?.left != nil { + temp = temp?.left + } + node?.right = removeHelper(node: node?.right, val: temp!.val) + node?.val = temp!.val + } + } + updateHeight(node: node) // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = rotate(node: node) + // Return root node of subtree + return node + } + + /* Search node */ + func search(val: Int) -> TreeNode? { + var cur = root + while cur != nil { + // Target node is in cur's right subtree + if cur!.val < val { + cur = cur?.right + } + // Target node is in cur's left subtree + else if cur!.val > val { + cur = cur?.left + } + // Found target node, exit loop + else { + break + } + } + // Return target node + return cur + } +} + +@main +enum _AVLTree { + static func testInsert(tree: AVLTree, val: Int) { + tree.insert(val: val) + print("\nAfter inserting node \(val), AVL tree is") + PrintUtil.printTree(root: tree.root) + } + + static func testRemove(tree: AVLTree, val: Int) { + tree.remove(val: val) + print("\nAfter deleting node \(val), AVL tree is") + PrintUtil.printTree(root: tree.root) + } + + /* Driver Code */ + static func main() { + /* Please pay attention to how the AVL tree maintains balance after inserting nodes */ + let avlTree = AVLTree() + + /* Insert node */ + // Delete nodes + testInsert(tree: avlTree, val: 1) + testInsert(tree: avlTree, val: 2) + testInsert(tree: avlTree, val: 3) + testInsert(tree: avlTree, val: 4) + testInsert(tree: avlTree, val: 5) + testInsert(tree: avlTree, val: 8) + testInsert(tree: avlTree, val: 7) + testInsert(tree: avlTree, val: 9) + testInsert(tree: avlTree, val: 10) + testInsert(tree: avlTree, val: 6) + + /* Please pay attention to how the AVL tree maintains balance after deleting nodes */ + testInsert(tree: avlTree, val: 7) + + /* Remove node */ + // Delete node with degree 1 + testRemove(tree: avlTree, val: 8) // Delete node with degree 2 + testRemove(tree: avlTree, val: 5) // Remove node with degree 1 + testRemove(tree: avlTree, val: 4) // Remove node with degree 2 + + /* Search node */ + let node = avlTree.search(val: 7) + print("\nFound node object is \(node!), node value = \(node!.val)") + } +} diff --git a/en/codes/swift/chapter_tree/binary_search_tree.swift b/en/codes/swift/chapter_tree/binary_search_tree.swift new file mode 100644 index 000000000..918fc0ca3 --- /dev/null +++ b/en/codes/swift/chapter_tree/binary_search_tree.swift @@ -0,0 +1,173 @@ +/** + * File: binary_search_tree.swift + * Created Time: 2023-01-26 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Binary search tree */ +class BinarySearchTree { + private var root: TreeNode? + + /* Constructor */ + init() { + // Initialize empty tree + root = nil + } + + /* Get binary tree root node */ + func getRoot() -> TreeNode? { + root + } + + /* Search node */ + func search(num: Int) -> TreeNode? { + var cur = root + // Loop search, exit after passing leaf node + while cur != nil { + // Target node is in cur's right subtree + if cur!.val < num { + cur = cur?.right + } + // Target node is in cur's left subtree + else if cur!.val > num { + cur = cur?.left + } + // Found target node, exit loop + else { + break + } + } + // Return target node + return cur + } + + /* Insert node */ + func insert(num: Int) { + // If tree is empty, initialize root node + if root == nil { + root = TreeNode(x: num) + return + } + var cur = root + var pre: TreeNode? + // Loop search, exit after passing leaf node + while cur != nil { + // Found duplicate node, return directly + if cur!.val == num { + return + } + pre = cur + // Insertion position is in cur's right subtree + if cur!.val < num { + cur = cur?.right + } + // Insertion position is in cur's left subtree + else { + cur = cur?.left + } + } + // Insert node + let node = TreeNode(x: num) + if pre!.val < num { + pre?.right = node + } else { + pre?.left = node + } + } + + /* Remove node */ + func remove(num: Int) { + // If tree is empty, return directly + if root == nil { + return + } + var cur = root + var pre: TreeNode? + // Loop search, exit after passing leaf node + while cur != nil { + // Found node to delete, exit loop + if cur!.val == num { + break + } + pre = cur + // Node to delete is in cur's right subtree + if cur!.val < num { + cur = cur?.right + } + // Node to delete is in cur's left subtree + else { + cur = cur?.left + } + } + // If no node to delete, return directly + if cur == nil { + return + } + // Number of child nodes = 0 or 1 + if cur?.left == nil || cur?.right == nil { + // When number of child nodes = 0 / 1, child = null / that child node + let child = cur?.left ?? cur?.right + // Delete node cur + if cur !== root { + if pre?.left === cur { + pre?.left = child + } else { + pre?.right = child + } + } else { + // If deleted node is root node, reassign root node + root = child + } + } + // Number of child nodes = 2 + else { + // Get next node of cur in inorder traversal + var tmp = cur?.right + while tmp?.left != nil { + tmp = tmp?.left + } + // Recursively delete node tmp + remove(num: tmp!.val) + // Replace cur with tmp + cur?.val = tmp!.val + } + } +} + +@main +enum _BinarySearchTree { + /* Driver Code */ + static func main() { + /* Initialize binary search tree */ + let bst = BinarySearchTree() + // Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree + let nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15] + for num in nums { + bst.insert(num: num) + } + print("\nInitialized binary tree is\n") + PrintUtil.printTree(root: bst.getRoot()) + + /* Search node */ + let node = bst.search(num: 7) + print("\nFound node object is \(node!), node value = \(node!.val)") + + /* Insert node */ + bst.insert(num: 16) + print("\nAfter inserting node 16, binary tree is\n") + PrintUtil.printTree(root: bst.getRoot()) + + /* Remove node */ + bst.remove(num: 1) + print("\nAfter removing node 1, binary tree is\n") + PrintUtil.printTree(root: bst.getRoot()) + bst.remove(num: 2) + print("\nAfter removing node 2, binary tree is\n") + PrintUtil.printTree(root: bst.getRoot()) + bst.remove(num: 4) + print("\nAfter removing node 4, binary tree is\n") + PrintUtil.printTree(root: bst.getRoot()) + } +} diff --git a/en/codes/swift/chapter_tree/binary_tree.swift b/en/codes/swift/chapter_tree/binary_tree.swift new file mode 100644 index 000000000..f4ba90f25 --- /dev/null +++ b/en/codes/swift/chapter_tree/binary_tree.swift @@ -0,0 +1,40 @@ +/** + * File: binary_tree.swift + * Created Time: 2023-01-18 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +@main +enum BinaryTree { + /* Driver Code */ + static func main() { + /* Initialize binary tree */ + // Initialize nodes + let n1 = TreeNode(x: 1) + let n2 = TreeNode(x: 2) + let n3 = TreeNode(x: 3) + let n4 = TreeNode(x: 4) + let n5 = TreeNode(x: 5) + // Build references (pointers) between nodes + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 + print("\nInitialize binary tree\n") + PrintUtil.printTree(root: n1) + + /* Insert node P between n1 -> n2 */ + let P = TreeNode(x: 0) + // Delete node + n1.left = P + P.left = n2 + print("\nAfter inserting node P\n") + PrintUtil.printTree(root: n1) + // Remove node P + n1.left = n2 + print("\nAfter removing node P\n") + PrintUtil.printTree(root: n1) + } +} diff --git a/en/codes/swift/chapter_tree/binary_tree_bfs.swift b/en/codes/swift/chapter_tree/binary_tree_bfs.swift new file mode 100644 index 000000000..6d2b2bffa --- /dev/null +++ b/en/codes/swift/chapter_tree/binary_tree_bfs.swift @@ -0,0 +1,42 @@ +/** + * File: binary_tree_bfs.swift + * Created Time: 2023-01-18 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* Level-order traversal */ +func levelOrder(root: TreeNode) -> [Int] { + // Initialize queue, add root node + var queue: [TreeNode] = [root] + // Initialize a list to save the traversal sequence + var list: [Int] = [] + while !queue.isEmpty { + let node = queue.removeFirst() // Dequeue + list.append(node.val) // Save node value + if let left = node.left { + queue.append(left) // Left child node enqueue + } + if let right = node.right { + queue.append(right) // Right child node enqueue + } + } + return list +} + +@main +enum BinaryTreeBFS { + /* Driver Code */ + static func main() { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + let node = TreeNode.listToTree(arr: [1, 2, 3, 4, 5, 6, 7])! + print("\nInitialize binary tree\n") + PrintUtil.printTree(root: node) + + /* Level-order traversal */ + let list = levelOrder(root: node) + print("\nLevel-order traversal node print sequence = \(list)") + } +} diff --git a/en/codes/swift/chapter_tree/binary_tree_dfs.swift b/en/codes/swift/chapter_tree/binary_tree_dfs.swift new file mode 100644 index 000000000..6ce977943 --- /dev/null +++ b/en/codes/swift/chapter_tree/binary_tree_dfs.swift @@ -0,0 +1,70 @@ +/** + * File: binary_tree_dfs.swift + * Created Time: 2023-01-18 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +// Initialize list for storing traversal sequence +var list: [Int] = [] + +/* Preorder traversal */ +func preOrder(root: TreeNode?) { + guard let root = root else { + return + } + // Visit priority: root node -> left subtree -> right subtree + list.append(root.val) + preOrder(root: root.left) + preOrder(root: root.right) +} + +/* Inorder traversal */ +func inOrder(root: TreeNode?) { + guard let root = root else { + return + } + // Visit priority: left subtree -> root node -> right subtree + inOrder(root: root.left) + list.append(root.val) + inOrder(root: root.right) +} + +/* Postorder traversal */ +func postOrder(root: TreeNode?) { + guard let root = root else { + return + } + // Visit priority: left subtree -> right subtree -> root node + postOrder(root: root.left) + postOrder(root: root.right) + list.append(root.val) +} + +@main +enum BinaryTreeDFS { + /* Driver Code */ + static func main() { + /* Initialize binary tree */ + // Here we use a function to generate a binary tree directly from an array + let root = TreeNode.listToTree(arr: [1, 2, 3, 4, 5, 6, 7])! + print("\nInitialize binary tree\n") + PrintUtil.printTree(root: root) + + /* Preorder traversal */ + list.removeAll() + preOrder(root: root) + print("\nPre-order traversal node print sequence = \(list)") + + /* Inorder traversal */ + list.removeAll() + inOrder(root: root) + print("\nIn-order traversal node print sequence = \(list)") + + /* Postorder traversal */ + list.removeAll() + postOrder(root: root) + print("\nPost-order traversal node print sequence = \(list)") + } +} diff --git a/en/codes/swift/utils/ListNode.swift b/en/codes/swift/utils/ListNode.swift new file mode 100644 index 000000000..ffa47de12 --- /dev/null +++ b/en/codes/swift/utils/ListNode.swift @@ -0,0 +1,33 @@ +/** + * File: ListNode.swift + * Created Time: 2023-01-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +public class ListNode: Hashable { + public var val: Int // Node value + public var next: ListNode? // Successor node reference + + public init(x: Int) { + val = x + } + + public static func == (lhs: ListNode, rhs: ListNode) -> Bool { + lhs.val == rhs.val && lhs.next.map { ObjectIdentifier($0) } == rhs.next.map { ObjectIdentifier($0) } + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(val) + hasher.combine(next.map { ObjectIdentifier($0) }) + } + + public static func arrToLinkedList(arr: [Int]) -> ListNode? { + let dum = ListNode(x: 0) + var head: ListNode? = dum + for val in arr { + head?.next = ListNode(x: val) + head = head?.next + } + return dum.next + } +} diff --git a/en/codes/swift/utils/Pair.swift b/en/codes/swift/utils/Pair.swift new file mode 100644 index 000000000..05559ecd6 --- /dev/null +++ b/en/codes/swift/utils/Pair.swift @@ -0,0 +1,20 @@ +/** + * File: Pair.swift + * Created Time: 2023-06-28 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Key-value pair */ +public class Pair: Equatable { + public var key: Int + public var val: String + + public init(key: Int, val: String) { + self.key = key + self.val = val + } + + public static func == (lhs: Pair, rhs: Pair) -> Bool { + lhs.key == rhs.key && lhs.val == rhs.val + } +} diff --git a/en/codes/swift/utils/PrintUtil.swift b/en/codes/swift/utils/PrintUtil.swift new file mode 100644 index 000000000..c2662634e --- /dev/null +++ b/en/codes/swift/utils/PrintUtil.swift @@ -0,0 +1,93 @@ +/** + * File: PrintUtil.swift + * Created Time: 2023-01-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +public enum PrintUtil { + private class Trunk { + var prev: Trunk? + var str: String + + init(prev: Trunk?, str: String) { + self.prev = prev + self.str = str + } + } + + public static func printLinkedList(head: ListNode) { + var head: ListNode? = head + var list: [String] = [] + while head != nil { + list.append("\(head!.val)") + head = head?.next + } + print(list.joined(separator: " -> ")) + } + + public static func printTree(root: TreeNode?) { + printTree(root: root, prev: nil, isRight: false) + } + + private static func printTree(root: TreeNode?, prev: Trunk?, isRight: Bool) { + if root == nil { + return + } + + var prevStr = " " + let trunk = Trunk(prev: prev, str: prevStr) + + printTree(root: root?.right, prev: trunk, isRight: true) + + if prev == nil { + trunk.str = "———" + } else if isRight { + trunk.str = "/———" + prevStr = " |" + } else { + trunk.str = "\\———" + prev?.str = prevStr + } + + showTrunks(p: trunk) + print(" \(root!.val)") + + if prev != nil { + prev?.str = prevStr + } + trunk.str = " |" + + printTree(root: root?.left, prev: trunk, isRight: false) + } + + private static func showTrunks(p: Trunk?) { + if p == nil { + return + } + + showTrunks(p: p?.prev) + print(p!.str, terminator: "") + } + + public static func printHashMap(map: [K: V]) { + for (key, value) in map { + print("\(key) -> \(value)") + } + } + + public static func printHeap(queue: [Int]) { + print("Heap array representation:", terminator: "") + print(queue) + print("Heap tree representation:") + let root = TreeNode.listToTree(arr: queue) + printTree(root: root) + } + + public static func printMatrix(matrix: [[T]]) { + print("[") + for row in matrix { + print(" \(row),") + } + print("]") + } +} diff --git a/en/codes/swift/utils/TreeNode.swift b/en/codes/swift/utils/TreeNode.swift new file mode 100644 index 000000000..4e8c48f28 --- /dev/null +++ b/en/codes/swift/utils/TreeNode.swift @@ -0,0 +1,71 @@ +/** + * File: TreeNode.swift + * Created Time: 2023-01-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Binary tree node class */ +public class TreeNode { + public var val: Int // Node value + public var height: Int // Node height + public var left: TreeNode? // Reference to left child node + public var right: TreeNode? // Reference to right child node + + /* Constructor */ + public init(x: Int) { + val = x + height = 0 + } + + // For the serialization encoding rules, please refer to: + // https://www.hello-algo.com/chapter_tree/array_representation_of_tree/ + // Array representation of binary tree: + // [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] + // Linked list representation of binary tree: + // /——— 15 + // /——— 7 + // /——— 3 + // | \——— 6 + // | \——— 12 + // ——— 1 + // \——— 2 + // | /——— 9 + // \——— 4 + // \——— 8 + + /* Deserialize a list into a binary tree: recursion */ + private static func listToTreeDFS(arr: [Int?], i: Int) -> TreeNode? { + if i < 0 || i >= arr.count || arr[i] == nil { + return nil + } + let root = TreeNode(x: arr[i]!) + root.left = listToTreeDFS(arr: arr, i: 2 * i + 1) + root.right = listToTreeDFS(arr: arr, i: 2 * i + 2) + return root + } + + /* Deserialize a list into a binary tree */ + public static func listToTree(arr: [Int?]) -> TreeNode? { + listToTreeDFS(arr: arr, i: 0) + } + + /* Serialize a binary tree into a list: recursion */ + private static func treeToListDFS(root: TreeNode?, i: Int, res: inout [Int?]) { + if root == nil { + return + } + while i >= res.count { + res.append(nil) + } + res[i] = root?.val + treeToListDFS(root: root?.left, i: 2 * i + 1, res: &res) + treeToListDFS(root: root?.right, i: 2 * i + 2, res: &res) + } + + /* Serialize a binary tree into a list */ + public static func treeToList(root: TreeNode?) -> [Int?] { + var res: [Int?] = [] + treeToListDFS(root: root, i: 0, res: &res) + return res + } +} diff --git a/en/codes/swift/utils/Vertex.swift b/en/codes/swift/utils/Vertex.swift new file mode 100644 index 000000000..fdc3c9a9e --- /dev/null +++ b/en/codes/swift/utils/Vertex.swift @@ -0,0 +1,32 @@ +/** + * File: Vertex.swift + * Created Time: 2023-02-19 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* Vertex class */ +public class Vertex: Hashable { + public var val: Int + + public init(val: Int) { + self.val = val + } + + public static func == (lhs: Vertex, rhs: Vertex) -> Bool { + lhs.val == rhs.val + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(val) + } + + /* Input value list vals, return vertex list vets */ + public static func valsToVets(vals: [Int]) -> [Vertex] { + vals.map { Vertex(val: $0) } + } + + /* Input vertex list vets, return value list vals */ + public static func vetsToVals(vets: [Vertex]) -> [Int] { + vets.map { $0.val } + } +} diff --git a/en/codes/typescript/.gitignore b/en/codes/typescript/.gitignore new file mode 100644 index 000000000..d5f19d89b --- /dev/null +++ b/en/codes/typescript/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json diff --git a/en/codes/typescript/.prettierrc b/en/codes/typescript/.prettierrc new file mode 100644 index 000000000..3f4aa8cb6 --- /dev/null +++ b/en/codes/typescript/.prettierrc @@ -0,0 +1,6 @@ +{ + "tabWidth": 4, + "useTabs": false, + "semi": true, + "singleQuote": true +} diff --git a/en/codes/typescript/chapter_array_and_linkedlist/array.ts b/en/codes/typescript/chapter_array_and_linkedlist/array.ts new file mode 100644 index 000000000..f775320e0 --- /dev/null +++ b/en/codes/typescript/chapter_array_and_linkedlist/array.ts @@ -0,0 +1,101 @@ +/** + * File: array.ts + * Created Time: 2022-12-04 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Random access to element */ +function randomAccess(nums: number[]): number { + // Randomly select a number in the interval [0, nums.length) + const random_index = Math.floor(Math.random() * nums.length); + // Retrieve and return the random element + const random_num = nums[random_index]; + return random_num; +} + +/* Extend array length */ +// Note: TypeScript's Array is dynamic array, can be directly expanded +// For learning purposes, this function treats Array as fixed-length array +function extend(nums: number[], enlarge: number): number[] { + // Initialize an array with extended length + const res = new Array(nums.length + enlarge).fill(0); + // Copy all elements from the original array to the new array + for (let i = 0; i < nums.length; i++) { + res[i] = nums[i]; + } + // Return the extended new array + return res; +} + +/* Insert element num at index index in the array */ +function insert(nums: number[], num: number, index: number): void { + // Move all elements at and after index index backward by one position + for (let i = nums.length - 1; i > index; i--) { + nums[i] = nums[i - 1]; + } + // Assign num to the element at index index + nums[index] = num; +} + +/* Remove the element at index index */ +function remove(nums: number[], index: number): void { + // Move all elements after index index forward by one position + for (let i = index; i < nums.length - 1; i++) { + nums[i] = nums[i + 1]; + } +} + +/* Traverse array */ +function traverse(nums: number[]): void { + let count = 0; + // Traverse array by index + for (let i = 0; i < nums.length; i++) { + count += nums[i]; + } + // Direct traversal of array elements + for (const num of nums) { + count += num; + } +} + +/* Find the specified element in the array */ +function find(nums: number[], target: number): number { + for (let i = 0; i < nums.length; i++) { + if (nums[i] === target) { + return i; + } + } + return -1; +} + +/* Driver Code */ +/* Initialize array */ +const arr: number[] = new Array(5).fill(0); +console.log('Array arr =', arr); +let nums: number[] = [1, 3, 2, 5, 4]; +console.log('Array nums =', nums); + +/* Insert element */ +let random_num = randomAccess(nums); +console.log('Get random element in nums', random_num); + +/* Traverse array */ +nums = extend(nums, 3); +console.log('Extend array length to 8, get nums =', nums); + +/* Insert element */ +insert(nums, 6, 3); +console.log('Insert number 6 at index 3, get nums =', nums); + +/* Remove element */ +remove(nums, 2); +console.log('Remove element at index 2, get nums =', nums); + +/* Traverse array */ +traverse(nums); + +/* Find element */ +let index = find(nums, 3); +console.log('Find element 3 in nums, get index =', index); + +export {}; diff --git a/en/codes/typescript/chapter_array_and_linkedlist/linked_list.ts b/en/codes/typescript/chapter_array_and_linkedlist/linked_list.ts new file mode 100644 index 000000000..44c431b3f --- /dev/null +++ b/en/codes/typescript/chapter_array_and_linkedlist/linked_list.ts @@ -0,0 +1,86 @@ +/** + * File: linked_list.ts + * Created Time: 2022-12-10 + * Author: Justin (xiefahit@gmail.com) + */ + +import { ListNode } from '../modules/ListNode'; +import { printLinkedList } from '../modules/PrintUtil'; + +/* Insert node P after node n0 in the linked list */ +function insert(n0: ListNode, P: ListNode): void { + const n1 = n0.next; + P.next = n1; + n0.next = P; +} + +/* Remove the first node after node n0 in the linked list */ +function remove(n0: ListNode): void { + if (!n0.next) { + return; + } + // n0 -> P -> n1 + const P = n0.next; + const n1 = P.next; + n0.next = n1; +} + +/* Access the node at index index in the linked list */ +function access(head: ListNode | null, index: number): ListNode | null { + for (let i = 0; i < index; i++) { + if (!head) { + return null; + } + head = head.next; + } + return head; +} + +/* Find the first node with value target in the linked list */ +function find(head: ListNode | null, target: number): number { + let index = 0; + while (head !== null) { + if (head.val === target) { + return index; + } + head = head.next; + index += 1; + } + return -1; +} + +/* Driver Code */ +/* Initialize linked list */ +// Initialize each node +const n0 = new ListNode(1); +const n1 = new ListNode(3); +const n2 = new ListNode(2); +const n3 = new ListNode(5); +const n4 = new ListNode(4); +// Build references between nodes +n0.next = n1; +n1.next = n2; +n2.next = n3; +n3.next = n4; +console.log('Initialized linked list is'); +printLinkedList(n0); + +/* Insert node */ +insert(n0, new ListNode(0)); +console.log('Linked list after inserting node is'); +printLinkedList(n0); + +/* Remove node */ +remove(n0); +console.log('Linked list after removing node is'); +printLinkedList(n0); + +/* Access node */ +const node = access(n0, 3); +console.log(`Value of node at index 3 in linked list = ${node?.val}`); + +/* Search node */ +const index = find(n0, 2); +console.log(`Index of node with value 2 in linked list = ${index}`); + +export {}; diff --git a/en/codes/typescript/chapter_array_and_linkedlist/list.ts b/en/codes/typescript/chapter_array_and_linkedlist/list.ts new file mode 100644 index 000000000..a94f04de6 --- /dev/null +++ b/en/codes/typescript/chapter_array_and_linkedlist/list.ts @@ -0,0 +1,59 @@ +/** + * File: list.ts + * Created Time: 2022-12-10 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Initialize list */ +const nums: number[] = [1, 3, 2, 5, 4]; +console.log(`List nums = ${nums}`); + +/* Update element */ +const num: number = nums[1]; +console.log(`Access element at index 1, get num = ${num}`); + +/* Add elements at the end */ +nums[1] = 0; +console.log(`Update element at index 1 to 0, get nums = ${nums}`); + +/* Remove element */ +nums.length = 0; +console.log(`After clearing list, nums = ${nums}`); + +/* Direct traversal of list elements */ +nums.push(1); +nums.push(3); +nums.push(2); +nums.push(5); +nums.push(4); +console.log(`After adding elements, nums = ${nums}`); + +/* Sort list */ +nums.splice(3, 0, 6); +console.log(`Insert number 6 at index 3, get nums = ${nums}`); + +/* Remove element */ +nums.splice(3, 1); +console.log(`Delete element at index 3, get nums = ${nums}`); + +/* Traverse list by index */ +let count = 0; +for (let i = 0; i < nums.length; i++) { + count += nums[i]; +} +/* Directly traverse list elements */ +count = 0; +for (const x of nums) { + count += x; +} + +/* Concatenate two lists */ +const nums1: number[] = [6, 8, 7, 10, 9]; +nums.push(...nums1); +console.log(`After concatenating list nums1 to nums, get nums = ${nums}`); + +/* Sort list */ +nums.sort((a, b) => a - b); +console.log(`After sorting list, nums = ${nums}`); + +export {}; diff --git a/en/codes/typescript/chapter_array_and_linkedlist/my_list.ts b/en/codes/typescript/chapter_array_and_linkedlist/my_list.ts new file mode 100644 index 000000000..4407efc82 --- /dev/null +++ b/en/codes/typescript/chapter_array_and_linkedlist/my_list.ts @@ -0,0 +1,141 @@ +/** + * File: my_list.ts + * Created Time: 2022-12-11 + * Author: Justin (xiefahit@gmail.com) + */ + +/* List class */ +class MyList { + private arr: Array; // Array (stores list elements) + private _capacity: number = 10; // List capacity + private _size: number = 0; // List length (current number of elements) + private extendRatio: number = 2; // Multiple by which the list capacity is extended each time + + /* Constructor */ + constructor() { + this.arr = new Array(this._capacity); + } + + /* Get list length (current number of elements) */ + public size(): number { + return this._size; + } + + /* Get list capacity */ + public capacity(): number { + return this._capacity; + } + + /* Update element */ + public get(index: number): number { + // If the index is out of bounds, throw an exception, as below + if (index < 0 || index >= this._size) throw new Error('Index out of bounds'); + return this.arr[index]; + } + + /* Add elements at the end */ + public set(index: number, num: number): void { + if (index < 0 || index >= this._size) throw new Error('Index out of bounds'); + this.arr[index] = num; + } + + /* Direct traversal of list elements */ + public add(num: number): void { + // If length equals capacity, need to expand + if (this._size === this._capacity) this.extendCapacity(); + // Add new element to end of list + this.arr[this._size] = num; + this._size++; + } + + /* Sort list */ + public insert(index: number, num: number): void { + if (index < 0 || index >= this._size) throw new Error('Index out of bounds'); + // When the number of elements exceeds capacity, trigger the extension mechanism + if (this._size === this._capacity) { + this.extendCapacity(); + } + // Move all elements after index index forward by one position + for (let j = this._size - 1; j >= index; j--) { + this.arr[j + 1] = this.arr[j]; + } + // Update the number of elements + this.arr[index] = num; + this._size++; + } + + /* Remove element */ + public remove(index: number): number { + if (index < 0 || index >= this._size) throw new Error('Index out of bounds'); + let num = this.arr[index]; + // Move all elements after index forward by one position + for (let j = index; j < this._size - 1; j++) { + this.arr[j] = this.arr[j + 1]; + } + // Update the number of elements + this._size--; + // Return the removed element + return num; + } + + /* Driver Code */ + public extendCapacity(): void { + // Create new array of length size and copy original array to new array + this.arr = this.arr.concat( + new Array(this.capacity() * (this.extendRatio - 1)) + ); + // Add elements at the end + this._capacity = this.arr.length; + } + + /* Convert list to array */ + public toArray(): number[] { + let size = this.size(); + // Elements enqueue + const arr = new Array(size); + for (let i = 0; i < size; i++) { + arr[i] = this.get(i); + } + return arr; + } +} + +/* Driver Code */ +/* Initialize list */ +const nums = new MyList(); +/* Direct traversal of list elements */ +nums.add(1); +nums.add(3); +nums.add(2); +nums.add(5); +nums.add(4); +console.log( + `List nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}` +); + +/* Sort list */ +nums.insert(3, 6); +console.log(`Insert number 6 at index 3, get nums = ${nums.toArray()}`); + +/* Remove element */ +nums.remove(3); +console.log(`Delete element at index 3, get nums = ${nums.toArray()}`); + +/* Update element */ +const num = nums.get(1); +console.log(`Access element at index 1, get num = ${num}`); + +/* Add elements at the end */ +nums.set(1, 0); +console.log(`Update element at index 1 to 0, get nums = ${nums.toArray()}`); + +/* Test capacity expansion mechanism */ +for (let i = 0; i < 10; i++) { + // At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism + nums.add(i); +} +console.log( + `After expansion, list nums = ${nums.toArray()}, capacity = ${nums.capacity()}, length = ${nums.size()}` +); + +export {}; diff --git a/en/codes/typescript/chapter_backtracking/n_queens.ts b/en/codes/typescript/chapter_backtracking/n_queens.ts new file mode 100644 index 000000000..33cbb231d --- /dev/null +++ b/en/codes/typescript/chapter_backtracking/n_queens.ts @@ -0,0 +1,65 @@ +/** + * File: n_queens.ts + * Created Time: 2023-05-13 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Backtracking algorithm: N queens */ +function backtrack( + row: number, + n: number, + state: string[][], + res: string[][][], + cols: boolean[], + diags1: boolean[], + diags2: boolean[] +): void { + // When all rows are placed, record the solution + if (row === n) { + res.push(state.map((row) => row.slice())); + return; + } + // Traverse all columns + for (let col = 0; col < n; col++) { + // Calculate the main diagonal and anti-diagonal corresponding to this cell + const diag1 = row - col + n - 1; + const diag2 = row + col; + // Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell + if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { + // Attempt: place the queen in this cell + state[row][col] = 'Q'; + cols[col] = diags1[diag1] = diags2[diag2] = true; + // Place the next row + backtrack(row + 1, n, state, res, cols, diags1, diags2); + // Backtrack: restore this cell to an empty cell + state[row][col] = '#'; + cols[col] = diags1[diag1] = diags2[diag2] = false; + } + } +} + +/* Solve N queens */ +function nQueens(n: number): string[][][] { + // Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell + const state = Array.from({ length: n }, () => Array(n).fill('#')); + const cols = Array(n).fill(false); // Record whether there is a queen in the column + const diags1 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the main diagonal + const diags2 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the anti-diagonal + const res: string[][][] = []; + + backtrack(0, n, state, res, cols, diags1, diags2); + return res; +} + +// Driver Code +const n = 4; +const res = nQueens(n); + +console.log(`Input board size is ${n}`); +console.log(`Total queen placement solutions: ${res.length}`); +res.forEach((state) => { + console.log('--------------------'); + state.forEach((row) => console.log(row)); +}); + +export {}; diff --git a/en/codes/typescript/chapter_backtracking/permutations_i.ts b/en/codes/typescript/chapter_backtracking/permutations_i.ts new file mode 100644 index 000000000..2d0828ab5 --- /dev/null +++ b/en/codes/typescript/chapter_backtracking/permutations_i.ts @@ -0,0 +1,49 @@ +/** + * File: permutations_i.ts + * Created Time: 2023-05-13 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Backtracking algorithm: Permutations I */ +function backtrack( + state: number[], + choices: number[], + selected: boolean[], + res: number[][] +): void { + // When the state length equals the number of elements, record the solution + if (state.length === choices.length) { + res.push([...state]); + return; + } + // Traverse all choices + choices.forEach((choice, i) => { + // Pruning: do not allow repeated selection of elements + if (!selected[i]) { + // Attempt: make choice, update state + selected[i] = true; + state.push(choice); + // Proceed to the next round of selection + backtrack(state, choices, selected, res); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + state.pop(); + } + }); +} + +/* Permutations I */ +function permutationsI(nums: number[]): number[][] { + const res: number[][] = []; + backtrack([], nums, Array(nums.length).fill(false), res); + return res; +} + +// Driver Code +const nums: number[] = [1, 2, 3]; +const res: number[][] = permutationsI(nums); + +console.log(`Input array nums = ${JSON.stringify(nums)}`); +console.log(`All permutations res = ${JSON.stringify(res)}`); + +export {}; diff --git a/en/codes/typescript/chapter_backtracking/permutations_ii.ts b/en/codes/typescript/chapter_backtracking/permutations_ii.ts new file mode 100644 index 000000000..df2544bdd --- /dev/null +++ b/en/codes/typescript/chapter_backtracking/permutations_ii.ts @@ -0,0 +1,51 @@ +/** + * File: permutations_ii.ts + * Created Time: 2023-05-13 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Backtracking algorithm: Permutations II */ +function backtrack( + state: number[], + choices: number[], + selected: boolean[], + res: number[][] +): void { + // When the state length equals the number of elements, record the solution + if (state.length === choices.length) { + res.push([...state]); + return; + } + // Traverse all choices + const duplicated = new Set(); + choices.forEach((choice, i) => { + // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements + if (!selected[i] && !duplicated.has(choice)) { + // Attempt: make choice, update state + duplicated.add(choice); // Record the selected element value + selected[i] = true; + state.push(choice); + // Proceed to the next round of selection + backtrack(state, choices, selected, res); + // Backtrack: undo choice, restore to previous state + selected[i] = false; + state.pop(); + } + }); +} + +/* Permutations II */ +function permutationsII(nums: number[]): number[][] { + const res: number[][] = []; + backtrack([], nums, Array(nums.length).fill(false), res); + return res; +} + +// Driver Code +const nums: number[] = [1, 2, 2]; +const res: number[][] = permutationsII(nums); + +console.log(`Input array nums = ${JSON.stringify(nums)}`); +console.log(`All permutations res = ${JSON.stringify(res)}`); + +export {}; diff --git a/en/codes/typescript/chapter_backtracking/preorder_traversal_i_compact.ts b/en/codes/typescript/chapter_backtracking/preorder_traversal_i_compact.ts new file mode 100644 index 000000000..39fbe1d6e --- /dev/null +++ b/en/codes/typescript/chapter_backtracking/preorder_traversal_i_compact.ts @@ -0,0 +1,36 @@ +/** + * File: preorder_traversal_i_compact.ts + * Created Time: 2023-05-09 + * Author: Justin (xiefahit@gmail.com) + */ + +import { type TreeNode } from '../modules/TreeNode'; +import { arrToTree } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; + +/* Preorder traversal: Example 1 */ +function preOrder(root: TreeNode | null, res: TreeNode[]): void { + if (root === null) { + return; + } + if (root.val === 7) { + // Record solution + res.push(root); + } + preOrder(root.left, res); + preOrder(root.right, res); +} + +// Driver Code +const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree'); +printTree(root); + +// Preorder traversal +const res: TreeNode[] = []; +preOrder(root, res); + +console.log('\nOutput all nodes with value 7'); +console.log(res.map((node) => node.val)); + +export {}; diff --git a/en/codes/typescript/chapter_backtracking/preorder_traversal_ii_compact.ts b/en/codes/typescript/chapter_backtracking/preorder_traversal_ii_compact.ts new file mode 100644 index 000000000..0508a4f91 --- /dev/null +++ b/en/codes/typescript/chapter_backtracking/preorder_traversal_ii_compact.ts @@ -0,0 +1,47 @@ +/** + * File: preorder_traversal_ii_compact.ts + * Created Time: 2023-05-09 + * Author: Justin (xiefahit@gmail.com) + */ + +import { type TreeNode } from '../modules/TreeNode'; +import { arrToTree } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; + +/* Preorder traversal: Example 2 */ +function preOrder( + root: TreeNode | null, + path: TreeNode[], + res: TreeNode[][] +): void { + if (root === null) { + return; + } + // Attempt + path.push(root); + if (root.val === 7) { + // Record solution + res.push([...path]); + } + preOrder(root.left, path, res); + preOrder(root.right, path, res); + // Backtrack + path.pop(); +} + +// Driver Code +const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree'); +printTree(root); + +// Preorder traversal +const path: TreeNode[] = []; +const res: TreeNode[][] = []; +preOrder(root, path, res); + +console.log('\nOutput all paths from root node to node 7'); +res.forEach((path) => { + console.log(path.map((node) => node.val)); +}); + +export {}; diff --git a/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_compact.ts b/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_compact.ts new file mode 100644 index 000000000..b8dfe14f7 --- /dev/null +++ b/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_compact.ts @@ -0,0 +1,48 @@ +/** + * File: preorder_traversal_iii_compact.ts + * Created Time: 2023-05-09 + * Author: Justin (xiefahit@gmail.com) + */ + +import { type TreeNode } from '../modules/TreeNode'; +import { arrToTree } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; + +/* Preorder traversal: Example 3 */ +function preOrder( + root: TreeNode | null, + path: TreeNode[], + res: TreeNode[][] +): void { + // Pruning + if (root === null || root.val === 3) { + return; + } + // Attempt + path.push(root); + if (root.val === 7) { + // Record solution + res.push([...path]); + } + preOrder(root.left, path, res); + preOrder(root.right, path, res); + // Backtrack + path.pop(); +} + +// Driver Code +const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree'); +printTree(root); + +// Preorder traversal +const path: TreeNode[] = []; +const res: TreeNode[][] = []; +preOrder(root, path, res); + +console.log('\nOutput all paths from root node to node 7, paths do not include nodes with value 3'); +res.forEach((path) => { + console.log(path.map((node) => node.val)); +}); + +export {}; diff --git a/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_template.ts b/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_template.ts new file mode 100644 index 000000000..3ac4c38b5 --- /dev/null +++ b/en/codes/typescript/chapter_backtracking/preorder_traversal_iii_template.ts @@ -0,0 +1,75 @@ +/** + * File: preorder_traversal_iii_template.ts + * Created Time: 2023-05-09 + * Author: Justin (xiefahit@gmail.com) + */ + +import { type TreeNode } from '../modules/TreeNode'; +import { arrToTree } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; + +/* Check if the current state is a solution */ +function isSolution(state: TreeNode[]): boolean { + return state && state[state.length - 1]?.val === 7; +} + +/* Record solution */ +function recordSolution(state: TreeNode[], res: TreeNode[][]): void { + res.push([...state]); +} + +/* Check if the choice is valid under the current state */ +function isValid(state: TreeNode[], choice: TreeNode): boolean { + return choice !== null && choice.val !== 3; +} + +/* Update state */ +function makeChoice(state: TreeNode[], choice: TreeNode): void { + state.push(choice); +} + +/* Restore state */ +function undoChoice(state: TreeNode[]): void { + state.pop(); +} + +/* Backtracking algorithm: Example 3 */ +function backtrack( + state: TreeNode[], + choices: TreeNode[], + res: TreeNode[][] +): void { + // Check if it is a solution + if (isSolution(state)) { + // Record solution + recordSolution(state, res); + } + // Traverse all choices + for (const choice of choices) { + // Pruning: check if the choice is valid + if (isValid(state, choice)) { + // Attempt: make choice, update state + makeChoice(state, choice); + // Proceed to the next round of selection + backtrack(state, [choice.left, choice.right], res); + // Backtrack: undo choice, restore to previous state + undoChoice(state); + } + } +} + +// Driver Code +const root = arrToTree([1, 7, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree'); +printTree(root); + +// Backtracking algorithm +const res: TreeNode[][] = []; +backtrack([], [root], res); + +console.log('\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3'); +res.forEach((path) => { + console.log(path.map((node) => node.val)); +}); + +export {}; diff --git a/en/codes/typescript/chapter_backtracking/subset_sum_i.ts b/en/codes/typescript/chapter_backtracking/subset_sum_i.ts new file mode 100644 index 000000000..ed0fde0f9 --- /dev/null +++ b/en/codes/typescript/chapter_backtracking/subset_sum_i.ts @@ -0,0 +1,54 @@ +/** + * File: subset_sum_i.ts + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Backtracking algorithm: Subset sum I */ +function backtrack( + state: number[], + target: number, + choices: number[], + start: number, + res: number[][] +): void { + // When the subset sum equals target, record the solution + if (target === 0) { + res.push([...state]); + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + for (let i = start; i < choices.length; i++) { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if (target - choices[i] < 0) { + break; + } + // Attempt: make choice, update target, start + state.push(choices[i]); + // Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i, res); + // Backtrack: undo choice, restore to previous state + state.pop(); + } +} + +/* Solve subset sum I */ +function subsetSumI(nums: number[], target: number): number[][] { + const state = []; // State (subset) + nums.sort((a, b) => a - b); // Sort nums + const start = 0; // Start point for traversal + const res = []; // Result list (subset list) + backtrack(state, target, nums, start, res); + return res; +} + +/* Driver Code */ +const nums = [3, 4, 5]; +const target = 9; +const res = subsetSumI(nums, target); +console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); +console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); + +export {}; diff --git a/en/codes/typescript/chapter_backtracking/subset_sum_i_naive.ts b/en/codes/typescript/chapter_backtracking/subset_sum_i_naive.ts new file mode 100644 index 000000000..c5d8d1249 --- /dev/null +++ b/en/codes/typescript/chapter_backtracking/subset_sum_i_naive.ts @@ -0,0 +1,52 @@ +/** + * File: subset_sum_i_naive.ts + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Backtracking algorithm: Subset sum I */ +function backtrack( + state: number[], + target: number, + total: number, + choices: number[], + res: number[][] +): void { + // When the subset sum equals target, record the solution + if (total === target) { + res.push([...state]); + return; + } + // Traverse all choices + for (let i = 0; i < choices.length; i++) { + // Pruning: if the subset sum exceeds target, skip this choice + if (total + choices[i] > target) { + continue; + } + // Attempt: make choice, update element sum total + state.push(choices[i]); + // Proceed to the next round of selection + backtrack(state, target, total + choices[i], choices, res); + // Backtrack: undo choice, restore to previous state + state.pop(); + } +} + +/* Solve subset sum I (including duplicate subsets) */ +function subsetSumINaive(nums: number[], target: number): number[][] { + const state = []; // State (subset) + const total = 0; // Subset sum + const res = []; // Result list (subset list) + backtrack(state, target, total, nums, res); + return res; +} + +/* Driver Code */ +const nums = [3, 4, 5]; +const target = 9; +const res = subsetSumINaive(nums, target); +console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); +console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); +console.log('Please note that this method outputs results containing duplicate sets'); + +export {}; diff --git a/en/codes/typescript/chapter_backtracking/subset_sum_ii.ts b/en/codes/typescript/chapter_backtracking/subset_sum_ii.ts new file mode 100644 index 000000000..b5449fee4 --- /dev/null +++ b/en/codes/typescript/chapter_backtracking/subset_sum_ii.ts @@ -0,0 +1,59 @@ +/** + * File: subset_sum_ii.ts + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Backtracking algorithm: Subset sum II */ +function backtrack( + state: number[], + target: number, + choices: number[], + start: number, + res: number[][] +): void { + // When the subset sum equals target, record the solution + if (target === 0) { + res.push([...state]); + return; + } + // Traverse all choices + // Pruning 2: start traversing from start to avoid generating duplicate subsets + // Pruning 3: start traversing from start to avoid repeatedly selecting the same element + for (let i = start; i < choices.length; i++) { + // Pruning 1: if the subset sum exceeds target, end the loop directly + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target + if (target - choices[i] < 0) { + break; + } + // Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly + if (i > start && choices[i] === choices[i - 1]) { + continue; + } + // Attempt: make choice, update target, start + state.push(choices[i]); + // Proceed to the next round of selection + backtrack(state, target - choices[i], choices, i + 1, res); + // Backtrack: undo choice, restore to previous state + state.pop(); + } +} + +/* Solve subset sum II */ +function subsetSumII(nums: number[], target: number): number[][] { + const state = []; // State (subset) + nums.sort((a, b) => a - b); // Sort nums + const start = 0; // Start point for traversal + const res = []; // Result list (subset list) + backtrack(state, target, nums, start, res); + return res; +} + +/* Driver Code */ +const nums = [4, 4, 5]; +const target = 9; +const res = subsetSumII(nums, target); +console.log(`Input array nums = ${JSON.stringify(nums)}, target = ${target}`); +console.log(`All subsets with sum equal to ${target} res = ${JSON.stringify(res)}`); + +export {}; diff --git a/en/codes/typescript/chapter_computational_complexity/iteration.ts b/en/codes/typescript/chapter_computational_complexity/iteration.ts new file mode 100644 index 000000000..5fd3dfc74 --- /dev/null +++ b/en/codes/typescript/chapter_computational_complexity/iteration.ts @@ -0,0 +1,72 @@ +/** + * File: iteration.ts + * Created Time: 2023-08-28 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* for loop */ +function forLoop(n: number): number { + let res = 0; + // Sum 1, 2, ..., n-1, n + for (let i = 1; i <= n; i++) { + res += i; + } + return res; +} + +/* while loop */ +function whileLoop(n: number): number { + let res = 0; + let i = 1; // Initialize condition variable + // Sum 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // Update condition variable + } + return res; +} + +/* while loop (two updates) */ +function whileLoopII(n: number): number { + let res = 0; + let i = 1; // Initialize condition variable + // Sum 1, 4, 10, ... + while (i <= n) { + res += i; + // Update condition variable + i++; + i *= 2; + } + return res; +} + +/* Nested for loop */ +function nestedForLoop(n: number): string { + let res = ''; + // Loop i = 1, 2, ..., n-1, n + for (let i = 1; i <= n; i++) { + // Loop j = 1, 2, ..., n-1, n + for (let j = 1; j <= n; j++) { + res += `(${i}, ${j}), `; + } + } + return res; +} + +/* Driver Code */ +const n = 5; +let res: number; + +res = forLoop(n); +console.log(`For loop sum result res = ${res}`); + +res = whileLoop(n); +console.log(`While loop sum result res = ${res}`); + +res = whileLoopII(n); +console.log(`While loop (two updates) sum result res = ${res}`); + +const resStr = nestedForLoop(n); +console.log(`Nested for loop traversal result ${resStr}`); + +export {}; diff --git a/en/codes/typescript/chapter_computational_complexity/recursion.ts b/en/codes/typescript/chapter_computational_complexity/recursion.ts new file mode 100644 index 000000000..d2b1de167 --- /dev/null +++ b/en/codes/typescript/chapter_computational_complexity/recursion.ts @@ -0,0 +1,70 @@ +/** + * File: recursion.ts + * Created Time: 2023-08-28 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Recursion */ +function recur(n: number): number { + // Termination condition + if (n === 1) return 1; + // Recurse: recursive call + const res = recur(n - 1); + // Return: return result + return n + res; +} + +/* Simulate recursion using iteration */ +function forLoopRecur(n: number): number { + // Use an explicit stack to simulate the system call stack + const stack: number[] = []; + let res: number = 0; + // Recurse: recursive call + for (let i = n; i > 0; i--) { + // Simulate "recurse" with "push" + stack.push(i); + } + // Return: return result + while (stack.length) { + // Simulate "return" with "pop" + res += stack.pop(); + } + // res = 1+2+3+...+n + return res; +} + +/* Tail recursion */ +function tailRecur(n: number, res: number): number { + // Termination condition + if (n === 0) return res; + // Tail recursive call + return tailRecur(n - 1, res + n); +} + +/* Fibonacci sequence: recursion */ +function fib(n: number): number { + // Termination condition f(1) = 0, f(2) = 1 + if (n === 1 || n === 2) return n - 1; + // Recursive call f(n) = f(n-1) + f(n-2) + const res = fib(n - 1) + fib(n - 2); + // Return result f(n) + return res; +} + +/* Driver Code */ +const n = 5; +let res: number; + +res = recur(n); +console.log(`Recursion sum result res = ${res}`); + +res = forLoopRecur(n); +console.log(`Using iteration to simulate recursion sum result res = ${res}`); + +res = tailRecur(n, 0); +console.log(`Tail recursion sum result res = ${res}`); + +res = fib(n); +console.log(`The ${n}th Fibonacci number is ${res}`); + +export {}; diff --git a/en/codes/typescript/chapter_computational_complexity/space_complexity.ts b/en/codes/typescript/chapter_computational_complexity/space_complexity.ts new file mode 100644 index 000000000..1e772bca6 --- /dev/null +++ b/en/codes/typescript/chapter_computational_complexity/space_complexity.ts @@ -0,0 +1,103 @@ +/** + * File: space_complexity.ts + * Created Time: 2023-02-05 + * Author: Justin (xiefahit@gmail.com) + */ + +import { ListNode } from '../modules/ListNode'; +import { TreeNode } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; + +/* Function */ +function constFunc(): number { + // Perform some operations + return 0; +} + +/* Constant order */ +function constant(n: number): void { + // Constants, variables, objects occupy O(1) space + const a = 0; + const b = 0; + const nums = new Array(10000); + const node = new ListNode(0); + // Variables in the loop occupy O(1) space + for (let i = 0; i < n; i++) { + const c = 0; + } + // Functions in the loop occupy O(1) space + for (let i = 0; i < n; i++) { + constFunc(); + } +} + +/* Linear order */ +function linear(n: number): void { + // Array of length n uses O(n) space + const nums = new Array(n); + // A list of length n occupies O(n) space + const nodes: ListNode[] = []; + for (let i = 0; i < n; i++) { + nodes.push(new ListNode(i)); + } + // A hash table of length n occupies O(n) space + const map = new Map(); + for (let i = 0; i < n; i++) { + map.set(i, i.toString()); + } +} + +/* Linear order (recursive implementation) */ +function linearRecur(n: number): void { + console.log(`Recursion n = ${n}`); + if (n === 1) return; + linearRecur(n - 1); +} + +/* Exponential order */ +function quadratic(n: number): void { + // Matrix uses O(n^2) space + const numMatrix = Array(n) + .fill(null) + .map(() => Array(n).fill(null)); + // 2D list uses O(n^2) space + const numList = []; + for (let i = 0; i < n; i++) { + const tmp = []; + for (let j = 0; j < n; j++) { + tmp.push(0); + } + numList.push(tmp); + } +} + +/* Quadratic order (recursive implementation) */ +function quadraticRecur(n: number): number { + if (n <= 0) return 0; + const nums = new Array(n); + console.log(`In recursion n = ${n}, nums length = ${nums.length}`); + return quadraticRecur(n - 1); +} + +/* Driver Code */ +function buildTree(n: number): TreeNode | null { + if (n === 0) return null; + const root = new TreeNode(0); + root.left = buildTree(n - 1); + root.right = buildTree(n - 1); + return root; +} + +/* Driver Code */ +const n = 5; +// Constant order +constant(n); +// Linear order +linear(n); +linearRecur(n); +// Exponential order +quadratic(n); +quadraticRecur(n); +// Exponential order +const root = buildTree(n); +printTree(root); diff --git a/en/codes/typescript/chapter_computational_complexity/time_complexity.ts b/en/codes/typescript/chapter_computational_complexity/time_complexity.ts new file mode 100644 index 000000000..94fcc9128 --- /dev/null +++ b/en/codes/typescript/chapter_computational_complexity/time_complexity.ts @@ -0,0 +1,157 @@ +/** + * File: time_complexity.ts + * Created Time: 2023-01-02 + * Author: RiverTwilight (contact@rene.wang) + */ + +/* Constant order */ +function constant(n: number): number { + let count = 0; + const size = 100000; + for (let i = 0; i < size; i++) count++; + return count; +} + +/* Linear order */ +function linear(n: number): number { + let count = 0; + for (let i = 0; i < n; i++) count++; + return count; +} + +/* Linear order (traversing array) */ +function arrayTraversal(nums: number[]): number { + let count = 0; + // Number of iterations is proportional to the array length + for (let i = 0; i < nums.length; i++) { + count++; + } + return count; +} + +/* Exponential order */ +function quadratic(n: number): number { + let count = 0; + // Number of iterations is quadratically related to the data size n + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + count++; + } + } + return count; +} + +/* Quadratic order (bubble sort) */ +function bubbleSort(nums: number[]): number { + let count = 0; // Counter + // Outer loop: unsorted range is [0, i] + for (let i = nums.length - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // Element swap includes 3 unit operations + } + } + } + return count; +} + +/* Exponential order (loop implementation) */ +function exponential(n: number): number { + let count = 0, + base = 1; + // Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1) + for (let i = 0; i < n; i++) { + for (let j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; +} + +/* Exponential order (recursive implementation) */ +function expRecur(n: number): number { + if (n === 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; +} + +/* Logarithmic order (loop implementation) */ +function logarithmic(n: number): number { + let count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; +} + +/* Logarithmic order (recursive implementation) */ +function logRecur(n: number): number { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; +} + +/* Linearithmic order */ +function linearLogRecur(n: number): number { + if (n <= 1) return 1; + let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (let i = 0; i < n; i++) { + count++; + } + return count; +} + +/* Factorial order (recursive implementation) */ +function factorialRecur(n: number): number { + if (n === 0) return 1; + let count = 0; + // Split from 1 into n + for (let i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; +} + +/* Driver Code */ +// You can modify n to run and observe the trend of the number of operations for various complexities +const n = 8; +console.log('Input data size n = ' + n); + +let count = constant(n); +console.log('Constant order operation count = ' + count); + +count = linear(n); +console.log('Linear order operation count = ' + count); +count = arrayTraversal(new Array(n)); +console.log('Linear order (array traversal) operation count = ' + count); + +count = quadratic(n); +console.log('Quadratic order operation count = ' + count); +var nums = new Array(n); +for (let i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] +count = bubbleSort(nums); +console.log('Quadratic order (bubble sort) operation count = ' + count); + +count = exponential(n); +console.log('Exponential order (loop implementation) operation count = ' + count); +count = expRecur(n); +console.log('Exponential order (recursive implementation) operation count = ' + count); + +count = logarithmic(n); +console.log('Logarithmic order (loop implementation) operation count = ' + count); +count = logRecur(n); +console.log('Logarithmic order (recursive implementation) operation count = ' + count); + +count = linearLogRecur(n); +console.log('Linearithmic order (recursive implementation) operation count = ' + count); + +count = factorialRecur(n); +console.log('Factorial order (recursive implementation) operation count = ' + count); + +export {}; diff --git a/en/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts b/en/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts new file mode 100644 index 000000000..d95614b4a --- /dev/null +++ b/en/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts @@ -0,0 +1,45 @@ +/** + * File: worst_best_time_complexity.ts + * Created Time: 2023-01-05 + * Author: RiverTwilight (contact@rene.wang) + */ + +/* Generate an array with elements { 1, 2, ..., n }, order shuffled */ +function randomNumbers(n: number): number[] { + const nums = Array(n); + // Generate array nums = { 1, 2, 3, ..., n } + for (let i = 0; i < n; i++) { + nums[i] = i + 1; + } + // Randomly shuffle array elements + for (let i = 0; i < n; i++) { + const r = Math.floor(Math.random() * (i + 1)); + const temp = nums[i]; + nums[i] = nums[r]; + nums[r] = temp; + } + return nums; +} + +/* Find the index of number 1 in array nums */ +function findOne(nums: number[]): number { + for (let i = 0; i < nums.length; i++) { + // When element 1 is at the head of the array, best time complexity O(1) is achieved + // When element 1 is at the tail of the array, worst time complexity O(n) is achieved + if (nums[i] === 1) { + return i; + } + } + return -1; +} + +/* Driver Code */ +for (let i = 0; i < 10; i++) { + const n = 100; + const nums = randomNumbers(n); + const index = findOne(nums); + console.log('\nArray [ 1, 2, ..., n ] after shuffling = [' + nums.join(', ') + ']'); + console.log('Index of number 1 is ' + index); +} + +export {}; diff --git a/en/codes/typescript/chapter_divide_and_conquer/binary_search_recur.ts b/en/codes/typescript/chapter_divide_and_conquer/binary_search_recur.ts new file mode 100644 index 000000000..482358356 --- /dev/null +++ b/en/codes/typescript/chapter_divide_and_conquer/binary_search_recur.ts @@ -0,0 +1,41 @@ +/** + * File: binary_search_recur.ts + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Binary search: problem f(i, j) */ +function dfs(nums: number[], target: number, i: number, j: number): number { + // If the interval is empty, it means there is no target element, return -1 + if (i > j) { + return -1; + } + // Calculate the midpoint index m + const m = i + ((j - i) >> 1); + if (nums[m] < target) { + // Recursion subproblem f(m+1, j) + return dfs(nums, target, m + 1, j); + } else if (nums[m] > target) { + // Recursion subproblem f(i, m-1) + return dfs(nums, target, i, m - 1); + } else { + // Found the target element, return its index + return m; + } +} + +/* Binary search */ +function binarySearch(nums: number[], target: number): number { + const n = nums.length; + // Solve the problem f(0, n-1) + return dfs(nums, target, 0, n - 1); +} + +/* Driver Code */ +const target = 6; +const nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; +// Binary search (closed interval on both sides) +const index = binarySearch(nums, target); +console.log(`Index of target element 6 is ${index}`); + +export {}; diff --git a/en/codes/typescript/chapter_divide_and_conquer/build_tree.ts b/en/codes/typescript/chapter_divide_and_conquer/build_tree.ts new file mode 100644 index 000000000..3221a3f2c --- /dev/null +++ b/en/codes/typescript/chapter_divide_and_conquer/build_tree.ts @@ -0,0 +1,50 @@ +/** + * File: build_tree.ts + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +import { printTree } from '../modules/PrintUtil'; +import { TreeNode } from '../modules/TreeNode'; + +/* Build binary tree: divide and conquer */ +function dfs( + preorder: number[], + inorderMap: Map, + i: number, + l: number, + r: number +): TreeNode | null { + // Terminate when the subtree interval is empty + if (r - l < 0) return null; + // Initialize the root node + const root: TreeNode = new TreeNode(preorder[i]); + // Query m to divide the left and right subtrees + const m = inorderMap.get(preorder[i]); + // Subproblem: build the left subtree + root.left = dfs(preorder, inorderMap, i + 1, l, m - 1); + // Subproblem: build the right subtree + root.right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r); + // Return the root node + return root; +} + +/* Build binary tree */ +function buildTree(preorder: number[], inorder: number[]): TreeNode | null { + // Initialize hash map, storing the mapping from inorder elements to indices + let inorderMap = new Map(); + for (let i = 0; i < inorder.length; i++) { + inorderMap.set(inorder[i], i); + } + const root = dfs(preorder, inorderMap, 0, 0, inorder.length - 1); + return root; +} + +/* Driver Code */ +const preorder = [3, 9, 2, 1, 7]; +const inorder = [9, 3, 1, 2, 7]; +console.log('Preorder traversal = ' + JSON.stringify(preorder)); +console.log('Inorder traversal = ' + JSON.stringify(inorder)); +const root = buildTree(preorder, inorder); +console.log('The constructed binary tree is:'); +printTree(root); diff --git a/en/codes/typescript/chapter_divide_and_conquer/hanota.ts b/en/codes/typescript/chapter_divide_and_conquer/hanota.ts new file mode 100644 index 000000000..0739de439 --- /dev/null +++ b/en/codes/typescript/chapter_divide_and_conquer/hanota.ts @@ -0,0 +1,52 @@ +/** + * File: hanota.ts + * Created Time: 2023-07-30 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Move a disk */ +function move(src: number[], tar: number[]): void { + // Take out a disk from the top of src + const pan = src.pop(); + // Place the disk on top of tar + tar.push(pan); +} + +/* Solve the Tower of Hanoi problem f(i) */ +function dfs(i: number, src: number[], buf: number[], tar: number[]): void { + // If there is only one disk left in src, move it directly to tar + if (i === 1) { + move(src, tar); + return; + } + // Subproblem f(i-1): move the top i-1 disks from src to buf using tar + dfs(i - 1, src, tar, buf); + // Subproblem f(1): move the remaining disk from src to tar + move(src, tar); + // Subproblem f(i-1): move the top i-1 disks from buf to tar using src + dfs(i - 1, buf, src, tar); +} + +/* Solve the Tower of Hanoi problem */ +function solveHanota(A: number[], B: number[], C: number[]): void { + const n = A.length; + // Move the top n disks from A to C using B + dfs(n, A, B, C); +} + +/* Driver Code */ +// The tail of the list is the top of the rod +const A = [5, 4, 3, 2, 1]; +const B = []; +const C = []; +console.log('In initial state:'); +console.log(`A = ${JSON.stringify(A)}`); +console.log(`B = ${JSON.stringify(B)}`); +console.log(`C = ${JSON.stringify(C)}`); + +solveHanota(A, B, C); + +console.log('After disk movement is complete:'); +console.log(`A = ${JSON.stringify(A)}`); +console.log(`B = ${JSON.stringify(B)}`); +console.log(`C = ${JSON.stringify(C)}`); diff --git a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_backtrack.ts b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_backtrack.ts new file mode 100644 index 000000000..6582417cb --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_backtrack.ts @@ -0,0 +1,41 @@ +/** + * File: climbing_stairs_backtrack.ts + * Created Time: 2023-07-26 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Backtracking */ +function backtrack( + choices: number[], + state: number, + n: number, + res: Map<0, any> +): void { + // When climbing to the n-th stair, add 1 to the solution count + if (state === n) res.set(0, res.get(0) + 1); + // Traverse all choices + for (const choice of choices) { + // Pruning: not allowed to go beyond the n-th stair + if (state + choice > n) continue; + // Attempt: make choice, update state + backtrack(choices, state + choice, n, res); + // Backtrack + } +} + +/* Climbing stairs: Backtracking */ +function climbingStairsBacktrack(n: number): number { + const choices = [1, 2]; // Can choose to climb up 1 or 2 stairs + const state = 0; // Start climbing from the 0-th stair + const res = new Map(); + res.set(0, 0); // Use res[0] to record the solution count + backtrack(choices, state, n, res); + return res.get(0); +} + +/* Driver Code */ +const n = 9; +const res = climbingStairsBacktrack(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); + +export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_constraint_dp.ts b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_constraint_dp.ts new file mode 100644 index 000000000..6e4a3c162 --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_constraint_dp.ts @@ -0,0 +1,32 @@ +/** + * File: climbing_stairs_constraint_dp.ts + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Climbing stairs with constraint: Dynamic programming */ +function climbingStairsConstraintDP(n: number): number { + if (n === 1 || n === 2) { + return 1; + } + // Initialize dp table, used to store solutions to subproblems + const dp = Array.from({ length: n + 1 }, () => new Array(3)); + // Initial state: preset the solution to the smallest subproblem + dp[1][1] = 1; + dp[1][2] = 0; + dp[2][1] = 0; + dp[2][2] = 1; + // State transition: gradually solve larger subproblems from smaller ones + for (let i = 3; i <= n; i++) { + dp[i][1] = dp[i - 1][2]; + dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; + } + return dp[n][1] + dp[n][2]; +} + +/* Driver Code */ +const n = 9; +const res = climbingStairsConstraintDP(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); + +export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs.ts b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs.ts new file mode 100644 index 000000000..97411f158 --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs.ts @@ -0,0 +1,26 @@ +/** + * File: climbing_stairs_dfs.ts + * Created Time: 2023-07-26 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Search */ +function dfs(i: number): number { + // Known dp[1] and dp[2], return them + if (i === 1 || i === 2) return i; + // dp[i] = dp[i-1] + dp[i-2] + const count = dfs(i - 1) + dfs(i - 2); + return count; +} + +/* Climbing stairs: Search */ +function climbingStairsDFS(n: number): number { + return dfs(n); +} + +/* Driver Code */ +const n = 9; +const res = climbingStairsDFS(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); + +export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs_mem.ts b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs_mem.ts new file mode 100644 index 000000000..9bce393bf --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dfs_mem.ts @@ -0,0 +1,32 @@ +/** + * File: climbing_stairs_dfs_mem.ts + * Created Time: 2023-07-26 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Memoization search */ +function dfs(i: number, mem: number[]): number { + // Known dp[1] and dp[2], return them + if (i === 1 || i === 2) return i; + // If record dp[i] exists, return it directly + if (mem[i] != -1) return mem[i]; + // dp[i] = dp[i-1] + dp[i-2] + const count = dfs(i - 1, mem) + dfs(i - 2, mem); + // Record dp[i] + mem[i] = count; + return count; +} + +/* Climbing stairs: Memoization search */ +function climbingStairsDFSMem(n: number): number { + // mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record + const mem = new Array(n + 1).fill(-1); + return dfs(n, mem); +} + +/* Driver Code */ +const n = 9; +const res = climbingStairsDFSMem(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); + +export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dp.ts b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dp.ts new file mode 100644 index 000000000..896aa7f7b --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/climbing_stairs_dp.ts @@ -0,0 +1,42 @@ +/** + * File: climbing_stairs_dp.ts + * Created Time: 2023-07-26 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Climbing stairs: Dynamic programming */ +function climbingStairsDP(n: number): number { + if (n === 1 || n === 2) return n; + // Initialize dp table, used to store solutions to subproblems + const dp = new Array(n + 1).fill(-1); + // Initial state: preset the solution to the smallest subproblem + dp[1] = 1; + dp[2] = 2; + // State transition: gradually solve larger subproblems from smaller ones + for (let i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; +} + +/* Climbing stairs: Space-optimized dynamic programming */ +function climbingStairsDPComp(n: number): number { + if (n === 1 || n === 2) return n; + let a = 1, + b = 2; + for (let i = 3; i <= n; i++) { + const tmp = b; + b = a + b; + a = tmp; + } + return b; +} + +/* Driver Code */ +const n = 9; +let res = climbingStairsDP(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); +res = climbingStairsDPComp(n); +console.log(`Climbing ${n} stairs has ${res} solutions`); + +export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/coin_change.ts b/en/codes/typescript/chapter_dynamic_programming/coin_change.ts new file mode 100644 index 000000000..9af6fc82a --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/coin_change.ts @@ -0,0 +1,68 @@ +/** + * File: coin_change.ts + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Coin change: Dynamic programming */ +function coinChangeDP(coins: Array, amt: number): number { + const n = coins.length; + const MAX = amt + 1; + // Initialize dp table + const dp = Array.from({ length: n + 1 }, () => + Array.from({ length: amt + 1 }, () => 0) + ); + // State transition: first row and first column + for (let a = 1; a <= amt; a++) { + dp[0][a] = MAX; + } + // State transition: rest of the rows and columns + for (let i = 1; i <= n; i++) { + for (let a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[i][a] = Math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); + } + } + } + return dp[n][amt] !== MAX ? dp[n][amt] : -1; +} + +/* Coin change: Space-optimized dynamic programming */ +function coinChangeDPComp(coins: Array, amt: number): number { + const n = coins.length; + const MAX = amt + 1; + // Initialize dp table + const dp = Array.from({ length: amt + 1 }, () => MAX); + dp[0] = 0; + // State transition + for (let i = 1; i <= n; i++) { + for (let a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // The smaller value between not selecting and selecting coin i + dp[a] = Math.min(dp[a], dp[a - coins[i - 1]] + 1); + } + } + } + return dp[amt] !== MAX ? dp[amt] : -1; +} + +/* Driver Code */ +const coins = [1, 2, 5]; +const amt = 4; + +// Dynamic programming +let res = coinChangeDP(coins, amt); +console.log(`Minimum coins needed to make target amount is ${res}`); + +// Space-optimized dynamic programming +res = coinChangeDPComp(coins, amt); +console.log(`Minimum coins needed to make target amount is ${res}`); + +export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/coin_change_ii.ts b/en/codes/typescript/chapter_dynamic_programming/coin_change_ii.ts new file mode 100644 index 000000000..3f3c3a1ba --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/coin_change_ii.ts @@ -0,0 +1,66 @@ +/** + * File: coin_change_ii.ts + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Coin change II: Dynamic programming */ +function coinChangeIIDP(coins: Array, amt: number): number { + const n = coins.length; + // Initialize dp table + const dp = Array.from({ length: n + 1 }, () => + Array.from({ length: amt + 1 }, () => 0) + ); + // Initialize first column + for (let i = 0; i <= n; i++) { + dp[i][0] = 1; + } + // State transition + for (let i = 1; i <= n; i++) { + for (let a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[i][a] = dp[i - 1][a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; + } + } + } + return dp[n][amt]; +} + +/* Coin change II: Space-optimized dynamic programming */ +function coinChangeIIDPComp(coins: Array, amt: number): number { + const n = coins.length; + // Initialize dp table + const dp = Array.from({ length: amt + 1 }, () => 0); + dp[0] = 1; + // State transition + for (let i = 1; i <= n; i++) { + for (let a = 1; a <= amt; a++) { + if (coins[i - 1] > a) { + // If exceeds target amount, don't select coin i + dp[a] = dp[a]; + } else { + // Sum of the two options: not selecting and selecting coin i + dp[a] = dp[a] + dp[a - coins[i - 1]]; + } + } + } + return dp[amt]; +} + +/* Driver Code */ +const coins = [1, 2, 5]; +const amt = 5; + +// Dynamic programming +let res = coinChangeIIDP(coins, amt); +console.log(`Number of coin combinations to make target amount is ${res}`); + +// Space-optimized dynamic programming +res = coinChangeIIDPComp(coins, amt); +console.log(`Number of coin combinations to make target amount is ${res}`); + +export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/edit_distance.ts b/en/codes/typescript/chapter_dynamic_programming/edit_distance.ts new file mode 100644 index 000000000..5c1b7ce20 --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/edit_distance.ts @@ -0,0 +1,148 @@ +/** + * File: edit_distance.ts + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Edit distance: Brute-force search */ +function editDistanceDFS(s: string, t: string, i: number, j: number): number { + // If both s and t are empty, return 0 + if (i === 0 && j === 0) return 0; + + // If s is empty, return length of t + if (i === 0) return j; + + // If t is empty, return length of s + if (j === 0) return i; + + // If two characters are equal, skip both characters + if (s.charAt(i - 1) === t.charAt(j - 1)) + return editDistanceDFS(s, t, i - 1, j - 1); + + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + const insert = editDistanceDFS(s, t, i, j - 1); + const del = editDistanceDFS(s, t, i - 1, j); + const replace = editDistanceDFS(s, t, i - 1, j - 1); + // Return minimum edit steps + return Math.min(insert, del, replace) + 1; +} + +/* Edit distance: Memoization search */ +function editDistanceDFSMem( + s: string, + t: string, + mem: Array>, + i: number, + j: number +): number { + // If both s and t are empty, return 0 + if (i === 0 && j === 0) return 0; + + // If s is empty, return length of t + if (i === 0) return j; + + // If t is empty, return length of s + if (j === 0) return i; + + // If there's a record, return it directly + if (mem[i][j] !== -1) return mem[i][j]; + + // If two characters are equal, skip both characters + if (s.charAt(i - 1) === t.charAt(j - 1)) + return editDistanceDFSMem(s, t, mem, i - 1, j - 1); + + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + const insert = editDistanceDFSMem(s, t, mem, i, j - 1); + const del = editDistanceDFSMem(s, t, mem, i - 1, j); + const replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1); + // Record and return minimum edit steps + mem[i][j] = Math.min(insert, del, replace) + 1; + return mem[i][j]; +} + +/* Edit distance: Dynamic programming */ +function editDistanceDP(s: string, t: string): number { + const n = s.length, + m = t.length; + const dp = Array.from({ length: n + 1 }, () => + Array.from({ length: m + 1 }, () => 0) + ); + // State transition: first row and first column + for (let i = 1; i <= n; i++) { + dp[i][0] = i; + } + for (let j = 1; j <= m; j++) { + dp[0][j] = j; + } + // State transition: rest of the rows and columns + for (let i = 1; i <= n; i++) { + for (let j = 1; j <= m; j++) { + if (s.charAt(i - 1) === t.charAt(j - 1)) { + // If two characters are equal, skip both characters + dp[i][j] = dp[i - 1][j - 1]; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[i][j] = + Math.min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1; + } + } + } + return dp[n][m]; +} + +/* Edit distance: Space-optimized dynamic programming */ +function editDistanceDPComp(s: string, t: string): number { + const n = s.length, + m = t.length; + const dp = new Array(m + 1).fill(0); + // State transition: first row + for (let j = 1; j <= m; j++) { + dp[j] = j; + } + // State transition: rest of the rows + for (let i = 1; i <= n; i++) { + // State transition: first column + let leftup = dp[0]; // Temporarily store dp[i-1, j-1] + dp[0] = i; + // State transition: rest of the columns + for (let j = 1; j <= m; j++) { + const temp = dp[j]; + if (s.charAt(i - 1) === t.charAt(j - 1)) { + // If two characters are equal, skip both characters + dp[j] = leftup; + } else { + // Minimum edit steps = minimum edit steps of insert, delete, replace + 1 + dp[j] = Math.min(dp[j - 1], dp[j], leftup) + 1; + } + leftup = temp; // Update for next round's dp[i-1, j-1] + } + } + return dp[m]; +} + +/* Driver Code */ +const s = 'bag'; +const t = 'pack'; +const n = s.length, + m = t.length; + +// Brute-force search +let res = editDistanceDFS(s, t, n, m); +console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); + +// Memoization search +const mem = Array.from({ length: n + 1 }, () => + Array.from({ length: m + 1 }, () => -1) +); +res = editDistanceDFSMem(s, t, mem, n, m); +console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); + +// Dynamic programming +res = editDistanceDP(s, t); +console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); + +// Space-optimized dynamic programming +res = editDistanceDPComp(s, t); +console.log(`Changing ${s} to ${t} requires minimum ${res} edits`); + +export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/knapsack.ts b/en/codes/typescript/chapter_dynamic_programming/knapsack.ts new file mode 100644 index 000000000..5107c59ab --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/knapsack.ts @@ -0,0 +1,134 @@ +/** + * File: knapsack.ts + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* 0-1 knapsack: Brute-force search */ +function knapsackDFS( + wgt: Array, + val: Array, + i: number, + c: number +): number { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i === 0 || c === 0) { + return 0; + } + // If exceeds knapsack capacity, can only choose not to put it in + if (wgt[i - 1] > c) { + return knapsackDFS(wgt, val, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + const no = knapsackDFS(wgt, val, i - 1, c); + const yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; + // Return the larger value of the two options + return Math.max(no, yes); +} + +/* 0-1 knapsack: Memoization search */ +function knapsackDFSMem( + wgt: Array, + val: Array, + mem: Array>, + i: number, + c: number +): number { + // If all items have been selected or knapsack has no remaining capacity, return value 0 + if (i === 0 || c === 0) { + return 0; + } + // If there's a record, return it directly + if (mem[i][c] !== -1) { + return mem[i][c]; + } + // If exceeds knapsack capacity, can only choose not to put it in + if (wgt[i - 1] > c) { + return knapsackDFSMem(wgt, val, mem, i - 1, c); + } + // Calculate the maximum value of not putting in and putting in item i + const no = knapsackDFSMem(wgt, val, mem, i - 1, c); + const yes = + knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; + // Record and return the larger value of the two options + mem[i][c] = Math.max(no, yes); + return mem[i][c]; +} + +/* 0-1 knapsack: Dynamic programming */ +function knapsackDP( + wgt: Array, + val: Array, + cap: number +): number { + const n = wgt.length; + // Initialize dp table + const dp = Array.from({ length: n + 1 }, () => + Array.from({ length: cap + 1 }, () => 0) + ); + // State transition + for (let i = 1; i <= n; i++) { + for (let c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c]; + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = Math.max( + dp[i - 1][c], + dp[i - 1][c - wgt[i - 1]] + val[i - 1] + ); + } + } + } + return dp[n][cap]; +} + +/* 0-1 knapsack: Space-optimized dynamic programming */ +function knapsackDPComp( + wgt: Array, + val: Array, + cap: number +): number { + const n = wgt.length; + // Initialize dp table + const dp = Array(cap + 1).fill(0); + // State transition + for (let i = 1; i <= n; i++) { + // Traverse in reverse order + for (let c = cap; c >= 1; c--) { + if (wgt[i - 1] <= c) { + // The larger value between not selecting and selecting item i + dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; +} + +/* Driver Code */ +const wgt = [10, 20, 30, 40, 50]; +const val = [50, 120, 150, 210, 240]; +const cap = 50; +const n = wgt.length; + +// Brute-force search +let res = knapsackDFS(wgt, val, n, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); + +// Memoization search +const mem = Array.from({ length: n + 1 }, () => + Array.from({ length: cap + 1 }, () => -1) +); +res = knapsackDFSMem(wgt, val, mem, n, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); + +// Dynamic programming +res = knapsackDP(wgt, val, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); + +// Space-optimized dynamic programming +res = knapsackDPComp(wgt, val, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); + +export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.ts b/en/codes/typescript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.ts new file mode 100644 index 000000000..6266990ce --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/min_cost_climbing_stairs_dp.ts @@ -0,0 +1,51 @@ +/** + * File: min_cost_climbing_stairs_dp.ts + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Minimum cost climbing stairs: Dynamic programming */ +function minCostClimbingStairsDP(cost: Array): number { + const n = cost.length - 1; + if (n === 1 || n === 2) { + return cost[n]; + } + // Initialize dp table, used to store solutions to subproblems + const dp = new Array(n + 1); + // Initial state: preset the solution to the smallest subproblem + dp[1] = cost[1]; + dp[2] = cost[2]; + // State transition: gradually solve larger subproblems from smaller ones + for (let i = 3; i <= n; i++) { + dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; + } + return dp[n]; +} + +/* Minimum cost climbing stairs: Space-optimized dynamic programming */ +function minCostClimbingStairsDPComp(cost: Array): number { + const n = cost.length - 1; + if (n === 1 || n === 2) { + return cost[n]; + } + let a = cost[1], + b = cost[2]; + for (let i = 3; i <= n; i++) { + const tmp = b; + b = Math.min(a, tmp) + cost[i]; + a = tmp; + } + return b; +} + +/* Driver Code */ +const cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]; +console.log(`Input stair cost list is: ${cost}`); + +let res = minCostClimbingStairsDP(cost); +console.log(`Minimum cost to climb stairs is: ${res}`); + +res = minCostClimbingStairsDPComp(cost); +console.log(`Minimum cost to climb stairs is: ${res}`); + +export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/min_path_sum.ts b/en/codes/typescript/chapter_dynamic_programming/min_path_sum.ts new file mode 100644 index 000000000..a94f3140c --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/min_path_sum.ts @@ -0,0 +1,132 @@ +/** + * File: min_path_sum.ts + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Minimum path sum: Brute-force search */ +function minPathSumDFS( + grid: Array>, + i: number, + j: number +): number { + // If it's the top-left cell, terminate the search + if (i === 0 && j == 0) { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + return Infinity; + } + // Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1) + const up = minPathSumDFS(grid, i - 1, j); + const left = minPathSumDFS(grid, i, j - 1); + // Return the minimum path cost from top-left to (i, j) + return Math.min(left, up) + grid[i][j]; +} + +/* Minimum path sum: Memoization search */ +function minPathSumDFSMem( + grid: Array>, + mem: Array>, + i: number, + j: number +): number { + // If it's the top-left cell, terminate the search + if (i === 0 && j === 0) { + return grid[0][0]; + } + // If row or column index is out of bounds, return +∞ cost + if (i < 0 || j < 0) { + return Infinity; + } + // If there's a record, return it directly + if (mem[i][j] != -1) { + return mem[i][j]; + } + // Minimum path cost for left and upper cells + const up = minPathSumDFSMem(grid, mem, i - 1, j); + const left = minPathSumDFSMem(grid, mem, i, j - 1); + // Record and return the minimum path cost from top-left to (i, j) + mem[i][j] = Math.min(left, up) + grid[i][j]; + return mem[i][j]; +} + +/* Minimum path sum: Dynamic programming */ +function minPathSumDP(grid: Array>): number { + const n = grid.length, + m = grid[0].length; + // Initialize dp table + const dp = Array.from({ length: n }, () => + Array.from({ length: m }, () => 0) + ); + dp[0][0] = grid[0][0]; + // State transition: first row + for (let j = 1; j < m; j++) { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + // State transition: first column + for (let i = 1; i < n; i++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + // State transition: rest of the rows and columns + for (let i = 1; i < n; i++) { + for (let j: number = 1; j < m; j++) { + dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; + } + } + return dp[n - 1][m - 1]; +} + +/* Minimum path sum: Space-optimized dynamic programming */ +function minPathSumDPComp(grid: Array>): number { + const n = grid.length, + m = grid[0].length; + // Initialize dp table + const dp = new Array(m); + // State transition: first row + dp[0] = grid[0][0]; + for (let j = 1; j < m; j++) { + dp[j] = dp[j - 1] + grid[0][j]; + } + // State transition: rest of the rows + for (let i = 1; i < n; i++) { + // State transition: first column + dp[0] = dp[0] + grid[i][0]; + // State transition: rest of the columns + for (let j = 1; j < m; j++) { + dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + return dp[m - 1]; +} + +/* Driver Code */ +const grid = [ + [1, 3, 1, 5], + [2, 2, 4, 2], + [5, 3, 2, 1], + [4, 3, 5, 2], +]; +const n = grid.length, + m = grid[0].length; +// Brute-force search +let res = minPathSumDFS(grid, n - 1, m - 1); +console.log(`Minimum path sum from top-left to bottom-right is ${res}`); + +// Memoization search +const mem = Array.from({ length: n }, () => + Array.from({ length: m }, () => -1) +); +res = minPathSumDFSMem(grid, mem, n - 1, m - 1); +console.log(`Minimum path sum from top-left to bottom-right is ${res}`); + +// Dynamic programming +res = minPathSumDP(grid); +console.log(`Minimum path sum from top-left to bottom-right is ${res}`); + +// Space-optimized dynamic programming +res = minPathSumDPComp(grid); +console.log(`Minimum path sum from top-left to bottom-right is ${res}`); + +export {}; diff --git a/en/codes/typescript/chapter_dynamic_programming/unbounded_knapsack.ts b/en/codes/typescript/chapter_dynamic_programming/unbounded_knapsack.ts new file mode 100644 index 000000000..6f026465f --- /dev/null +++ b/en/codes/typescript/chapter_dynamic_programming/unbounded_knapsack.ts @@ -0,0 +1,73 @@ +/** + * File: unbounded_knapsack.ts + * Created Time: 2023-08-23 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Unbounded knapsack: Dynamic programming */ +function unboundedKnapsackDP( + wgt: Array, + val: Array, + cap: number +): number { + const n = wgt.length; + // Initialize dp table + const dp = Array.from({ length: n + 1 }, () => + Array.from({ length: cap + 1 }, () => 0) + ); + // State transition + for (let i = 1; i <= n; i++) { + for (let c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[i][c] = dp[i - 1][c]; + } else { + // The larger value between not selecting and selecting item i + dp[i][c] = Math.max( + dp[i - 1][c], + dp[i][c - wgt[i - 1]] + val[i - 1] + ); + } + } + } + return dp[n][cap]; +} + +/* Unbounded knapsack: Space-optimized dynamic programming */ +function unboundedKnapsackDPComp( + wgt: Array, + val: Array, + cap: number +): number { + const n = wgt.length; + // Initialize dp table + const dp = Array.from({ length: cap + 1 }, () => 0); + // State transition + for (let i = 1; i <= n; i++) { + for (let c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // If exceeds knapsack capacity, don't select item i + dp[c] = dp[c]; + } else { + // The larger value between not selecting and selecting item i + dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; +} + +/* Driver Code */ +const wgt = [1, 2, 3]; +const val = [5, 11, 15]; +const cap = 4; + +// Dynamic programming +let res = unboundedKnapsackDP(wgt, val, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); + +// Space-optimized dynamic programming +res = unboundedKnapsackDPComp(wgt, val, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); + +export {}; diff --git a/en/codes/typescript/chapter_graph/graph_adjacency_list.ts b/en/codes/typescript/chapter_graph/graph_adjacency_list.ts new file mode 100644 index 000000000..6f4fe359c --- /dev/null +++ b/en/codes/typescript/chapter_graph/graph_adjacency_list.ts @@ -0,0 +1,140 @@ +/** + * File: graph_adjacency_list.ts + * Created Time: 2023-02-09 + * Author: Justin (xiefahit@gmail.com) + */ + +import { Vertex } from '../modules/Vertex'; + +/* Undirected graph class based on adjacency list */ +class GraphAdjList { + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex + adjList: Map; + + /* Constructor */ + constructor(edges: Vertex[][]) { + this.adjList = new Map(); + // Add all vertices and edges + for (const edge of edges) { + this.addVertex(edge[0]); + this.addVertex(edge[1]); + this.addEdge(edge[0], edge[1]); + } + } + + /* Get the number of vertices */ + size(): number { + return this.adjList.size; + } + + /* Add edge */ + addEdge(vet1: Vertex, vet2: Vertex): void { + if ( + !this.adjList.has(vet1) || + !this.adjList.has(vet2) || + vet1 === vet2 + ) { + throw new Error('Illegal Argument Exception'); + } + // Add edge vet1 - vet2 + this.adjList.get(vet1).push(vet2); + this.adjList.get(vet2).push(vet1); + } + + /* Remove edge */ + removeEdge(vet1: Vertex, vet2: Vertex): void { + if ( + !this.adjList.has(vet1) || + !this.adjList.has(vet2) || + vet1 === vet2 || + this.adjList.get(vet1).indexOf(vet2) === -1 + ) { + throw new Error('Illegal Argument Exception'); + } + // Remove edge vet1 - vet2 + this.adjList.get(vet1).splice(this.adjList.get(vet1).indexOf(vet2), 1); + this.adjList.get(vet2).splice(this.adjList.get(vet2).indexOf(vet1), 1); + } + + /* Add vertex */ + addVertex(vet: Vertex): void { + if (this.adjList.has(vet)) return; + // Add a new linked list in the adjacency list + this.adjList.set(vet, []); + } + + /* Remove vertex */ + removeVertex(vet: Vertex): void { + if (!this.adjList.has(vet)) { + throw new Error('Illegal Argument Exception'); + } + // Remove the linked list corresponding to vertex vet in the adjacency list + this.adjList.delete(vet); + // Traverse the linked lists of other vertices and remove all edges containing vet + for (const set of this.adjList.values()) { + const index: number = set.indexOf(vet); + if (index > -1) { + set.splice(index, 1); + } + } + } + + /* Print adjacency list */ + print(): void { + console.log('Adjacency list ='); + for (const [key, value] of this.adjList.entries()) { + const tmp = []; + for (const vertex of value) { + tmp.push(vertex.val); + } + console.log(key.val + ': ' + tmp.join()); + } + } +} + +/* Driver Code */ +if (import.meta.url.endsWith(process.argv[1])) { + /* Add edge */ + const v0 = new Vertex(1), + v1 = new Vertex(3), + v2 = new Vertex(2), + v3 = new Vertex(5), + v4 = new Vertex(4); + const edges = [ + [v0, v1], + [v1, v2], + [v2, v3], + [v0, v3], + [v2, v4], + [v3, v4], + ]; + const graph = new GraphAdjList(edges); + console.log('\nAfter initialization, graph is'); + graph.print(); + + /* Add edge */ + // Vertices 1, 2 are v0, v2 + graph.addEdge(v0, v2); + console.log('\nAfter adding edge 1-2, graph is'); + graph.print(); + + /* Remove edge */ + // Vertices 1, 3 are v0, v1 + graph.removeEdge(v0, v1); + console.log('\nAfter removing edge 1-3, graph is'); + graph.print(); + + /* Add vertex */ + const v5 = new Vertex(6); + graph.addVertex(v5); + console.log('\nAfter adding vertex 6, graph is'); + graph.print(); + + /* Remove vertex */ + // Vertex 3 is v1 + graph.removeVertex(v1); + console.log('\nAfter removing vertex 3, graph is'); + graph.print(); +} + +export { GraphAdjList }; diff --git a/en/codes/typescript/chapter_graph/graph_adjacency_matrix.ts b/en/codes/typescript/chapter_graph/graph_adjacency_matrix.ts new file mode 100644 index 000000000..986a9df9a --- /dev/null +++ b/en/codes/typescript/chapter_graph/graph_adjacency_matrix.ts @@ -0,0 +1,134 @@ +/** + * File: graph_adjacency_matrix.ts + * Created Time: 2023-02-09 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +/* Undirected graph class based on adjacency matrix */ +class GraphAdjMat { + vertices: number[]; // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index" + adjMat: number[][]; // Adjacency matrix, where the row and column indices correspond to the "vertex index" + + /* Constructor */ + constructor(vertices: number[], edges: number[][]) { + this.vertices = []; + this.adjMat = []; + // Add vertex + for (const val of vertices) { + this.addVertex(val); + } + // Add edge + // Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices + for (const e of edges) { + this.addEdge(e[0], e[1]); + } + } + + /* Get the number of vertices */ + size(): number { + return this.vertices.length; + } + + /* Add vertex */ + addVertex(val: number): void { + const n: number = this.size(); + // Add the value of the new vertex to the vertex list + this.vertices.push(val); + // Add a row to the adjacency matrix + const newRow: number[] = []; + for (let j: number = 0; j < n; j++) { + newRow.push(0); + } + this.adjMat.push(newRow); + // Add a column to the adjacency matrix + for (const row of this.adjMat) { + row.push(0); + } + } + + /* Remove vertex */ + removeVertex(index: number): void { + if (index >= this.size()) { + throw new RangeError('Index Out Of Bounds Exception'); + } + // Remove the vertex at index from the vertex list + this.vertices.splice(index, 1); + + // Remove the row at index from the adjacency matrix + this.adjMat.splice(index, 1); + // Remove the column at index from the adjacency matrix + for (const row of this.adjMat) { + row.splice(index, 1); + } + } + + /* Add edge */ + // Parameters i, j correspond to the vertices element indices + addEdge(i: number, j: number): void { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { + throw new RangeError('Index Out Of Bounds Exception'); + } + // In undirected graph, adjacency matrix is symmetric about main diagonal, i.e., satisfies (i, j) === (j, i) + this.adjMat[i][j] = 1; + this.adjMat[j][i] = 1; + } + + /* Remove edge */ + // Parameters i, j correspond to the vertices element indices + removeEdge(i: number, j: number): void { + // Handle index out of bounds and equality + if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { + throw new RangeError('Index Out Of Bounds Exception'); + } + this.adjMat[i][j] = 0; + this.adjMat[j][i] = 0; + } + + /* Print adjacency matrix */ + print(): void { + console.log('Vertex list = ', this.vertices); + console.log('Adjacency matrix =', this.adjMat); + } +} + +/* Driver Code */ +/* Add edge */ +// Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices +const vertices: number[] = [1, 3, 2, 5, 4]; +const edges: number[][] = [ + [0, 1], + [1, 2], + [2, 3], + [0, 3], + [2, 4], + [3, 4], +]; +const graph: GraphAdjMat = new GraphAdjMat(vertices, edges); +console.log('\nAfter initialization, graph is'); +graph.print(); + +/* Add edge */ +// Add vertex +graph.addEdge(0, 2); +console.log('\nAfter adding edge 1-2, graph is'); +graph.print(); + +/* Remove edge */ +// Vertices 1, 3 have indices 0, 1 respectively +graph.removeEdge(0, 1); +console.log('\nAfter removing edge 1-3, graph is'); +graph.print(); + +/* Add vertex */ +graph.addVertex(6); +console.log('\nAfter adding vertex 6, graph is'); +graph.print(); + +/* Remove vertex */ +// Vertex 3 has index 1 +graph.removeVertex(1); +console.log('\nAfter removing vertex 3, graph is'); +graph.print(); + +export {}; diff --git a/en/codes/typescript/chapter_graph/graph_bfs.ts b/en/codes/typescript/chapter_graph/graph_bfs.ts new file mode 100644 index 000000000..0274440f9 --- /dev/null +++ b/en/codes/typescript/chapter_graph/graph_bfs.ts @@ -0,0 +1,61 @@ +/** + * File: graph_bfs.ts + * Created Time: 2023-02-21 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +import { GraphAdjList } from './graph_adjacency_list'; +import { Vertex } from '../modules/Vertex'; + +/* Breadth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +function graphBFS(graph: GraphAdjList, startVet: Vertex): Vertex[] { + // Vertex traversal sequence + const res: Vertex[] = []; + // Hash set for recording vertices that have been visited + const visited: Set = new Set(); + visited.add(startVet); + // Queue used to implement BFS + const que = [startVet]; + // Starting from vertex vet, loop until all vertices are visited + while (que.length) { + const vet = que.shift(); // Dequeue the front vertex + res.push(vet); // Record visited vertex + // Traverse all adjacent vertices of this vertex + for (const adjVet of graph.adjList.get(vet) ?? []) { + if (visited.has(adjVet)) { + continue; // Skip vertices that have been visited + } + que.push(adjVet); // Only enqueue unvisited + visited.add(adjVet); // Mark this vertex as visited + } + } + // Return vertex traversal sequence + return res; +} + +/* Driver Code */ +/* Add edge */ +const v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); +const edges = [ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[1], v[4]], + [v[2], v[5]], + [v[3], v[4]], + [v[3], v[6]], + [v[4], v[5]], + [v[4], v[7]], + [v[5], v[8]], + [v[6], v[7]], + [v[7], v[8]], +]; +const graph = new GraphAdjList(edges); +console.log('\nAfter initialization, graph is'); +graph.print(); + +/* Breadth-first traversal */ +const res = graphBFS(graph, v[0]); +console.log('\nBreadth-first traversal (BFS) vertex sequence is'); +console.log(Vertex.vetsToVals(res)); diff --git a/en/codes/typescript/chapter_graph/graph_dfs.ts b/en/codes/typescript/chapter_graph/graph_dfs.ts new file mode 100644 index 000000000..736d82890 --- /dev/null +++ b/en/codes/typescript/chapter_graph/graph_dfs.ts @@ -0,0 +1,58 @@ +/** + * File: graph_dfs.ts + * Created Time: 2023-02-21 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +import { Vertex } from '../modules/Vertex'; +import { GraphAdjList } from './graph_adjacency_list'; + +/* Depth-first traversal helper function */ +function dfs( + graph: GraphAdjList, + visited: Set, + res: Vertex[], + vet: Vertex +): void { + res.push(vet); // Record visited vertex + visited.add(vet); // Mark this vertex as visited + // Traverse all adjacent vertices of this vertex + for (const adjVet of graph.adjList.get(vet)) { + if (visited.has(adjVet)) { + continue; // Skip vertices that have been visited + } + // Recursively visit adjacent vertices + dfs(graph, visited, res, adjVet); + } +} + +/* Depth-first traversal */ +// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex +function graphDFS(graph: GraphAdjList, startVet: Vertex): Vertex[] { + // Vertex traversal sequence + const res: Vertex[] = []; + // Hash set for recording vertices that have been visited + const visited: Set = new Set(); + dfs(graph, visited, res, startVet); + return res; +} + +/* Driver Code */ +/* Add edge */ +const v = Vertex.valsToVets([0, 1, 2, 3, 4, 5, 6]); +const edges = [ + [v[0], v[1]], + [v[0], v[3]], + [v[1], v[2]], + [v[2], v[5]], + [v[4], v[5]], + [v[5], v[6]], +]; +const graph = new GraphAdjList(edges); +console.log('\nAfter initialization, graph is'); +graph.print(); + +/* Depth-first traversal */ +const res = graphDFS(graph, v[0]); +console.log('\nDepth-first traversal (DFS) vertex sequence is'); +console.log(Vertex.vetsToVals(res)); diff --git a/en/codes/typescript/chapter_greedy/coin_change_greedy.ts b/en/codes/typescript/chapter_greedy/coin_change_greedy.ts new file mode 100644 index 000000000..7a31304d0 --- /dev/null +++ b/en/codes/typescript/chapter_greedy/coin_change_greedy.ts @@ -0,0 +1,50 @@ +/** + * File: coin_change_greedy.ts + * Created Time: 2023-09-02 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Coin change: Greedy algorithm */ +function coinChangeGreedy(coins: number[], amt: number): number { + // Assume coins array is sorted + let i = coins.length - 1; + let count = 0; + // Loop to make greedy choices until no remaining amount + while (amt > 0) { + // Find the coin that is less than and closest to the remaining amount + while (i > 0 && coins[i] > amt) { + i--; + } + // Choose coins[i] + amt -= coins[i]; + count++; + } + // If no feasible solution is found, return -1 + return amt === 0 ? count : -1; +} + +/* Driver Code */ +// Greedy algorithm: Can guarantee finding the global optimal solution +let coins: number[] = [1, 5, 10, 20, 50, 100]; +let amt: number = 186; +let res: number = coinChangeGreedy(coins, amt); +console.log(`\ncoins = ${coins}, amt = ${amt}`); +console.log(`Minimum coins needed to make ${amt} is ${res}`); + +// Greedy algorithm: Cannot guarantee finding the global optimal solution +coins = [1, 20, 50]; +amt = 60; +res = coinChangeGreedy(coins, amt); +console.log(`\ncoins = ${coins}, amt = ${amt}`); +console.log(`Minimum coins needed to make ${amt} is ${res}`); +console.log('Actually the minimum number needed is 3, i.e., 20 + 20 + 20'); + +// Greedy algorithm: Cannot guarantee finding the global optimal solution +coins = [1, 49, 50]; +amt = 98; +res = coinChangeGreedy(coins, amt); +console.log(`\ncoins = ${coins}, amt = ${amt}`); +console.log(`Minimum coins needed to make ${amt} is ${res}`); +console.log('Actually the minimum number needed is 2, i.e., 49 + 49'); + +export {}; diff --git a/en/codes/typescript/chapter_greedy/fractional_knapsack.ts b/en/codes/typescript/chapter_greedy/fractional_knapsack.ts new file mode 100644 index 000000000..43236d262 --- /dev/null +++ b/en/codes/typescript/chapter_greedy/fractional_knapsack.ts @@ -0,0 +1,50 @@ +/** + * File: fractional_knapsack.ts + * Created Time: 2023-09-02 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Item */ +class Item { + w: number; // Item weight + v: number; // Item value + + constructor(w: number, v: number) { + this.w = w; + this.v = v; + } +} + +/* Fractional knapsack: Greedy algorithm */ +function fractionalKnapsack(wgt: number[], val: number[], cap: number): number { + // Create item list with two attributes: weight, value + const items: Item[] = wgt.map((w, i) => new Item(w, val[i])); + // Sort by unit value item.v / item.w from high to low + items.sort((a, b) => b.v / b.w - a.v / a.w); + // Loop for greedy selection + let res = 0; + for (const item of items) { + if (item.w <= cap) { + // If remaining capacity is sufficient, put the entire current item into the knapsack + res += item.v; + cap -= item.w; + } else { + // If remaining capacity is insufficient, put part of the current item into the knapsack + res += (item.v / item.w) * cap; + // No remaining capacity, so break out of the loop + break; + } + } + return res; +} + +/* Driver Code */ +const wgt: number[] = [10, 20, 30, 40, 50]; +const val: number[] = [50, 120, 150, 210, 240]; +const cap: number = 50; + +// Greedy algorithm +const res: number = fractionalKnapsack(wgt, val, cap); +console.log(`Maximum item value not exceeding knapsack capacity is ${res}`); + +export {}; diff --git a/en/codes/typescript/chapter_greedy/max_capacity.ts b/en/codes/typescript/chapter_greedy/max_capacity.ts new file mode 100644 index 000000000..7da8d703d --- /dev/null +++ b/en/codes/typescript/chapter_greedy/max_capacity.ts @@ -0,0 +1,36 @@ +/** + * File: max_capacity.ts + * Created Time: 2023-09-02 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Max capacity: Greedy algorithm */ +function maxCapacity(ht: number[]): number { + // Initialize i, j to be at both ends of the array + let i = 0, + j = ht.length - 1; + // Initial max capacity is 0 + let res = 0; + // Loop for greedy selection until the two boards meet + while (i < j) { + // Update max capacity + const cap: number = Math.min(ht[i], ht[j]) * (j - i); + res = Math.max(res, cap); + // Move the shorter board inward + if (ht[i] < ht[j]) { + i += 1; + } else { + j -= 1; + } + } + return res; +} + +/* Driver Code */ +const ht: number[] = [3, 8, 5, 2, 7, 7, 3, 4]; + +// Greedy algorithm +const res: number = maxCapacity(ht); +console.log(`Maximum capacity is ${res}`); + +export {}; diff --git a/en/codes/typescript/chapter_greedy/max_product_cutting.ts b/en/codes/typescript/chapter_greedy/max_product_cutting.ts new file mode 100644 index 000000000..7041f6625 --- /dev/null +++ b/en/codes/typescript/chapter_greedy/max_product_cutting.ts @@ -0,0 +1,35 @@ +/** + * File: max_product_cutting.ts + * Created Time: 2023-09-02 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Max product cutting: Greedy algorithm */ +function maxProductCutting(n: number): number { + // When n <= 3, must cut out a 1 + if (n <= 3) { + return 1 * (n - 1); + } + // Greedily cut out 3, a is the number of 3s, b is the remainder + let a: number = Math.floor(n / 3); + let b: number = n % 3; + if (b === 1) { + // When the remainder is 1, convert a pair of 1 * 3 to 2 * 2 + return Math.pow(3, a - 1) * 2 * 2; + } + if (b === 2) { + // When the remainder is 2, do nothing + return Math.pow(3, a) * 2; + } + // When the remainder is 0, do nothing + return Math.pow(3, a); +} + +/* Driver Code */ +let n: number = 58; + +// Greedy algorithm +let res: number = maxProductCutting(n); +console.log(`Maximum cutting product is ${res}`); + +export {}; diff --git a/en/codes/typescript/chapter_hashing/array_hash_map.ts b/en/codes/typescript/chapter_hashing/array_hash_map.ts new file mode 100644 index 000000000..faf263f5c --- /dev/null +++ b/en/codes/typescript/chapter_hashing/array_hash_map.ts @@ -0,0 +1,134 @@ +/** + * File: array_hash_map.ts + * Created Time: 2022-12-20 + * Author: Daniel (better.sunjian@gmail.com) + */ + +/* Key-value pair Number -> String */ +class Pair { + public key: number; + public val: string; + + constructor(key: number, val: string) { + this.key = key; + this.val = val; + } +} + +/* Hash table based on array implementation */ +class ArrayHashMap { + private readonly buckets: (Pair | null)[]; + + constructor() { + // Initialize array with 100 buckets + this.buckets = new Array(100).fill(null); + } + + /* Hash function */ + private hashFunc(key: number): number { + return key % 100; + } + + /* Query operation */ + public get(key: number): string | null { + let index = this.hashFunc(key); + let pair = this.buckets[index]; + if (pair === null) return null; + return pair.val; + } + + /* Add operation */ + public set(key: number, val: string) { + let index = this.hashFunc(key); + this.buckets[index] = new Pair(key, val); + } + + /* Remove operation */ + public delete(key: number) { + let index = this.hashFunc(key); + // Set to null to represent deletion + this.buckets[index] = null; + } + + /* Get all key-value pairs */ + public entries(): (Pair | null)[] { + let arr: (Pair | null)[] = []; + for (let i = 0; i < this.buckets.length; i++) { + if (this.buckets[i]) { + arr.push(this.buckets[i]); + } + } + return arr; + } + + /* Get all keys */ + public keys(): (number | undefined)[] { + let arr: (number | undefined)[] = []; + for (let i = 0; i < this.buckets.length; i++) { + if (this.buckets[i]) { + arr.push(this.buckets[i].key); + } + } + return arr; + } + + /* Get all values */ + public values(): (string | undefined)[] { + let arr: (string | undefined)[] = []; + for (let i = 0; i < this.buckets.length; i++) { + if (this.buckets[i]) { + arr.push(this.buckets[i].val); + } + } + return arr; + } + + /* Print hash table */ + public print() { + let pairSet = this.entries(); + for (const pair of pairSet) { + console.info(`${pair.key} -> ${pair.val}`); + } + } +} + +/* Driver Code */ +/* Initialize hash table */ +const map = new ArrayHashMap(); +/* Add operation */ +// Add key-value pair (key, value) to the hash table +map.set(12836, 'Xiao Ha'); +map.set(15937, 'Xiao Luo'); +map.set(16750, 'Xiao Suan'); +map.set(13276, 'Xiao Fa'); +map.set(10583, 'Xiao Ya'); +console.info('\nAfter adding is complete, hash table is\nKey -> Value'); +map.print(); + +/* Query operation */ +// Input key into hash table to get value +let name = map.get(15937); +console.info('\nInput student ID 15937, query name ' + name); + +/* Remove operation */ +// Remove key-value pair (key, value) from hash table +map.delete(10583); +console.info('\nAfter removing 10583, hash table is\nKey -> Value'); +map.print(); + +/* Traverse hash table */ +console.info('\nTraverse key-value pairs Key->Value'); +for (const pair of map.entries()) { + if (!pair) continue; + console.info(pair.key + ' -> ' + pair.val); +} +console.info('\nTraverse keys only Key'); +for (const key of map.keys()) { + console.info(key); +} +console.info('\nTraverse values only Value'); +for (const val of map.values()) { + console.info(val); +} + +export {}; diff --git a/en/codes/typescript/chapter_hashing/hash_map.ts b/en/codes/typescript/chapter_hashing/hash_map.ts new file mode 100644 index 000000000..31c3205f5 --- /dev/null +++ b/en/codes/typescript/chapter_hashing/hash_map.ts @@ -0,0 +1,46 @@ +/** + * File: hash_map.ts + * Created Time: 2022-12-20 + * Author: Daniel (better.sunjian@gmail.com) + */ + +/* Driver Code */ +/* Initialize hash table */ +const map = new Map(); + +/* Add operation */ +// Add key-value pair (key, value) to the hash table +map.set(12836, 'Xiao Ha'); +map.set(15937, 'Xiao Luo'); +map.set(16750, 'Xiao Suan'); +map.set(13276, 'Xiao Fa'); +map.set(10583, 'Xiao Ya'); +console.info('\nAfter adding is complete, hash table is\nKey -> Value'); +console.info(map); + +/* Query operation */ +// Input key into hash table to get value +let name = map.get(15937); +console.info('\nInput student ID 15937, query name ' + name); + +/* Remove operation */ +// Remove key-value pair (key, value) from hash table +map.delete(10583); +console.info('\nAfter removing 10583, hash table is\nKey -> Value'); +console.info(map); + +/* Traverse hash table */ +console.info('\nTraverse key-value pairs Key->Value'); +for (const [k, v] of map.entries()) { + console.info(k + ' -> ' + v); +} +console.info('\nTraverse keys only Key'); +for (const k of map.keys()) { + console.info(k); +} +console.info('\nTraverse values only Value'); +for (const v of map.values()) { + console.info(v); +} + +export {}; diff --git a/en/codes/typescript/chapter_hashing/hash_map_chaining.ts b/en/codes/typescript/chapter_hashing/hash_map_chaining.ts new file mode 100644 index 000000000..48bac7f32 --- /dev/null +++ b/en/codes/typescript/chapter_hashing/hash_map_chaining.ts @@ -0,0 +1,146 @@ +/** + * File: hash_map_chaining.ts + * Created Time: 2023-08-06 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Key-value pair Number -> String */ +class Pair { + key: number; + val: string; + constructor(key: number, val: string) { + this.key = key; + this.val = val; + } +} + +/* Hash table with separate chaining */ +class HashMapChaining { + #size: number; // Number of key-value pairs + #capacity: number; // Hash table capacity + #loadThres: number; // Load factor threshold for triggering expansion + #extendRatio: number; // Expansion multiplier + #buckets: Pair[][]; // Bucket array + + /* Constructor */ + constructor() { + this.#size = 0; + this.#capacity = 4; + this.#loadThres = 2.0 / 3.0; + this.#extendRatio = 2; + this.#buckets = new Array(this.#capacity).fill(null).map((x) => []); + } + + /* Hash function */ + #hashFunc(key: number): number { + return key % this.#capacity; + } + + /* Load factor */ + #loadFactor(): number { + return this.#size / this.#capacity; + } + + /* Query operation */ + get(key: number): string | null { + const index = this.#hashFunc(key); + const bucket = this.#buckets[index]; + // Traverse bucket, if key is found, return corresponding val + for (const pair of bucket) { + if (pair.key === key) { + return pair.val; + } + } + // If key is not found, return null + return null; + } + + /* Add operation */ + put(key: number, val: string): void { + // When load factor exceeds threshold, perform expansion + if (this.#loadFactor() > this.#loadThres) { + this.#extend(); + } + const index = this.#hashFunc(key); + const bucket = this.#buckets[index]; + // Traverse bucket, if specified key is encountered, update corresponding val and return + for (const pair of bucket) { + if (pair.key === key) { + pair.val = val; + return; + } + } + // If key does not exist, append key-value pair to the end + const pair = new Pair(key, val); + bucket.push(pair); + this.#size++; + } + + /* Remove operation */ + remove(key: number): void { + const index = this.#hashFunc(key); + let bucket = this.#buckets[index]; + // Traverse bucket and remove key-value pair from it + for (let i = 0; i < bucket.length; i++) { + if (bucket[i].key === key) { + bucket.splice(i, 1); + this.#size--; + break; + } + } + } + + /* Expand hash table */ + #extend(): void { + // Temporarily store the original hash table + const bucketsTmp = this.#buckets; + // Initialize expanded new hash table + this.#capacity *= this.#extendRatio; + this.#buckets = new Array(this.#capacity).fill(null).map((x) => []); + this.#size = 0; + // Move key-value pairs from original hash table to new hash table + for (const bucket of bucketsTmp) { + for (const pair of bucket) { + this.put(pair.key, pair.val); + } + } + } + + /* Print hash table */ + print(): void { + for (const bucket of this.#buckets) { + let res = []; + for (const pair of bucket) { + res.push(pair.key + ' -> ' + pair.val); + } + console.log(res); + } + } +} + +/* Driver Code */ +/* Initialize hash table */ +const map = new HashMapChaining(); + +/* Add operation */ +// Add key-value pair (key, value) to the hash table +map.put(12836, 'Xiao Ha'); +map.put(15937, 'Xiao Luo'); +map.put(16750, 'Xiao Suan'); +map.put(13276, 'Xiao Fa'); +map.put(10583, 'Xiao Ya'); +console.log('\nAfter adding is complete, hash table is\nKey -> Value'); +map.print(); + +/* Query operation */ +// Input key into hash table to get value +const name = map.get(13276); +console.log('\nInput student ID 13276, query name ' + name); + +/* Remove operation */ +// Remove key-value pair (key, value) from hash table +map.remove(12836); +console.log('\nAfter removing 12836, hash table is\nKey -> Value'); +map.print(); + +export {}; diff --git a/en/codes/typescript/chapter_hashing/hash_map_open_addressing.ts b/en/codes/typescript/chapter_hashing/hash_map_open_addressing.ts new file mode 100644 index 000000000..e755d60b8 --- /dev/null +++ b/en/codes/typescript/chapter_hashing/hash_map_open_addressing.ts @@ -0,0 +1,182 @@ +/** + * File: hash_map_open_addressing.ts + * Created Time: 2023-08-06 + * Author: yuan0221 (yl1452491917@gmail.com), krahets (krahets@163.com) + */ + +/* Key-value pair Number -> String */ +class Pair { + key: number; + val: string; + + constructor(key: number, val: string) { + this.key = key; + this.val = val; + } +} + +/* Hash table with open addressing */ +class HashMapOpenAddressing { + private size: number; // Number of key-value pairs + private capacity: number; // Hash table capacity + private loadThres: number; // Load factor threshold for triggering expansion + private extendRatio: number; // Expansion multiplier + private buckets: Array; // Bucket array + private TOMBSTONE: Pair; // Removal marker + + /* Constructor */ + constructor() { + this.size = 0; // Number of key-value pairs + this.capacity = 4; // Hash table capacity + this.loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion + this.extendRatio = 2; // Expansion multiplier + this.buckets = Array(this.capacity).fill(null); // Bucket array + this.TOMBSTONE = new Pair(-1, '-1'); // Removal marker + } + + /* Hash function */ + private hashFunc(key: number): number { + return key % this.capacity; + } + + /* Load factor */ + private loadFactor(): number { + return this.size / this.capacity; + } + + /* Search for bucket index corresponding to key */ + private findBucket(key: number): number { + let index = this.hashFunc(key); + let firstTombstone = -1; + // Linear probing, break when encountering an empty bucket + while (this.buckets[index] !== null) { + // If key is encountered, return the corresponding bucket index + if (this.buckets[index]!.key === key) { + // If a removal marker was encountered before, move the key-value pair to that index + if (firstTombstone !== -1) { + this.buckets[firstTombstone] = this.buckets[index]; + this.buckets[index] = this.TOMBSTONE; + return firstTombstone; // Return the moved bucket index + } + return index; // Return bucket index + } + // Record the first removal marker encountered + if ( + firstTombstone === -1 && + this.buckets[index] === this.TOMBSTONE + ) { + firstTombstone = index; + } + // Calculate bucket index, wrap around to the head if past the tail + index = (index + 1) % this.capacity; + } + // If key does not exist, return the index for insertion + return firstTombstone === -1 ? index : firstTombstone; + } + + /* Query operation */ + get(key: number): string | null { + // Search for bucket index corresponding to key + const index = this.findBucket(key); + // If key-value pair is found, return corresponding val + if ( + this.buckets[index] !== null && + this.buckets[index] !== this.TOMBSTONE + ) { + return this.buckets[index]!.val; + } + // If key-value pair does not exist, return null + return null; + } + + /* Add operation */ + put(key: number, val: string): void { + // When load factor exceeds threshold, perform expansion + if (this.loadFactor() > this.loadThres) { + this.extend(); + } + // Search for bucket index corresponding to key + const index = this.findBucket(key); + // If key-value pair is found, overwrite val and return + if ( + this.buckets[index] !== null && + this.buckets[index] !== this.TOMBSTONE + ) { + this.buckets[index]!.val = val; + return; + } + // If key-value pair does not exist, add the key-value pair + this.buckets[index] = new Pair(key, val); + this.size++; + } + + /* Remove operation */ + remove(key: number): void { + // Search for bucket index corresponding to key + const index = this.findBucket(key); + // If key-value pair is found, overwrite it with removal marker + if ( + this.buckets[index] !== null && + this.buckets[index] !== this.TOMBSTONE + ) { + this.buckets[index] = this.TOMBSTONE; + this.size--; + } + } + + /* Expand hash table */ + private extend(): void { + // Temporarily store the original hash table + const bucketsTmp = this.buckets; + // Initialize expanded new hash table + this.capacity *= this.extendRatio; + this.buckets = Array(this.capacity).fill(null); + this.size = 0; + // Move key-value pairs from original hash table to new hash table + for (const pair of bucketsTmp) { + if (pair !== null && pair !== this.TOMBSTONE) { + this.put(pair.key, pair.val); + } + } + } + + /* Print hash table */ + print(): void { + for (const pair of this.buckets) { + if (pair === null) { + console.log('null'); + } else if (pair === this.TOMBSTONE) { + console.log('TOMBSTONE'); + } else { + console.log(pair.key + ' -> ' + pair.val); + } + } + } +} + +/* Driver Code */ +// Initialize hash table +const hashmap = new HashMapOpenAddressing(); + +// Add operation +// Add key-value pair (key, val) to the hash table +hashmap.put(12836, 'Xiao Ha'); +hashmap.put(15937, 'Xiao Luo'); +hashmap.put(16750, 'Xiao Suan'); +hashmap.put(13276, 'Xiao Fa'); +hashmap.put(10583, 'Xiao Ya'); +console.log('\nAfter adding is complete, hash table is\nKey -> Value'); +hashmap.print(); + +// Query operation +// Input key into hash table to get value val +const name = hashmap.get(13276); +console.log('\nInput student ID 13276, query name ' + name); + +// Remove operation +// Remove key-value pair (key, val) from hash table +hashmap.remove(16750); +console.log('\nAfter removing 16750, hash table is\nKey -> Value'); +hashmap.print(); + +export {}; diff --git a/en/codes/typescript/chapter_hashing/simple_hash.ts b/en/codes/typescript/chapter_hashing/simple_hash.ts new file mode 100644 index 000000000..ecc80a0be --- /dev/null +++ b/en/codes/typescript/chapter_hashing/simple_hash.ts @@ -0,0 +1,60 @@ +/** + * File: simple_hash.ts + * Created Time: 2023-08-06 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +/* Additive hash */ +function addHash(key: string): number { + let hash = 0; + const MODULUS = 1000000007; + for (const c of key) { + hash = (hash + c.charCodeAt(0)) % MODULUS; + } + return hash; +} + +/* Multiplicative hash */ +function mulHash(key: string): number { + let hash = 0; + const MODULUS = 1000000007; + for (const c of key) { + hash = (31 * hash + c.charCodeAt(0)) % MODULUS; + } + return hash; +} + +/* XOR hash */ +function xorHash(key: string): number { + let hash = 0; + const MODULUS = 1000000007; + for (const c of key) { + hash ^= c.charCodeAt(0); + } + return hash % MODULUS; +} + +/* Rotational hash */ +function rotHash(key: string): number { + let hash = 0; + const MODULUS = 1000000007; + for (const c of key) { + hash = ((hash << 4) ^ (hash >> 28) ^ c.charCodeAt(0)) % MODULUS; + } + return hash; +} + +/* Driver Code */ +const key = 'Hello Algo'; + +let hash = addHash(key); +console.log('Additive hash value is ' + hash); + +hash = mulHash(key); +console.log('Multiplicative hash value is ' + hash); + +hash = xorHash(key); +console.log('XOR hash value is ' + hash); + +hash = rotHash(key); +console.log('Rotational hash value is ' + hash); diff --git a/en/codes/typescript/chapter_heap/my_heap.ts b/en/codes/typescript/chapter_heap/my_heap.ts new file mode 100644 index 000000000..3947b5c81 --- /dev/null +++ b/en/codes/typescript/chapter_heap/my_heap.ts @@ -0,0 +1,155 @@ +/** + * File: my_heap.ts + * Created Time: 2023-02-07 + * Author: Justin (xiefahit@gmail.com) + */ + +import { printHeap } from '../modules/PrintUtil'; + +/* Max heap class */ +class MaxHeap { + private maxHeap: number[]; + /* Constructor, build empty heap or build heap from input list */ + constructor(nums?: number[]) { + // Add list elements to heap as is + this.maxHeap = nums === undefined ? [] : [...nums]; + // Heapify all nodes except leaf nodes + for (let i = this.parent(this.size() - 1); i >= 0; i--) { + this.siftDown(i); + } + } + + /* Get index of left child node */ + private left(i: number): number { + return 2 * i + 1; + } + + /* Get index of right child node */ + private right(i: number): number { + return 2 * i + 2; + } + + /* Get index of parent node */ + private parent(i: number): number { + return Math.floor((i - 1) / 2); // Floor division + } + + /* Swap elements */ + private swap(i: number, j: number): void { + const tmp = this.maxHeap[i]; + this.maxHeap[i] = this.maxHeap[j]; + this.maxHeap[j] = tmp; + } + + /* Get heap size */ + public size(): number { + return this.maxHeap.length; + } + + /* Check if heap is empty */ + public isEmpty(): boolean { + return this.size() === 0; + } + + /* Access top element */ + public peek(): number { + return this.maxHeap[0]; + } + + /* Element enters heap */ + public push(val: number): void { + // Add node + this.maxHeap.push(val); + // Heapify from bottom to top + this.siftUp(this.size() - 1); + } + + /* Starting from node i, heapify from bottom to top */ + private siftUp(i: number): void { + while (true) { + // Get parent node of node i + const p = this.parent(i); + // When "crossing root node" or "node needs no repair", end heapify + if (p < 0 || this.maxHeap[i] <= this.maxHeap[p]) break; + // Swap two nodes + this.swap(i, p); + // Loop upward heapify + i = p; + } + } + + /* Element exits heap */ + public pop(): number { + // Handle empty case + if (this.isEmpty()) throw new RangeError('Heap is empty.'); + // Delete node + this.swap(0, this.size() - 1); + // Remove node + const val = this.maxHeap.pop(); + // Return top element + this.siftDown(0); + // Return heap top element + return val; + } + + /* Starting from node i, heapify from top to bottom */ + private siftDown(i: number): void { + while (true) { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + const l = this.left(i), + r = this.right(i); + let ma = i; + if (l < this.size() && this.maxHeap[l] > this.maxHeap[ma]) ma = l; + if (r < this.size() && this.maxHeap[r] > this.maxHeap[ma]) ma = r; + // Swap two nodes + if (ma === i) break; + // Swap two nodes + this.swap(i, ma); + // Loop downwards heapification + i = ma; + } + } + + /* Driver Code */ + public print(): void { + printHeap(this.maxHeap); + } + + /* Extract elements from heap */ + public getMaxHeap(): number[] { + return this.maxHeap; + } +} + +/* Driver Code */ +if (import.meta.url.endsWith(process.argv[1])) { + /* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */ + const maxHeap = new MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); + console.log('\nAfter inputting list and building heap'); + maxHeap.print(); + + /* Check if heap is empty */ + let peek = maxHeap.peek(); + console.log(`\nHeap top element is ${peek}`); + + /* Element enters heap */ + const val = 7; + maxHeap.push(val); + console.log(`\nAfter element ${val} pushes to heap`); + maxHeap.print(); + + /* Time complexity is O(n), not O(nlogn) */ + peek = maxHeap.pop(); + console.log(`\nAfter heap top element ${peek} pops from heap`); + maxHeap.print(); + + /* Get heap size */ + const size = maxHeap.size(); + console.log(`\nHeap size is ${size}`); + + /* Check if heap is empty */ + const isEmpty = maxHeap.isEmpty(); + console.log(`\nIs heap empty ${isEmpty}`); +} + +export { MaxHeap }; diff --git a/en/codes/typescript/chapter_heap/top_k.ts b/en/codes/typescript/chapter_heap/top_k.ts new file mode 100644 index 000000000..0884a752d --- /dev/null +++ b/en/codes/typescript/chapter_heap/top_k.ts @@ -0,0 +1,58 @@ +/** + * File: top_k.ts + * Created Time: 2023-08-13 + * Author: Justin (xiefahit@gmail.com) + */ + +import { MaxHeap } from './my_heap'; + +/* Element enters heap */ +function pushMinHeap(maxHeap: MaxHeap, val: number): void { + // Negate element + maxHeap.push(-val); +} + +/* Element exits heap */ +function popMinHeap(maxHeap: MaxHeap): number { + // Negate element + return -maxHeap.pop(); +} + +/* Access top element */ +function peekMinHeap(maxHeap: MaxHeap): number { + // Negate element + return -maxHeap.peek(); +} + +/* Extract elements from heap */ +function getMinHeap(maxHeap: MaxHeap): number[] { + // Negate element + return maxHeap.getMaxHeap().map((num: number) => -num); +} + +/* Find the largest k elements in array based on heap */ +function topKHeap(nums: number[], k: number): number[] { + // Python's heapq module implements min heap by default + // Note: We negate all heap elements to simulate min heap using max heap + const maxHeap = new MaxHeap([]); + // Enter the first k elements of array into heap + for (let i = 0; i < k; i++) { + pushMinHeap(maxHeap, nums[i]); + } + // Starting from the (k+1)th element, maintain heap length as k + for (let i = k; i < nums.length; i++) { + // If current element is greater than top element, top element exits heap, current element enters heap + if (nums[i] > peekMinHeap(maxHeap)) { + popMinHeap(maxHeap); + pushMinHeap(maxHeap, nums[i]); + } + } + // Return elements in heap + return getMinHeap(maxHeap); +} + +/* Driver Code */ +const nums = [1, 7, 6, 3, 2]; +const k = 3; +const res = topKHeap(nums, k); +console.log(`The largest ${k} elements are`, res); diff --git a/en/codes/typescript/chapter_searching/binary_search.ts b/en/codes/typescript/chapter_searching/binary_search.ts new file mode 100644 index 000000000..341cce37b --- /dev/null +++ b/en/codes/typescript/chapter_searching/binary_search.ts @@ -0,0 +1,65 @@ +/** + * File: binary_search.ts + * Created Time: 2022-12-27 + * Author: Daniel (better.sunjian@gmail.com) + */ + +/* Binary search (closed interval on both sides) */ +function binarySearch(nums: number[], target: number): number { + // Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array + let i = 0, + j = nums.length - 1; + // Loop, exit when the search interval is empty (empty when i > j) + while (i <= j) { + // Calculate the midpoint index m + const m = Math.floor(i + (j - i) / 2); + if (nums[m] < target) { + // This means target is in the interval [m+1, j] + i = m + 1; + } else if (nums[m] > target) { + // This means target is in the interval [i, m-1] + j = m - 1; + } else { + // Found the target element, return its index + return m; + } + } + return -1; // Target element not found, return -1 +} + +/* Binary search (left-closed right-open interval) */ +function binarySearchLCRO(nums: number[], target: number): number { + // Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1 + let i = 0, + j = nums.length; + // Loop, exit when the search interval is empty (empty when i = j) + while (i < j) { + // Calculate the midpoint index m + const m = Math.floor(i + (j - i) / 2); + if (nums[m] < target) { + // This means target is in the interval [m+1, j) + i = m + 1; + } else if (nums[m] > target) { + // This means target is in the interval [i, m) + j = m; + } else { + // Found the target element, return its index + return m; + } + } + return -1; // Target element not found, return -1 +} + +/* Driver Code */ +const target = 6; +const nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; + +/* Binary search (closed interval on both sides) */ +let index = binarySearch(nums, target); +console.info('Index of target element 6 is %d', index); + +/* Binary search (left-closed right-open interval) */ +index = binarySearchLCRO(nums, target); +console.info('Index of target element 6 is %d', index); + +export {}; diff --git a/en/codes/typescript/chapter_searching/binary_search_edge.ts b/en/codes/typescript/chapter_searching/binary_search_edge.ts new file mode 100644 index 000000000..dfdd270bc --- /dev/null +++ b/en/codes/typescript/chapter_searching/binary_search_edge.ts @@ -0,0 +1,46 @@ +/** + * File: binary_search_edge.ts + * Created Time: 2023-08-22 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ +import { binarySearchInsertion } from './binary_search_insertion'; + +/* Binary search for the leftmost target */ +function binarySearchLeftEdge(nums: Array, target: number): number { + // Equivalent to finding the insertion point of target + const i = binarySearchInsertion(nums, target); + // Target not found, return -1 + if (i === nums.length || nums[i] !== target) { + return -1; + } + // Found target, return index i + return i; +} + +/* Binary search for the rightmost target */ +function binarySearchRightEdge(nums: Array, target: number): number { + // Convert to finding the leftmost target + 1 + const i = binarySearchInsertion(nums, target + 1); + // j points to the rightmost target, i points to the first element greater than target + const j = i - 1; + // Target not found, return -1 + if (j === -1 || nums[j] !== target) { + return -1; + } + // Found target, return index j + return j; +} + +/* Driver Code */ +// Array with duplicate elements +let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; +console.log('\nArray nums = ' + nums); +// Binary search left and right boundaries +for (const target of [6, 7]) { + let index = binarySearchLeftEdge(nums, target); + console.log('Leftmost element ' + target + ' has index ' + index); + index = binarySearchRightEdge(nums, target); + console.log('Rightmost element ' + target + ' has index ' + index); +} + +export {}; diff --git a/en/codes/typescript/chapter_searching/binary_search_insertion.ts b/en/codes/typescript/chapter_searching/binary_search_insertion.ts new file mode 100644 index 000000000..d32429ac4 --- /dev/null +++ b/en/codes/typescript/chapter_searching/binary_search_insertion.ts @@ -0,0 +1,65 @@ +/** + * File: binary_search_insertion.ts + * Created Time: 2023-08-22 + * Author: Gaofer Chou (gaofer-chou@qq.com) + */ + +/* Binary search for insertion point (no duplicate elements) */ +function binarySearchInsertionSimple( + nums: Array, + target: number +): number { + let i = 0, + j = nums.length - 1; // Initialize closed interval [0, n-1] + while (i <= j) { + const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, use Math.floor() to round down + if (nums[m] < target) { + i = m + 1; // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // target is in the interval [i, m-1] + } else { + return m; // Found target, return insertion point m + } + } + // Target not found, return insertion point i + return i; +} + +/* Binary search for insertion point (with duplicate elements) */ +function binarySearchInsertion(nums: Array, target: number): number { + let i = 0, + j = nums.length - 1; // Initialize closed interval [0, n-1] + while (i <= j) { + const m = Math.floor(i + (j - i) / 2); // Calculate midpoint index m, use Math.floor() to round down + if (nums[m] < target) { + i = m + 1; // target is in the interval [m+1, j] + } else if (nums[m] > target) { + j = m - 1; // target is in the interval [i, m-1] + } else { + j = m - 1; // The first element less than target is in the interval [i, m-1] + } + } + // Return insertion point i + return i; +} + +/* Driver Code */ +// Array without duplicate elements +let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]; +console.log('\nArray nums = ' + nums); +// Binary search for insertion point +for (const target of [6, 9]) { + const index = binarySearchInsertionSimple(nums, target); + console.log('Element ' + target + ''s insertion point index is ' + index); +} + +// Array with duplicate elements +nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]; +console.log('\nArray nums = ' + nums); +// Binary search for insertion point +for (const target of [2, 6, 20]) { + const index = binarySearchInsertion(nums, target); + console.log('Element ' + target + ''s insertion point index is ' + index); +} + +export { binarySearchInsertion }; diff --git a/en/codes/typescript/chapter_searching/hashing_search.ts b/en/codes/typescript/chapter_searching/hashing_search.ts new file mode 100644 index 000000000..51e4f8add --- /dev/null +++ b/en/codes/typescript/chapter_searching/hashing_search.ts @@ -0,0 +1,50 @@ +/** + * File: hashing_search.ts + * Created Time: 2022-12-29 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +import { ListNode, arrToLinkedList } from '../modules/ListNode'; + +/* Hash search (array) */ +function hashingSearchArray(map: Map, target: number): number { + // Hash table's key: target element, value: index + // If this key does not exist in the hash table, return -1 + return map.has(target) ? (map.get(target) as number) : -1; +} + +/* Hash search (linked list) */ +function hashingSearchLinkedList( + map: Map, + target: number +): ListNode | null { + // Hash table key: target node value, value: node object + // If key is not in hash table, return null + return map.has(target) ? (map.get(target) as ListNode) : null; +} + +/* Driver Code */ +const target = 3; + +/* Hash search (array) */ +const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; +// Initialize hash table +const map = new Map(); +for (let i = 0; i < nums.length; i++) { + map.set(nums[i], i); // key: element, value: index +} +const index = hashingSearchArray(map, target); +console.log('Index of target element 3 = ' + index); + +/* Hash search (linked list) */ +let head = arrToLinkedList(nums); +// Initialize hash table +const map1 = new Map(); +while (head != null) { + map1.set(head.val, head); // key: node value, value: node + head = head.next; +} +const node = hashingSearchLinkedList(map1, target); +console.log('Node object with target value 3 is', node); + +export {}; diff --git a/en/codes/typescript/chapter_searching/linear_search.ts b/en/codes/typescript/chapter_searching/linear_search.ts new file mode 100644 index 000000000..fd068d932 --- /dev/null +++ b/en/codes/typescript/chapter_searching/linear_search.ts @@ -0,0 +1,52 @@ +/** + * File: linear_search.ts + * Created Time: 2023-01-07 + * Author: Daniel (better.sunjian@gmail.com) + */ + +import { ListNode, arrToLinkedList } from '../modules/ListNode'; + +/* Linear search (array) */ +function linearSearchArray(nums: number[], target: number): number { + // Traverse array + for (let i = 0; i < nums.length; i++) { + // Found the target element, return its index + if (nums[i] === target) { + return i; + } + } + // Target element not found, return -1 + return -1; +} + +/* Linear search (linked list) */ +function linearSearchLinkedList( + head: ListNode | null, + target: number +): ListNode | null { + // Traverse the linked list + while (head) { + // Found the target node, return it + if (head.val === target) { + return head; + } + head = head.next; + } + // Target node not found, return null + return null; +} + +/* Driver Code */ +const target = 3; + +/* Perform linear search in array */ +const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; +const index = linearSearchArray(nums, target); +console.log('Index of target element 3 =', index); + +/* Perform linear search in linked list */ +const head = arrToLinkedList(nums); +const node = linearSearchLinkedList(head, target); +console.log('Node object with target value 3 is', node); + +export {}; diff --git a/en/codes/typescript/chapter_searching/two_sum.ts b/en/codes/typescript/chapter_searching/two_sum.ts new file mode 100644 index 000000000..de2f05412 --- /dev/null +++ b/en/codes/typescript/chapter_searching/two_sum.ts @@ -0,0 +1,49 @@ +/** + * File: two_sum.ts + * Created Time: 2022-12-15 + * Author: gyt95 (gytkwan@gmail.com) + */ + +/* Method 1: Brute force enumeration */ +function twoSumBruteForce(nums: number[], target: number): number[] { + const n = nums.length; + // Two nested loops, time complexity is O(n^2) + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + if (nums[i] + nums[j] === target) { + return [i, j]; + } + } + } + return []; +} + +/* Method 2: Auxiliary hash table */ +function twoSumHashTable(nums: number[], target: number): number[] { + // Auxiliary hash table, space complexity is O(n) + let m: Map = new Map(); + // Single loop, time complexity is O(n) + for (let i = 0; i < nums.length; i++) { + let index = m.get(target - nums[i]); + if (index !== undefined) { + return [index, i]; + } else { + m.set(nums[i], i); + } + } + return []; +} + +/* Driver Code */ +// Method 1 +const nums = [2, 7, 11, 15], + target = 13; + +let res = twoSumBruteForce(nums, target); +console.log('Method 1 res = ', res); + +// Method 2 +res = twoSumHashTable(nums, target); +console.log('Method 2 res = ', res); + +export {}; diff --git a/en/codes/typescript/chapter_sorting/bubble_sort.ts b/en/codes/typescript/chapter_sorting/bubble_sort.ts new file mode 100644 index 000000000..0c8d9cdef --- /dev/null +++ b/en/codes/typescript/chapter_sorting/bubble_sort.ts @@ -0,0 +1,51 @@ +/** + * File: bubble_sort.ts + * Created Time: 2022-12-12 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Bubble sort */ +function bubbleSort(nums: number[]): void { + // Outer loop: unsorted range is [0, i] + for (let i = nums.length - 1; i > 0; i--) { + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + } + } + } +} + +/* Bubble sort (flag optimization) */ +function bubbleSortWithFlag(nums: number[]): void { + // Outer loop: unsorted range is [0, i] + for (let i = nums.length - 1; i > 0; i--) { + let flag = false; // Initialize flag + // Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // Swap nums[j] and nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + flag = true; // Record element swap + } + } + if (!flag) break; // No elements were swapped in this round of "bubbling", exit directly + } +} + +/* Driver Code */ +const nums = [4, 1, 3, 1, 5, 2]; +bubbleSort(nums); +console.log('After bubble sort, nums =', nums); + +const nums1 = [4, 1, 3, 1, 5, 2]; +bubbleSortWithFlag(nums1); +console.log('After bubble sort, nums =', nums1); + +export {}; diff --git a/en/codes/typescript/chapter_sorting/bucket_sort.ts b/en/codes/typescript/chapter_sorting/bucket_sort.ts new file mode 100644 index 000000000..52fc54857 --- /dev/null +++ b/en/codes/typescript/chapter_sorting/bucket_sort.ts @@ -0,0 +1,41 @@ +/** + * File: bucket_sort.ts + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Bucket sort */ +function bucketSort(nums: number[]): void { + // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket + const k = nums.length / 2; + const buckets: number[][] = []; + for (let i = 0; i < k; i++) { + buckets.push([]); + } + // 1. Distribute array elements into various buckets + for (const num of nums) { + // Input data range is [0, 1), use num * k to map to index range [0, k-1] + const i = Math.floor(num * k); + // Add num to bucket i + buckets[i].push(num); + } + // 2. Sort each bucket + for (const bucket of buckets) { + // Use built-in sorting function, can also replace with other sorting algorithms + bucket.sort((a, b) => a - b); + } + // 3. Traverse buckets to merge results + let i = 0; + for (const bucket of buckets) { + for (const num of bucket) { + nums[i++] = num; + } + } +} + +/* Driver Code */ +const nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37]; +bucketSort(nums); +console.log('After bucket sort, nums =', nums); + +export {}; diff --git a/en/codes/typescript/chapter_sorting/counting_sort.ts b/en/codes/typescript/chapter_sorting/counting_sort.ts new file mode 100644 index 000000000..da30e112f --- /dev/null +++ b/en/codes/typescript/chapter_sorting/counting_sort.ts @@ -0,0 +1,67 @@ +/** + * File: counting_sort.ts + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Counting sort */ +// Simple implementation, cannot be used for sorting objects +function countingSortNaive(nums: number[]): void { + // 1. Count the maximum element m in the array + let m: number = Math.max(...nums); + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + const counter: number[] = new Array(m + 1).fill(0); + for (const num of nums) { + counter[num]++; + } + // 3. Traverse counter, filling each element back into the original array nums + let i = 0; + for (let num = 0; num < m + 1; num++) { + for (let j = 0; j < counter[num]; j++, i++) { + nums[i] = num; + } + } +} + +/* Counting sort */ +// Complete implementation, can sort objects and is a stable sort +function countingSort(nums: number[]): void { + // 1. Count the maximum element m in the array + let m: number = Math.max(...nums); + // 2. Count the occurrence of each number + // counter[num] represents the occurrence of num + const counter: number[] = new Array(m + 1).fill(0); + for (const num of nums) { + counter[num]++; + } + // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + // counter[num]-1 is the last index where num appears in res + for (let i = 0; i < m; i++) { + counter[i + 1] += counter[i]; + } + // 4. Traverse nums in reverse order, placing each element into the result array res + // Initialize the array res to record results + const n = nums.length; + const res: number[] = new Array(n); + for (let i = n - 1; i >= 0; i--) { + const num = nums[i]; + res[counter[num] - 1] = num; // Place num at the corresponding index + counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num + } + // Use result array res to overwrite the original array nums + for (let i = 0; i < n; i++) { + nums[i] = res[i]; + } +} + +/* Driver Code */ +const nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; +countingSortNaive(nums); +console.log('After counting sort (cannot sort objects), nums =', nums); + +const nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; +countingSort(nums1); +console.log('After counting sort, nums1 =', nums1); + +export {}; diff --git a/en/codes/typescript/chapter_sorting/heap_sort.ts b/en/codes/typescript/chapter_sorting/heap_sort.ts new file mode 100644 index 000000000..9cff66aa9 --- /dev/null +++ b/en/codes/typescript/chapter_sorting/heap_sort.ts @@ -0,0 +1,51 @@ +/** + * File: heap_sort.ts + * Created Time: 2023-06-04 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Heap length is n, start heapifying node i, from top to bottom */ +function siftDown(nums: number[], n: number, i: number): void { + while (true) { + // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break + let l = 2 * i + 1; + let r = 2 * i + 2; + let ma = i; + if (l < n && nums[l] > nums[ma]) { + ma = l; + } + if (r < n && nums[r] > nums[ma]) { + ma = r; + } + // Swap two nodes + if (ma === i) { + break; + } + // Swap two nodes + [nums[i], nums[ma]] = [nums[ma], nums[i]]; + // Loop downwards heapification + i = ma; + } +} + +/* Heap sort */ +function heapSort(nums: number[]): void { + // Build heap operation: heapify all nodes except leaves + for (let i = Math.floor(nums.length / 2) - 1; i >= 0; i--) { + siftDown(nums, nums.length, i); + } + // Extract the largest element from the heap and repeat for n-1 rounds + for (let i = nums.length - 1; i > 0; i--) { + // Delete node + [nums[0], nums[i]] = [nums[i], nums[0]]; + // Start heapifying the root node, from top to bottom + siftDown(nums, i, 0); + } +} + +/* Driver Code */ +const nums: number[] = [4, 1, 3, 1, 5, 2]; +heapSort(nums); +console.log('After heap sort, nums =', nums); + +export {}; diff --git a/en/codes/typescript/chapter_sorting/insertion_sort.ts b/en/codes/typescript/chapter_sorting/insertion_sort.ts new file mode 100644 index 000000000..8a0ea037c --- /dev/null +++ b/en/codes/typescript/chapter_sorting/insertion_sort.ts @@ -0,0 +1,27 @@ +/** + * File: insertion_sort.ts + * Created Time: 2022-12-12 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Insertion sort */ +function insertionSort(nums: number[]): void { + // Outer loop: sorted interval is [0, i-1] + for (let i = 1; i < nums.length; i++) { + const base = nums[i]; + let j = i - 1; + // Inner loop: insert base into the correct position within the sorted interval [0, i-1] + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // Move nums[j] to the right by one position + j--; + } + nums[j + 1] = base; // Assign base to the correct position + } +} + +/* Driver Code */ +const nums = [4, 1, 3, 1, 5, 2]; +insertionSort(nums); +console.log('After insertion sort, nums =', nums); + +export {}; diff --git a/en/codes/typescript/chapter_sorting/merge_sort.ts b/en/codes/typescript/chapter_sorting/merge_sort.ts new file mode 100644 index 000000000..ded2e4c8f --- /dev/null +++ b/en/codes/typescript/chapter_sorting/merge_sort.ts @@ -0,0 +1,54 @@ +/** + * File: merge_sort.ts + * Created Time: 2022-12-12 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Merge left subarray and right subarray */ +function merge(nums: number[], left: number, mid: number, right: number): void { + // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + // Create a temporary array tmp to store the merged results + const tmp = new Array(right - left + 1); + // Initialize the start indices of the left and right subarrays + let i = left, + j = mid + 1, + k = 0; + // While both subarrays still have elements, compare and copy the smaller element into the temporary array + while (i <= mid && j <= right) { + if (nums[i] <= nums[j]) { + tmp[k++] = nums[i++]; + } else { + tmp[k++] = nums[j++]; + } + } + // Copy the remaining elements of the left and right subarrays into the temporary array + while (i <= mid) { + tmp[k++] = nums[i++]; + } + while (j <= right) { + tmp[k++] = nums[j++]; + } + // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval + for (k = 0; k < tmp.length; k++) { + nums[left + k] = tmp[k]; + } +} + +/* Merge sort */ +function mergeSort(nums: number[], left: number, right: number): void { + // Termination condition + if (left >= right) return; // Terminate recursion when subarray length is 1 + // Divide and conquer stage + let mid = Math.floor(left + (right - left) / 2); // Calculate midpoint + mergeSort(nums, left, mid); // Recursively process the left subarray + mergeSort(nums, mid + 1, right); // Recursively process the right subarray + // Merge stage + merge(nums, left, mid, right); +} + +/* Driver Code */ +const nums = [7, 3, 2, 6, 0, 1, 5, 4]; +mergeSort(nums, 0, nums.length - 1); +console.log('After merge sort, nums =', nums); + +export {}; diff --git a/en/codes/typescript/chapter_sorting/quick_sort.ts b/en/codes/typescript/chapter_sorting/quick_sort.ts new file mode 100644 index 000000000..354965b11 --- /dev/null +++ b/en/codes/typescript/chapter_sorting/quick_sort.ts @@ -0,0 +1,180 @@ +/** + * File: quick_sort.ts + * Created Time: 2022-12-12 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Quick sort class */ +class QuickSort { + /* Swap elements */ + swap(nums: number[], i: number, j: number): void { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Sentinel partition */ + partition(nums: number[], left: number, right: number): number { + // Use nums[left] as the pivot + let i = left, + j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) { + j -= 1; // Search from right to left for the first element smaller than the pivot + } + while (i < j && nums[i] <= nums[left]) { + i += 1; // Search from left to right for the first element greater than the pivot + } + // Swap elements + this.swap(nums, i, j); // Swap these two elements + } + this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort */ + quickSort(nums: number[], left: number, right: number): void { + // Terminate recursion when subarray length is 1 + if (left >= right) { + return; + } + // Sentinel partition + const pivot = this.partition(nums, left, right); + // Recursively process the left subarray and right subarray + this.quickSort(nums, left, pivot - 1); + this.quickSort(nums, pivot + 1, right); + } +} + +/* Quick sort class (median pivot optimization) */ +class QuickSortMedian { + /* Swap elements */ + swap(nums: number[], i: number, j: number): void { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Select the median of three candidate elements */ + medianThree( + nums: number[], + left: number, + mid: number, + right: number + ): number { + let l = nums[left], + m = nums[mid], + r = nums[right]; + // m is between l and r + if ((l <= m && m <= r) || (r <= m && m <= l)) return mid; + // l is between m and r + if ((m <= l && l <= r) || (r <= l && l <= m)) return left; + return right; + } + + /* Sentinel partition (median of three) */ + partition(nums: number[], left: number, right: number): number { + // Select the median of three candidate elements + let med = this.medianThree( + nums, + left, + Math.floor((left + right) / 2), + right + ); + // Swap the median to the array's leftmost position + this.swap(nums, left, med); + // Use nums[left] as the pivot + let i = left, + j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) { + j--; // Search from right to left for the first element smaller than the pivot + } + while (i < j && nums[i] <= nums[left]) { + i++; // Search from left to right for the first element greater than the pivot + } + this.swap(nums, i, j); // Swap these two elements + } + this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort */ + quickSort(nums: number[], left: number, right: number): void { + // Terminate recursion when subarray length is 1 + if (left >= right) { + return; + } + // Sentinel partition + const pivot = this.partition(nums, left, right); + // Recursively process the left subarray and right subarray + this.quickSort(nums, left, pivot - 1); + this.quickSort(nums, pivot + 1, right); + } +} + +/* Quick sort class (recursion depth optimization) */ +class QuickSortTailCall { + /* Swap elements */ + swap(nums: number[], i: number, j: number): void { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* Sentinel partition */ + partition(nums: number[], left: number, right: number): number { + // Use nums[left] as the pivot + let i = left, + j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) { + j--; // Search from right to left for the first element smaller than the pivot + } + while (i < j && nums[i] <= nums[left]) { + i++; // Search from left to right for the first element greater than the pivot + } + this.swap(nums, i, j); // Swap these two elements + } + this.swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot + } + + /* Quick sort (recursion depth optimization) */ + quickSort(nums: number[], left: number, right: number): void { + // Terminate when subarray length is 1 + while (left < right) { + // Sentinel partition operation + let pivot = this.partition(nums, left, right); + // Perform quick sort on the shorter of the two subarrays + if (pivot - left < right - pivot) { + this.quickSort(nums, left, pivot - 1); // Recursively sort the left subarray + left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] + } else { + this.quickSort(nums, pivot + 1, right); // Recursively sort the right subarray + right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] + } + } + } +} + +/* Driver Code */ +/* Quick sort */ +const nums = [2, 4, 1, 0, 3, 5]; +const quickSort = new QuickSort(); +quickSort.quickSort(nums, 0, nums.length - 1); +console.log('After quick sort, nums =', nums); + +/* Quick sort (recursion depth optimization) */ +const nums1 = [2, 4, 1, 0, 3, 5]; +const quickSortMedian = new QuickSortMedian(); +quickSortMedian.quickSort(nums1, 0, nums1.length - 1); +console.log('After quick sort (median pivot optimization), nums =', nums1); + +/* Quick sort (recursion depth optimization) */ +const nums2 = [2, 4, 1, 0, 3, 5]; +const quickSortTailCall = new QuickSortTailCall(); +quickSortTailCall.quickSort(nums2, 0, nums2.length - 1); +console.log('After quick sort (recursion depth optimization), nums =', nums2); + +export {}; diff --git a/en/codes/typescript/chapter_sorting/radix_sort.ts b/en/codes/typescript/chapter_sorting/radix_sort.ts new file mode 100644 index 000000000..df0137ef5 --- /dev/null +++ b/en/codes/typescript/chapter_sorting/radix_sort.ts @@ -0,0 +1,63 @@ +/** + * File: radix_sort.ts + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Get the k-th digit of element num, where exp = 10^(k-1) */ +function digit(num: number, exp: number): number { + // Passing exp instead of k can avoid repeated expensive exponentiation here + return Math.floor(num / exp) % 10; +} + +/* Counting sort (based on nums k-th digit) */ +function countingSortDigit(nums: number[], exp: number): void { + // Decimal digit range is 0~9, therefore need a bucket array of length 10 + const counter = new Array(10).fill(0); + const n = nums.length; + // Count the occurrence of digits 0~9 + for (let i = 0; i < n; i++) { + const d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d + counter[d]++; // Count the occurrence of digit d + } + // Calculate prefix sum, converting "occurrence count" into "array index" + for (let i = 1; i < 10; i++) { + counter[i] += counter[i - 1]; + } + // Traverse in reverse, based on bucket statistics, place each element into res + const res = new Array(n).fill(0); + for (let i = n - 1; i >= 0; i--) { + const d = digit(nums[i], exp); + const j = counter[d] - 1; // Get the index j for d in the array + res[j] = nums[i]; // Place the current element at index j + counter[d]--; // Decrease the count of d by 1 + } + // Use result to overwrite the original array nums + for (let i = 0; i < n; i++) { + nums[i] = res[i]; + } +} + +/* Radix sort */ +function radixSort(nums: number[]): void { + // Get the maximum element of the array, used to determine the maximum number of digits + let m: number = Math.max(... nums); + // Traverse from the lowest to the highest digit + for (let exp = 1; exp <= m; exp *= 10) { + // Perform counting sort on the k-th digit of array elements + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // i.e., exp = 10^(k-1) + countingSortDigit(nums, exp); + } +} + +/* Driver Code */ +const nums = [ + 10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, + 30524779, 82060337, 63832996, +]; +radixSort(nums); +console.log('After radix sort, nums =', nums); + +export {}; diff --git a/en/codes/typescript/chapter_sorting/selection_sort.ts b/en/codes/typescript/chapter_sorting/selection_sort.ts new file mode 100644 index 000000000..8d1ecf41d --- /dev/null +++ b/en/codes/typescript/chapter_sorting/selection_sort.ts @@ -0,0 +1,29 @@ +/** + * File: selection_sort.ts + * Created Time: 2023-06-04 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Selection sort */ +function selectionSort(nums: number[]): void { + let n = nums.length; + // Outer loop: unsorted interval is [i, n-1] + for (let i = 0; i < n - 1; i++) { + // Inner loop: find the smallest element within the unsorted interval + let k = i; + for (let j = i + 1; j < n; j++) { + if (nums[j] < nums[k]) { + k = j; // Record the index of the smallest element + } + } + // Swap the smallest element with the first element of the unsorted interval + [nums[i], nums[k]] = [nums[k], nums[i]]; + } +} + +/* Driver Code */ +const nums: number[] = [4, 1, 3, 1, 5, 2]; +selectionSort(nums); +console.log('After selection sort, nums =', nums); + +export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/array_deque.ts b/en/codes/typescript/chapter_stack_and_queue/array_deque.ts new file mode 100644 index 000000000..ae3331ee4 --- /dev/null +++ b/en/codes/typescript/chapter_stack_and_queue/array_deque.ts @@ -0,0 +1,158 @@ +/** + * File: array_deque.ts + * Created Time: 2023-02-28 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +/* Double-ended queue based on circular array implementation */ +class ArrayDeque { + private nums: number[]; // Array for storing double-ended queue elements + private front: number; // Front pointer, points to the front of the queue element + private queSize: number; // Double-ended queue length + + /* Constructor */ + constructor(capacity: number) { + this.nums = new Array(capacity); + this.front = 0; + this.queSize = 0; + } + + /* Get the capacity of the double-ended queue */ + capacity(): number { + return this.nums.length; + } + + /* Get the length of the double-ended queue */ + size(): number { + return this.queSize; + } + + /* Check if the double-ended queue is empty */ + isEmpty(): boolean { + return this.queSize === 0; + } + + /* Calculate circular array index */ + index(i: number): number { + // Use modulo operation to wrap the array head and tail together + // When i passes the tail of the array, return to the head + // When i passes the head of the array, return to the tail + return (i + this.capacity()) % this.capacity(); + } + + /* Front of the queue enqueue */ + pushFirst(num: number): void { + if (this.queSize === this.capacity()) { + console.log('Double-ended queue is full'); + return; + } + // Use modulo operation to wrap front around to the tail after passing the head of the array + // Add num to the front of the queue + this.front = this.index(this.front - 1); + // Add num to front of queue + this.nums[this.front] = num; + this.queSize++; + } + + /* Rear of the queue enqueue */ + pushLast(num: number): void { + if (this.queSize === this.capacity()) { + console.log('Double-ended queue is full'); + return; + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + const rear: number = this.index(this.front + this.queSize); + // Front pointer moves one position backward + this.nums[rear] = num; + this.queSize++; + } + + /* Rear of the queue dequeue */ + popFirst(): number { + const num: number = this.peekFirst(); + // Move front pointer backward by one position + this.front = this.index(this.front + 1); + this.queSize--; + return num; + } + + /* Access rear of the queue element */ + popLast(): number { + const num: number = this.peekLast(); + this.queSize--; + return num; + } + + /* Return list for printing */ + peekFirst(): number { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + return this.nums[this.front]; + } + + /* Driver Code */ + peekLast(): number { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + // Initialize double-ended queue + const last = this.index(this.front + this.queSize - 1); + return this.nums[last]; + } + + /* Return array for printing */ + toArray(): number[] { + // Elements enqueue + const res: number[] = []; + for (let i = 0, j = this.front; i < this.queSize; i++, j++) { + res[i] = this.nums[this.index(j)]; + } + return res; + } +} + +/* Driver Code */ +/* Get the length of the double-ended queue */ +const capacity = 5; +const deque: ArrayDeque = new ArrayDeque(capacity); +deque.pushLast(3); +deque.pushLast(2); +deque.pushLast(5); +console.log('Deque deque = [' + deque.toArray() + ']'); + +/* Update element */ +const peekFirst = deque.peekFirst(); +console.log('Front element peekFirst = ' + peekFirst); +const peekLast = deque.peekLast(); +console.log('Rear element peekLast = ' + peekLast); + +/* Elements enqueue */ +deque.pushLast(4); +console.log('After element 4 enqueues at rear, deque = [' + deque.toArray() + ']'); +deque.pushFirst(1); +console.log('After element 1 enqueues at front, deque = [' + deque.toArray() + ']'); + +/* Element dequeue */ +const popLast = deque.popLast(); +console.log( + 'Rear dequeue element = ' + + popLast + + ', after rear dequeue deque = [' + + deque.toArray() + + ']' +); +const popFirst = deque.popFirst(); +console.log( + 'Front dequeue element = ' + + popFirst + + ', after front dequeue deque = [' + + deque.toArray() + + ']' +); + +/* Get the length of the double-ended queue */ +const size = deque.size(); +console.log('Double-ended queue length size = ' + size); + +/* Check if the double-ended queue is empty */ +const isEmpty = deque.isEmpty(); +console.log('Double-ended queue is empty = ' + isEmpty); + +export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/array_queue.ts b/en/codes/typescript/chapter_stack_and_queue/array_queue.ts new file mode 100644 index 000000000..5b2b25d01 --- /dev/null +++ b/en/codes/typescript/chapter_stack_and_queue/array_queue.ts @@ -0,0 +1,109 @@ +/** + * File: array_queue.ts + * Created Time: 2022-12-11 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +/* Queue based on circular array implementation */ +class ArrayQueue { + private nums: number[]; // Array for storing queue elements + private front: number; // Front pointer, points to the front of the queue element + private queSize: number; // Queue length + + constructor(capacity: number) { + this.nums = new Array(capacity); + this.front = this.queSize = 0; + } + + /* Get the capacity of the queue */ + get capacity(): number { + return this.nums.length; + } + + /* Get the length of the queue */ + get size(): number { + return this.queSize; + } + + /* Check if the queue is empty */ + isEmpty(): boolean { + return this.queSize === 0; + } + + /* Enqueue */ + push(num: number): void { + if (this.size === this.capacity) { + console.log('Queue is full'); + return; + } + // Use modulo operation to wrap rear around to the head after passing the tail of the array + // Add num to the rear of the queue + const rear = (this.front + this.queSize) % this.capacity; + // Front pointer moves one position backward + this.nums[rear] = num; + this.queSize++; + } + + /* Dequeue */ + pop(): number { + const num = this.peek(); + // Move front pointer backward by one position, if it passes the tail, return to array head + this.front = (this.front + 1) % this.capacity; + this.queSize--; + return num; + } + + /* Return list for printing */ + peek(): number { + if (this.isEmpty()) throw new Error('Queue is empty'); + return this.nums[this.front]; + } + + /* Return Array */ + toArray(): number[] { + // Elements enqueue + const arr = new Array(this.size); + for (let i = 0, j = this.front; i < this.size; i++, j++) { + arr[i] = this.nums[j % this.capacity]; + } + return arr; + } +} + +/* Driver Code */ +/* Access front of the queue element */ +const capacity = 10; +const queue = new ArrayQueue(capacity); + +/* Elements enqueue */ +queue.push(1); +queue.push(3); +queue.push(2); +queue.push(5); +queue.push(4); +console.log('Queue queue =', queue.toArray()); + +/* Return list for printing */ +const peek = queue.peek(); +console.log('Front element peek = ' + peek); + +/* Element dequeue */ +const pop = queue.pop(); +console.log('Dequeue element pop = ' + pop + ', after dequeue queue =', queue.toArray()); + +/* Get the length of the queue */ +const size = queue.size; +console.log('Queue length size = ' + size); + +/* Check if the queue is empty */ +const isEmpty = queue.isEmpty(); +console.log('Queue is empty = ' + isEmpty); + +/* Test circular array */ +for (let i = 0; i < 10; i++) { + queue.push(i); + queue.pop(); + console.log('Round ' + i + ' rounds of enqueue + dequeue, queue =', queue.toArray()); +} + +export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/array_stack.ts b/en/codes/typescript/chapter_stack_and_queue/array_stack.ts new file mode 100644 index 000000000..e98274c57 --- /dev/null +++ b/en/codes/typescript/chapter_stack_and_queue/array_stack.ts @@ -0,0 +1,77 @@ +/** + * File: array_stack.ts + * Created Time: 2022-12-08 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +/* Stack based on array implementation */ +class ArrayStack { + private stack: number[]; + constructor() { + this.stack = []; + } + + /* Get the length of the stack */ + get size(): number { + return this.stack.length; + } + + /* Check if the stack is empty */ + isEmpty(): boolean { + return this.stack.length === 0; + } + + /* Push */ + push(num: number): void { + this.stack.push(num); + } + + /* Pop */ + pop(): number | undefined { + if (this.isEmpty()) throw new Error('Stack is empty'); + return this.stack.pop(); + } + + /* Return list for printing */ + top(): number | undefined { + if (this.isEmpty()) throw new Error('Stack is empty'); + return this.stack[this.stack.length - 1]; + } + + /* Return Array */ + toArray() { + return this.stack; + } +} + +/* Driver Code */ +/* Access top of the stack element */ +const stack = new ArrayStack(); + +/* Elements push onto stack */ +stack.push(1); +stack.push(3); +stack.push(2); +stack.push(5); +stack.push(4); +console.log('Stack stack = '); +console.log(stack.toArray()); + +/* Return list for printing */ +const top = stack.top(); +console.log('Stack top element top = ' + top); + +/* Element pop from stack */ +const pop = stack.pop(); +console.log('Pop element pop = ' + pop + ', after pop, stack = '); +console.log(stack.toArray()); + +/* Get the length of the stack */ +const size = stack.size; +console.log('Stack length size = ' + size); + +/* Check if empty */ +const isEmpty = stack.isEmpty(); +console.log('Stack is empty = ' + isEmpty); + +export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/deque.ts b/en/codes/typescript/chapter_stack_and_queue/deque.ts new file mode 100644 index 000000000..f8cd205de --- /dev/null +++ b/en/codes/typescript/chapter_stack_and_queue/deque.ts @@ -0,0 +1,46 @@ +/** + * File: deque.ts + * Created Time: 2023-01-17 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +/* Driver Code */ +/* Get the length of the double-ended queue */ +// TypeScript has no built-in deque, can only use Array as deque +const deque: number[] = []; + +/* Elements enqueue */ +deque.push(2); +deque.push(5); +deque.push(4); +// Note: due to array, unshift() method has O(n) time complexity +deque.unshift(3); +deque.unshift(1); +console.log('Double-ended queue deque = ', deque); + +/* Update element */ +const peekFirst: number = deque[0]; +console.log('Front element peekFirst = ' + peekFirst); +const peekLast: number = deque[deque.length - 1]; +console.log('Rear element peekLast = ' + peekLast); + +/* Element dequeue */ +// Note: due to array, shift() method has O(n) time complexity +const popFront: number = deque.shift() as number; +console.log( + 'Front dequeue element popFront = ' + popFront + ', after front dequeue, deque = ' + deque +); +const popBack: number = deque.pop() as number; +console.log( + 'Dequeue rear element popBack = ' + popBack + ', after rear dequeue, deque = ' + deque +); + +/* Get the length of the double-ended queue */ +const size: number = deque.length; +console.log('Double-ended queue length size = ' + size); + +/* Check if the double-ended queue is empty */ +const isEmpty: boolean = size === 0; +console.log('Double-ended queue is empty = ' + isEmpty); + +export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/linkedlist_deque.ts b/en/codes/typescript/chapter_stack_and_queue/linkedlist_deque.ts new file mode 100644 index 000000000..b8fa239e3 --- /dev/null +++ b/en/codes/typescript/chapter_stack_and_queue/linkedlist_deque.ts @@ -0,0 +1,167 @@ +/** + * File: linkedlist_deque.ts + * Created Time: 2023-02-04 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +/* Doubly linked list node */ +class ListNode { + prev: ListNode; // Predecessor node reference (pointer) + next: ListNode; // Successor node reference (pointer) + val: number; // Node value + + constructor(val: number) { + this.val = val; + this.next = null; + this.prev = null; + } +} + +/* Double-ended queue based on doubly linked list implementation */ +class LinkedListDeque { + private front: ListNode; // Head node front + private rear: ListNode; // Tail node rear + private queSize: number; // Length of the double-ended queue + + constructor() { + this.front = null; + this.rear = null; + this.queSize = 0; + } + + /* Rear of the queue enqueue operation */ + pushLast(val: number): void { + const node: ListNode = new ListNode(val); + // If the linked list is empty, make both front and rear point to node + if (this.queSize === 0) { + this.front = node; + this.rear = node; + } else { + // Add node to the tail of the linked list + this.rear.next = node; + node.prev = this.rear; + this.rear = node; // Update tail node + } + this.queSize++; + } + + /* Front of the queue enqueue operation */ + pushFirst(val: number): void { + const node: ListNode = new ListNode(val); + // If the linked list is empty, make both front and rear point to node + if (this.queSize === 0) { + this.front = node; + this.rear = node; + } else { + // Add node to the head of the linked list + this.front.prev = node; + node.next = this.front; + this.front = node; // Update head node + } + this.queSize++; + } + + /* Temporarily store tail node value */ + popLast(): number { + if (this.queSize === 0) { + return null; + } + const value: number = this.rear.val; // Store tail node value + // Update tail node + let temp: ListNode = this.rear.prev; + if (temp !== null) { + temp.next = null; + this.rear.prev = null; + } + this.rear = temp; // Update tail node + this.queSize--; + return value; + } + + /* Temporarily store head node value */ + popFirst(): number { + if (this.queSize === 0) { + return null; + } + const value: number = this.front.val; // Store tail node value + // Delete head node + let temp: ListNode = this.front.next; + if (temp !== null) { + temp.prev = null; + this.front.next = null; + } + this.front = temp; // Update head node + this.queSize--; + return value; + } + + /* Driver Code */ + peekLast(): number { + return this.queSize === 0 ? null : this.rear.val; + } + + /* Return list for printing */ + peekFirst(): number { + return this.queSize === 0 ? null : this.front.val; + } + + /* Get the length of the double-ended queue */ + size(): number { + return this.queSize; + } + + /* Check if the double-ended queue is empty */ + isEmpty(): boolean { + return this.queSize === 0; + } + + /* Print deque */ + print(): void { + const arr: number[] = []; + let temp: ListNode = this.front; + while (temp !== null) { + arr.push(temp.val); + temp = temp.next; + } + console.log('[' + arr.join(', ') + ']'); + } +} + +/* Driver Code */ +/* Get the length of the double-ended queue */ +const linkedListDeque: LinkedListDeque = new LinkedListDeque(); +linkedListDeque.pushLast(3); +linkedListDeque.pushLast(2); +linkedListDeque.pushLast(5); +console.log('Deque linkedListDeque = '); +linkedListDeque.print(); + +/* Update element */ +const peekFirst: number = linkedListDeque.peekFirst(); +console.log('Front element peekFirst = ' + peekFirst); +const peekLast: number = linkedListDeque.peekLast(); +console.log('Rear element peekLast = ' + peekLast); + +/* Elements enqueue */ +linkedListDeque.pushLast(4); +console.log('After element 4 enqueues at rear, linkedListDeque = '); +linkedListDeque.print(); +linkedListDeque.pushFirst(1); +console.log('After element 1 enqueues at front, linkedListDeque = '); +linkedListDeque.print(); + +/* Element dequeue */ +const popLast: number = linkedListDeque.popLast(); +console.log('Rear dequeue element = ' + popLast + ', after rear dequeue linkedListDeque = '); +linkedListDeque.print(); +const popFirst: number = linkedListDeque.popFirst(); +console.log('Front dequeue element = ' + popFirst + ', after front dequeue linkedListDeque = '); +linkedListDeque.print(); + +/* Get the length of the double-ended queue */ +const size: number = linkedListDeque.size(); +console.log('Double-ended queue length size = ' + size); + +/* Check if the double-ended queue is empty */ +const isEmpty: boolean = linkedListDeque.isEmpty(); +console.log('Double-ended queue is empty = ' + isEmpty); diff --git a/en/codes/typescript/chapter_stack_and_queue/linkedlist_queue.ts b/en/codes/typescript/chapter_stack_and_queue/linkedlist_queue.ts new file mode 100644 index 000000000..9f7bd37ee --- /dev/null +++ b/en/codes/typescript/chapter_stack_and_queue/linkedlist_queue.ts @@ -0,0 +1,102 @@ +/** + * File: linkedlist_queue.ts + * Created Time: 2022-12-19 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +import { ListNode } from '../modules/ListNode'; + +/* Queue based on linked list implementation */ +class LinkedListQueue { + private front: ListNode | null; // Head node front + private rear: ListNode | null; // Tail node rear + private queSize: number = 0; + + constructor() { + this.front = null; + this.rear = null; + } + + /* Get the length of the queue */ + get size(): number { + return this.queSize; + } + + /* Check if the queue is empty */ + isEmpty(): boolean { + return this.size === 0; + } + + /* Enqueue */ + push(num: number): void { + // Add num after the tail node + const node = new ListNode(num); + // If the queue is empty, make both front and rear point to the node + if (!this.front) { + this.front = node; + this.rear = node; + // If the queue is not empty, add the node after the tail node + } else { + this.rear!.next = node; + this.rear = node; + } + this.queSize++; + } + + /* Dequeue */ + pop(): number { + const num = this.peek(); + if (!this.front) throw new Error('Queue is empty'); + // Delete head node + this.front = this.front.next; + this.queSize--; + return num; + } + + /* Return list for printing */ + peek(): number { + if (this.size === 0) throw new Error('Queue is empty'); + return this.front!.val; + } + + /* Convert linked list to Array and return */ + toArray(): number[] { + let node = this.front; + const res = new Array(this.size); + for (let i = 0; i < res.length; i++) { + res[i] = node!.val; + node = node!.next; + } + return res; + } +} + +/* Driver Code */ +/* Access front of the queue element */ +const queue = new LinkedListQueue(); + +/* Elements enqueue */ +queue.push(1); +queue.push(3); +queue.push(2); +queue.push(5); +queue.push(4); +console.log('Queue queue = ' + queue.toArray()); + +/* Return list for printing */ +const peek = queue.peek(); +console.log('Front element peek = ' + peek); + +/* Element dequeue */ +const pop = queue.pop(); +console.log('Dequeue element pop = ' + pop + ', after dequeue, queue = ' + queue.toArray()); + +/* Get the length of the queue */ +const size = queue.size; +console.log('Queue length size = ' + size); + +/* Check if the queue is empty */ +const isEmpty = queue.isEmpty(); +console.log('Queue is empty = ' + isEmpty); + +export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/linkedlist_stack.ts b/en/codes/typescript/chapter_stack_and_queue/linkedlist_stack.ts new file mode 100644 index 000000000..d42f99c06 --- /dev/null +++ b/en/codes/typescript/chapter_stack_and_queue/linkedlist_stack.ts @@ -0,0 +1,91 @@ +/** + * File: linkedlist_stack.ts + * Created Time: 2022-12-21 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +import { ListNode } from '../modules/ListNode'; + +/* Stack based on linked list implementation */ +class LinkedListStack { + private stackPeek: ListNode | null; // Use head node as stack top + private stkSize: number = 0; // Stack length + + constructor() { + this.stackPeek = null; + } + + /* Get the length of the stack */ + get size(): number { + return this.stkSize; + } + + /* Check if the stack is empty */ + isEmpty(): boolean { + return this.size === 0; + } + + /* Push */ + push(num: number): void { + const node = new ListNode(num); + node.next = this.stackPeek; + this.stackPeek = node; + this.stkSize++; + } + + /* Pop */ + pop(): number { + const num = this.peek(); + if (!this.stackPeek) throw new Error('Stack is empty'); + this.stackPeek = this.stackPeek.next; + this.stkSize--; + return num; + } + + /* Return list for printing */ + peek(): number { + if (!this.stackPeek) throw new Error('Stack is empty'); + return this.stackPeek.val; + } + + /* Convert linked list to Array and return */ + toArray(): number[] { + let node = this.stackPeek; + const res = new Array(this.size); + for (let i = res.length - 1; i >= 0; i--) { + res[i] = node!.val; + node = node!.next; + } + return res; + } +} + +/* Driver Code */ +/* Access top of the stack element */ +const stack = new LinkedListStack(); + +/* Elements push onto stack */ +stack.push(1); +stack.push(3); +stack.push(2); +stack.push(5); +stack.push(4); +console.log('Stack stack = ' + stack.toArray()); + +/* Return list for printing */ +const peek = stack.peek(); +console.log('Stack top element peek = ' + peek); + +/* Element pop from stack */ +const pop = stack.pop(); +console.log('Pop element pop = ' + pop + ', after pop, stack = ' + stack.toArray()); + +/* Get the length of the stack */ +const size = stack.size; +console.log('Stack length size = ' + size); + +/* Check if empty */ +const isEmpty = stack.isEmpty(); +console.log('Stack is empty = ' + isEmpty); + +export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/queue.ts b/en/codes/typescript/chapter_stack_and_queue/queue.ts new file mode 100644 index 000000000..71e4f0fbd --- /dev/null +++ b/en/codes/typescript/chapter_stack_and_queue/queue.ts @@ -0,0 +1,37 @@ +/** + * File: queue.ts + * Created Time: 2022-12-05 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +/* Driver Code */ +/* Access front of the queue element */ +// TypeScript has no built-in queue, can use Array as queue +const queue: number[] = []; + +/* Elements enqueue */ +queue.push(1); +queue.push(3); +queue.push(2); +queue.push(5); +queue.push(4); +console.log('Queue queue =', queue); + +/* Return list for printing */ +const peek = queue[0]; +console.log('Front element peek =', peek); + +/* Element dequeue */ +// Underlying is array, so shift() method has O(n) time complexity +const pop = queue.shift(); +console.log('Dequeue element pop =', pop, ', after dequeue, queue = ', queue); + +/* Get the length of the queue */ +const size = queue.length; +console.log('Queue length size =', size); + +/* Check if the queue is empty */ +const isEmpty = queue.length === 0; +console.log('Queue is empty = ', isEmpty); + +export {}; diff --git a/en/codes/typescript/chapter_stack_and_queue/stack.ts b/en/codes/typescript/chapter_stack_and_queue/stack.ts new file mode 100644 index 000000000..a503dcf5d --- /dev/null +++ b/en/codes/typescript/chapter_stack_and_queue/stack.ts @@ -0,0 +1,37 @@ +/** + * File: stack.ts + * Created Time: 2022-12-04 + * Author: S-N-O-R-L-A-X (snorlax.xu@outlook.com) + */ + +/* Driver Code */ +/* Access top of the stack element */ +// TypeScript has no built-in stack class, can use Array as stack +const stack: number[] = []; + +/* Elements push onto stack */ +stack.push(1); +stack.push(3); +stack.push(2); +stack.push(5); +stack.push(4); +console.log('Stack stack =', stack); + +/* Return list for printing */ +const peek = stack[stack.length - 1]; +console.log('Stack top element peek =', peek); + +/* Element pop from stack */ +const pop = stack.pop(); +console.log('Pop element pop =', pop); +console.log('After pop, stack =', stack); + +/* Get the length of the stack */ +const size = stack.length; +console.log('Stack length size =', size); + +/* Check if empty */ +const isEmpty = stack.length === 0; +console.log('Is stack empty =', isEmpty); + +export {}; diff --git a/en/codes/typescript/chapter_tree/array_binary_tree.ts b/en/codes/typescript/chapter_tree/array_binary_tree.ts new file mode 100644 index 000000000..c97205536 --- /dev/null +++ b/en/codes/typescript/chapter_tree/array_binary_tree.ts @@ -0,0 +1,151 @@ +/** + * File: array_binary_tree.js + * Created Time: 2023-08-09 + * Author: yuan0221 (yl1452491917@gmail.com) + */ + +import { arrToTree } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; + +type Order = 'pre' | 'in' | 'post'; + +/* Binary tree class represented by array */ +class ArrayBinaryTree { + #tree: (number | null)[]; + + /* Constructor */ + constructor(arr: (number | null)[]) { + this.#tree = arr; + } + + /* List capacity */ + size(): number { + return this.#tree.length; + } + + /* Get value of node at index i */ + val(i: number): number | null { + // If index out of bounds, return null to represent empty position + if (i < 0 || i >= this.size()) return null; + return this.#tree[i]; + } + + /* Get index of left child node of node at index i */ + left(i: number): number { + return 2 * i + 1; + } + + /* Get index of right child node of node at index i */ + right(i: number): number { + return 2 * i + 2; + } + + /* Get index of parent node of node at index i */ + parent(i: number): number { + return Math.floor((i - 1) / 2); // Floor division + } + + /* Level-order traversal */ + levelOrder(): number[] { + let res = []; + // Traverse array directly + for (let i = 0; i < this.size(); i++) { + if (this.val(i) !== null) res.push(this.val(i)); + } + return res; + } + + /* Depth-first traversal */ + #dfs(i: number, order: Order, res: (number | null)[]): void { + // If empty position, return + if (this.val(i) === null) return; + // Preorder traversal + if (order === 'pre') res.push(this.val(i)); + this.#dfs(this.left(i), order, res); + // Inorder traversal + if (order === 'in') res.push(this.val(i)); + this.#dfs(this.right(i), order, res); + // Postorder traversal + if (order === 'post') res.push(this.val(i)); + } + + /* Preorder traversal */ + preOrder(): (number | null)[] { + const res = []; + this.#dfs(0, 'pre', res); + return res; + } + + /* Inorder traversal */ + inOrder(): (number | null)[] { + const res = []; + this.#dfs(0, 'in', res); + return res; + } + + /* Postorder traversal */ + postOrder(): (number | null)[] { + const res = []; + this.#dfs(0, 'post', res); + return res; + } +} + +/* Driver Code */ +// Initialize binary tree +// Here we use a function to generate a binary tree directly from an array +const arr = Array.of( + 1, + 2, + 3, + 4, + null, + 6, + 7, + 8, + 9, + null, + null, + 12, + null, + null, + 15 +); + +const root = arrToTree(arr); +console.log('\nInitialize binary tree\n'); +console.log('Array representation of binary tree:'); +console.log(arr); +console.log('Linked list representation of binary tree:'); +printTree(root); + +// Binary tree class represented by array +const abt = new ArrayBinaryTree(arr); + +// Access node +const i = 1; +const l = abt.left(i); +const r = abt.right(i); +const p = abt.parent(i); +console.log('\nCurrent node index is ' + i + ', value is ' + abt.val(i)); +console.log( + 'Its left child node index is ' + l + ', value is ' + (l === null ? 'null' : abt.val(l)) +); +console.log( + 'Its right child node index is ' + r + ', value is ' + (r === null ? 'null' : abt.val(r)) +); +console.log( + 'Its parent node index is ' + p + ', value is ' + (p === null ? 'null' : abt.val(p)) +); + +// Traverse tree +let res = abt.levelOrder(); +console.log('\nLevel-order traversal is:' + res); +res = abt.preOrder(); +console.log('Preorder traversal is:' + res); +res = abt.inOrder(); +console.log('Inorder traversal is:' + res); +res = abt.postOrder(); +console.log('Postorder traversal is:' + res); + +export {}; diff --git a/en/codes/typescript/chapter_tree/avl_tree.ts b/en/codes/typescript/chapter_tree/avl_tree.ts new file mode 100644 index 000000000..39cc02d15 --- /dev/null +++ b/en/codes/typescript/chapter_tree/avl_tree.ts @@ -0,0 +1,222 @@ +/** + * File: avl_tree.ts + * Created Time: 2023-02-06 + * Author: Justin (xiefahit@gmail.com) + */ + +import { TreeNode } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; + +/* AVL tree */ +class AVLTree { + root: TreeNode; + /* Constructor */ + constructor() { + this.root = null; // Root node + } + + /* Get node height */ + height(node: TreeNode): number { + // Empty node height is -1, leaf node height is 0 + return node === null ? -1 : node.height; + } + + /* Update node height */ + private updateHeight(node: TreeNode): void { + // Node height equals the height of the tallest subtree + 1 + node.height = + Math.max(this.height(node.left), this.height(node.right)) + 1; + } + + /* Get balance factor */ + balanceFactor(node: TreeNode): number { + // Empty node balance factor is 0 + if (node === null) return 0; + // Node balance factor = left subtree height - right subtree height + return this.height(node.left) - this.height(node.right); + } + + /* Right rotation operation */ + private rightRotate(node: TreeNode): TreeNode { + const child = node.left; + const grandChild = child.right; + // Using child as pivot, rotate node to the right + child.right = node; + node.left = grandChild; + // Update node height + this.updateHeight(node); + this.updateHeight(child); + // Return root node of subtree after rotation + return child; + } + + /* Left rotation operation */ + private leftRotate(node: TreeNode): TreeNode { + const child = node.right; + const grandChild = child.left; + // Using child as pivot, rotate node to the left + child.left = node; + node.right = grandChild; + // Update node height + this.updateHeight(node); + this.updateHeight(child); + // Return root node of subtree after rotation + return child; + } + + /* Perform rotation operation to restore balance to this subtree */ + private rotate(node: TreeNode): TreeNode { + // Get balance factor of node + const balanceFactor = this.balanceFactor(node); + // Left-leaning tree + if (balanceFactor > 1) { + if (this.balanceFactor(node.left) >= 0) { + // Right rotation + return this.rightRotate(node); + } else { + // First left rotation then right rotation + node.left = this.leftRotate(node.left); + return this.rightRotate(node); + } + } + // Right-leaning tree + if (balanceFactor < -1) { + if (this.balanceFactor(node.right) <= 0) { + // Left rotation + return this.leftRotate(node); + } else { + // First right rotation then left rotation + node.right = this.rightRotate(node.right); + return this.leftRotate(node); + } + } + // Balanced tree, no rotation needed, return directly + return node; + } + + /* Insert node */ + insert(val: number): void { + this.root = this.insertHelper(this.root, val); + } + + /* Recursively insert node (helper method) */ + private insertHelper(node: TreeNode, val: number): TreeNode { + if (node === null) return new TreeNode(val); + /* 1. Find insertion position and insert node */ + if (val < node.val) { + node.left = this.insertHelper(node.left, val); + } else if (val > node.val) { + node.right = this.insertHelper(node.right, val); + } else { + return node; // Duplicate node not inserted, return directly + } + this.updateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = this.rotate(node); + // Return root node of subtree + return node; + } + + /* Remove node */ + remove(val: number): void { + this.root = this.removeHelper(this.root, val); + } + + /* Recursively delete node (helper method) */ + private removeHelper(node: TreeNode, val: number): TreeNode { + if (node === null) return null; + /* 1. Find node and delete */ + if (val < node.val) { + node.left = this.removeHelper(node.left, val); + } else if (val > node.val) { + node.right = this.removeHelper(node.right, val); + } else { + if (node.left === null || node.right === null) { + const child = node.left !== null ? node.left : node.right; + // Number of child nodes = 0, delete node directly and return + if (child === null) { + return null; + } else { + // Number of child nodes = 1, delete node directly + node = child; + } + } else { + // Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it + let temp = node.right; + while (temp.left !== null) { + temp = temp.left; + } + node.right = this.removeHelper(node.right, temp.val); + node.val = temp.val; + } + } + this.updateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to this subtree */ + node = this.rotate(node); + // Return root node of subtree + return node; + } + + /* Search node */ + search(val: number): TreeNode { + let cur = this.root; + // Loop search, exit after passing leaf node + while (cur !== null) { + if (cur.val < val) { + // Target node is in cur's right subtree + cur = cur.right; + } else if (cur.val > val) { + // Target node is in cur's left subtree + cur = cur.left; + } else { + // Found target node, exit loop + break; + } + } + // Return target node + return cur; + } +} + +function testInsert(tree: AVLTree, val: number): void { + tree.insert(val); + console.log('\nInsert node ' + val + ', AVL tree is'); + printTree(tree.root); +} + +function testRemove(tree: AVLTree, val: number): void { + tree.remove(val); + console.log('\nRemove node ' + val + ', AVL tree is'); + printTree(tree.root); +} + +/* Driver Code */ +/* Please pay attention to how the AVL tree maintains balance after inserting nodes */ +const avlTree = new AVLTree(); +/* Insert node */ +// Delete nodes +testInsert(avlTree, 1); +testInsert(avlTree, 2); +testInsert(avlTree, 3); +testInsert(avlTree, 4); +testInsert(avlTree, 5); +testInsert(avlTree, 8); +testInsert(avlTree, 7); +testInsert(avlTree, 9); +testInsert(avlTree, 10); +testInsert(avlTree, 6); + +/* Please pay attention to how the AVL tree maintains balance after deleting nodes */ +testInsert(avlTree, 7); + +/* Remove node */ +// Delete node with degree 1 +testRemove(avlTree, 8); // Delete node with degree 2 +testRemove(avlTree, 5); // Remove node with degree 1 +testRemove(avlTree, 4); // Remove node with degree 2 + +/* Search node */ +const node = avlTree.search(7); +console.log('\nFound node object is', node, ', node value = ' + node.val); + +export {}; diff --git a/en/codes/typescript/chapter_tree/binary_search_tree.ts b/en/codes/typescript/chapter_tree/binary_search_tree.ts new file mode 100644 index 000000000..25005d8e9 --- /dev/null +++ b/en/codes/typescript/chapter_tree/binary_search_tree.ts @@ -0,0 +1,146 @@ +/** + * File: binary_search_tree.ts + * Created Time: 2022-12-14 + * Author: Justin (xiefahit@gmail.com) + */ + +import { TreeNode } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; + +/* Binary search tree */ +class BinarySearchTree { + private root: TreeNode | null; + + /* Constructor */ + constructor() { + // Initialize empty tree + this.root = null; + } + + /* Get binary tree root node */ + getRoot(): TreeNode | null { + return this.root; + } + + /* Search node */ + search(num: number): TreeNode | null { + let cur = this.root; + // Loop search, exit after passing leaf node + while (cur !== null) { + // Target node is in cur's right subtree + if (cur.val < num) cur = cur.right; + // Target node is in cur's left subtree + else if (cur.val > num) cur = cur.left; + // Found target node, exit loop + else break; + } + // Return target node + return cur; + } + + /* Insert node */ + insert(num: number): void { + // If tree is empty, initialize root node + if (this.root === null) { + this.root = new TreeNode(num); + return; + } + let cur: TreeNode | null = this.root, + pre: TreeNode | null = null; + // Loop search, exit after passing leaf node + while (cur !== null) { + // Found duplicate node, return directly + if (cur.val === num) return; + pre = cur; + // Insertion position is in cur's right subtree + if (cur.val < num) cur = cur.right; + // Insertion position is in cur's left subtree + else cur = cur.left; + } + // Insert node + const node = new TreeNode(num); + if (pre!.val < num) pre!.right = node; + else pre!.left = node; + } + + /* Remove node */ + remove(num: number): void { + // If tree is empty, return directly + if (this.root === null) return; + let cur: TreeNode | null = this.root, + pre: TreeNode | null = null; + // Loop search, exit after passing leaf node + while (cur !== null) { + // Found node to delete, exit loop + if (cur.val === num) break; + pre = cur; + // Node to delete is in cur's right subtree + if (cur.val < num) cur = cur.right; + // Node to delete is in cur's left subtree + else cur = cur.left; + } + // If no node to delete, return directly + if (cur === null) return; + // Number of child nodes = 0 or 1 + if (cur.left === null || cur.right === null) { + // When number of child nodes = 0 / 1, child = null / that child node + const child: TreeNode | null = + cur.left !== null ? cur.left : cur.right; + // Delete node cur + if (cur !== this.root) { + if (pre!.left === cur) pre!.left = child; + else pre!.right = child; + } else { + // If deleted node is root node, reassign root node + this.root = child; + } + } + // Number of child nodes = 2 + else { + // Get next node of cur in inorder traversal + let tmp: TreeNode | null = cur.right; + while (tmp!.left !== null) { + tmp = tmp!.left; + } + // Recursively delete node tmp + this.remove(tmp!.val); + // Replace cur with tmp + cur.val = tmp!.val; + } + } +} + +/* Driver Code */ +/* Initialize binary search tree */ +const bst = new BinarySearchTree(); +// Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree +const nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]; +for (const num of nums) { + bst.insert(num); +} +console.log('\nInitialized binary tree is\n'); +printTree(bst.getRoot()); + +/* Search node */ +const node = bst.search(7); +console.log( + '\nFound node object is ' + node + ', node value = ' + (node ? node.val : 'null') +); + +/* Insert node */ +bst.insert(16); +console.log('\nAfter inserting node 16, binary tree is\n'); +printTree(bst.getRoot()); + +/* Remove node */ +bst.remove(1); +console.log('\nAfter removing node 1, binary tree is\n'); +printTree(bst.getRoot()); +bst.remove(2); +console.log('\nAfter removing node 2, binary tree is\n'); +printTree(bst.getRoot()); +bst.remove(4); +console.log('\nAfter removing node 4, binary tree is\n'); +printTree(bst.getRoot()); + +export {}; diff --git a/en/codes/typescript/chapter_tree/binary_tree.ts b/en/codes/typescript/chapter_tree/binary_tree.ts new file mode 100644 index 000000000..5e82a6b4b --- /dev/null +++ b/en/codes/typescript/chapter_tree/binary_tree.ts @@ -0,0 +1,37 @@ +/** + * File: binary_tree.ts + * Created Time: 2022-12-13 + * Author: Justin (xiefahit@gmail.com) + */ + +import { TreeNode } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; + +/* Initialize binary tree */ +// Initialize nodes +let n1 = new TreeNode(1), + n2 = new TreeNode(2), + n3 = new TreeNode(3), + n4 = new TreeNode(4), + n5 = new TreeNode(5); +// Build references (pointers) between nodes +n1.left = n2; +n1.right = n3; +n2.left = n4; +n2.right = n5; +console.log('\nInitialize binary tree\n'); +printTree(n1); + +/* Insert node P between n1 -> n2 */ +const P = new TreeNode(0); +// Delete node +n1.left = P; +P.left = n2; +console.log('\nAfter inserting node P\n'); +printTree(n1); +// Remove node P +n1.left = n2; +console.log('\nAfter removing node P\n'); +printTree(n1); + +export {}; diff --git a/en/codes/typescript/chapter_tree/binary_tree_bfs.ts b/en/codes/typescript/chapter_tree/binary_tree_bfs.ts new file mode 100644 index 000000000..5ebc90a28 --- /dev/null +++ b/en/codes/typescript/chapter_tree/binary_tree_bfs.ts @@ -0,0 +1,41 @@ +/** + * File: binary_tree_bfs.ts + * Created Time: 2022-12-14 + * Author: Justin (xiefahit@gmail.com) + */ + +import { type TreeNode } from '../modules/TreeNode'; +import { arrToTree } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; + +/* Level-order traversal */ +function levelOrder(root: TreeNode | null): number[] { + // Initialize queue, add root node + const queue = [root]; + // Initialize a list to save the traversal sequence + const list: number[] = []; + while (queue.length) { + let node = queue.shift() as TreeNode; // Dequeue + list.push(node.val); // Save node value + if (node.left) { + queue.push(node.left); // Left child node enqueue + } + if (node.right) { + queue.push(node.right); // Right child node enqueue + } + } + return list; +} + +/* Driver Code */ +/* Initialize binary tree */ +// Here we use a function to generate a binary tree directly from an array +const root = arrToTree([1, 2, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree\n'); +printTree(root); + +/* Level-order traversal */ +const list = levelOrder(root); +console.log('\nLevel-order traversal node print sequence = ' + list); + +export {}; diff --git a/en/codes/typescript/chapter_tree/binary_tree_dfs.ts b/en/codes/typescript/chapter_tree/binary_tree_dfs.ts new file mode 100644 index 000000000..32555af3d --- /dev/null +++ b/en/codes/typescript/chapter_tree/binary_tree_dfs.ts @@ -0,0 +1,69 @@ +/** + * File: binary_tree_dfs.ts + * Created Time: 2022-12-14 + * Author: Justin (xiefahit@gmail.com) + */ + +import { type TreeNode } from '../modules/TreeNode'; +import { arrToTree } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; + +// Initialize list for storing traversal sequence +const list: number[] = []; + +/* Preorder traversal */ +function preOrder(root: TreeNode | null): void { + if (root === null) { + return; + } + // Visit priority: root node -> left subtree -> right subtree + list.push(root.val); + preOrder(root.left); + preOrder(root.right); +} + +/* Inorder traversal */ +function inOrder(root: TreeNode | null): void { + if (root === null) { + return; + } + // Visit priority: left subtree -> root node -> right subtree + inOrder(root.left); + list.push(root.val); + inOrder(root.right); +} + +/* Postorder traversal */ +function postOrder(root: TreeNode | null): void { + if (root === null) { + return; + } + // Visit priority: left subtree -> right subtree -> root node + postOrder(root.left); + postOrder(root.right); + list.push(root.val); +} + +/* Driver Code */ +/* Initialize binary tree */ +// Here we use a function to generate a binary tree directly from an array +const root = arrToTree([1, 2, 3, 4, 5, 6, 7]); +console.log('\nInitialize binary tree\n'); +printTree(root); + +/* Preorder traversal */ +list.length = 0; +preOrder(root); +console.log('\nPreorder traversal node print sequence = ' + list); + +/* Inorder traversal */ +list.length = 0; +inOrder(root); +console.log('\nInorder traversal node print sequence = ' + list); + +/* Postorder traversal */ +list.length = 0; +postOrder(root); +console.log('\nPostorder traversal node print sequence = ' + list); + +export {}; diff --git a/en/codes/typescript/modules/ListNode.ts b/en/codes/typescript/modules/ListNode.ts new file mode 100644 index 000000000..b20fb01b5 --- /dev/null +++ b/en/codes/typescript/modules/ListNode.ts @@ -0,0 +1,28 @@ +/** + * File: ListNode.ts + * Created Time: 2022-12-10 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Linked list node */ +class ListNode { + val: number; + next: ListNode | null; + constructor(val?: number, next?: ListNode | null) { + this.val = val === undefined ? 0 : val; + this.next = next === undefined ? null : next; + } +} + +/* Deserialize array to linked list */ +function arrToLinkedList(arr: number[]): ListNode | null { + const dum: ListNode = new ListNode(0); + let head = dum; + for (const val of arr) { + head.next = new ListNode(val); + head = head.next; + } + return dum.next; +} + +export { ListNode, arrToLinkedList }; diff --git a/en/codes/typescript/modules/PrintUtil.ts b/en/codes/typescript/modules/PrintUtil.ts new file mode 100644 index 000000000..6cb0abc61 --- /dev/null +++ b/en/codes/typescript/modules/PrintUtil.ts @@ -0,0 +1,93 @@ +/** + * File: PrintUtil.ts + * Created Time: 2022-12-13 + * Author: Justin (xiefahit@gmail.com) + */ + +import { ListNode } from './ListNode'; +import { TreeNode, arrToTree } from './TreeNode'; + +/* Print linked list */ +function printLinkedList(head: ListNode | null): void { + const list: string[] = []; + while (head !== null) { + list.push(head.val.toString()); + head = head.next; + } + console.log(list.join(' -> ')); +} + +class Trunk { + prev: Trunk | null; + str: string; + + constructor(prev: Trunk | null, str: string) { + this.prev = prev; + this.str = str; + } +} + +/** + * Print binary tree + * This tree printer is borrowed from TECHIE DELIGHT + * https://www.techiedelight.com/c-program-print-binary-tree/ + */ +function printTree(root: TreeNode | null) { + printTreeHelper(root, null, false); +} + +/* Print binary tree */ +function printTreeHelper( + root: TreeNode | null, + prev: Trunk | null, + isRight: boolean +) { + if (root === null) { + return; + } + + let prev_str = ' '; + const trunk = new Trunk(prev, prev_str); + + printTreeHelper(root.right, trunk, true); + + if (prev === null) { + trunk.str = '———'; + } else if (isRight) { + trunk.str = '/———'; + prev_str = ' |'; + } else { + trunk.str = '\\———'; + prev.str = prev_str; + } + + showTrunks(trunk); + console.log(' ' + root.val); + + if (prev) { + prev.str = prev_str; + } + trunk.str = ' |'; + + printTreeHelper(root.left, trunk, false); +} + +function showTrunks(p: Trunk | null) { + if (p === null) { + return; + } + + showTrunks(p.prev); + process.stdout.write(p.str); +} + +/* Print heap */ +function printHeap(arr: number[]): void { + console.log('Heap array representation:'); + console.log(arr); + console.log('Heap tree representation:'); + const root = arrToTree(arr); + printTree(root); +} + +export { printLinkedList, printTree, printHeap }; diff --git a/en/codes/typescript/modules/TreeNode.ts b/en/codes/typescript/modules/TreeNode.ts new file mode 100644 index 000000000..ae854402d --- /dev/null +++ b/en/codes/typescript/modules/TreeNode.ts @@ -0,0 +1,37 @@ +/** + * File: TreeNode.ts + * Created Time: 2022-12-13 + * Author: Justin (xiefahit@gmail.com) + */ + +/* Binary tree node */ +class TreeNode { + val: number; // Node value + height: number; // Node height + left: TreeNode | null; // Left child pointer + right: TreeNode | null; // Right child pointer + constructor( + val?: number, + height?: number, + left?: TreeNode | null, + right?: TreeNode | null + ) { + this.val = val === undefined ? 0 : val; + this.height = height === undefined ? 0 : height; + this.left = left === undefined ? null : left; + this.right = right === undefined ? null : right; + } +} + +/* Deserialize array to binary tree */ +function arrToTree(arr: (number | null)[], i: number = 0): TreeNode | null { + if (i < 0 || i >= arr.length || arr[i] === null) { + return null; + } + let root = new TreeNode(arr[i]); + root.left = arrToTree(arr, 2 * i + 1); + root.right = arrToTree(arr, 2 * i + 2); + return root; +} + +export { TreeNode, arrToTree }; diff --git a/en/codes/typescript/modules/Vertex.ts b/en/codes/typescript/modules/Vertex.ts new file mode 100644 index 000000000..7c1f167dc --- /dev/null +++ b/en/codes/typescript/modules/Vertex.ts @@ -0,0 +1,33 @@ +/** + * File: Vertex.ts + * Created Time: 2023-02-15 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +/* Vertex class */ +class Vertex { + val: number; + constructor(val: number) { + this.val = val; + } + + /* Input value list vals, return vertex list vets */ + public static valsToVets(vals: number[]): Vertex[] { + const vets: Vertex[] = []; + for (let i = 0; i < vals.length; i++) { + vets[i] = new Vertex(vals[i]); + } + return vets; + } + + /* Input vertex list vets, return value list vals */ + public static vetsToVals(vets: Vertex[]): number[] { + const vals: number[] = []; + for (const vet of vets) { + vals.push(vet.val); + } + return vals; + } +} + +export { Vertex }; diff --git a/en/codes/typescript/package.json b/en/codes/typescript/package.json new file mode 100644 index 000000000..ae81c64fe --- /dev/null +++ b/en/codes/typescript/package.json @@ -0,0 +1,11 @@ +{ + "private": true, + "type": "module", + "scripts": { + "check": "tsc" + }, + "devDependencies": { + "@types/node": "^24.9.2", + "typescript": "^5.9.3" + } +} diff --git a/en/codes/typescript/tsconfig.json b/en/codes/typescript/tsconfig.json new file mode 100644 index 000000000..954efb8e7 --- /dev/null +++ b/en/codes/typescript/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "module": "esnext", + "moduleResolution": "node", + "types": ["@types/node"], + "noEmit": true, + "target": "esnext", + }, + "include": ["chapter_*/*.ts"], + "exclude": ["node_modules"] +} diff --git a/en/docs/chapter_appendix/installation.md b/en/docs/chapter_appendix/installation.md index d8aebde65..4b97078ca 100644 --- a/en/docs/chapter_appendix/installation.md +++ b/en/docs/chapter_appendix/installation.md @@ -1,6 +1,6 @@ # Programming Environment Installation -## Installing IDE +## Installing Ide We recommend using the open-source and lightweight VS Code as the local integrated development environment (IDE). Visit the [VS Code official website](https://code.visualstudio.com/), and download and install the appropriate version of VS Code according to your operating system. @@ -18,7 +18,7 @@ VS Code has a powerful ecosystem of extensions that supports running and debuggi 2. Search for `python` in the VS Code extension marketplace and install the Python Extension Pack. 3. (Optional) Enter `pip install black` on the command line to install the code formatter. -### C/C++ Environment +### C/c++ Environment 1. Windows systems need to install [MinGW](https://sourceforge.net/projects/mingw-w64/files/) ([configuration tutorial](https://blog.csdn.net/qq_33698226/article/details/129031241)); macOS comes with Clang built-in and does not require installation. 2. Search for `c++` in the VS Code extension marketplace and install the C/C++ Extension Pack. @@ -46,12 +46,12 @@ VS Code has a powerful ecosystem of extensions that supports running and debuggi 1. Download and install [Swift](https://www.swift.org/download/). 2. Search for `swift` in the VS Code extension marketplace and install [Swift for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=sswg.swift-lang). -### JavaScript Environment +### Javascript Environment 1. Download and install [Node.js](https://nodejs.org/en/). 2. (Optional) Search for `Prettier` in the VS Code extension marketplace and install the code formatter. -### TypeScript Environment +### Typescript Environment 1. Follow the same installation steps as the JavaScript environment. 2. Install [TypeScript Execute (tsx)](https://github.com/privatenumber/tsx?tab=readme-ov-file#global-installation). diff --git a/en/docs/chapter_array_and_linkedlist/array.md b/en/docs/chapter_array_and_linkedlist/array.md index bd389e381..97f385660 100755 --- a/en/docs/chapter_array_and_linkedlist/array.md +++ b/en/docs/chapter_array_and_linkedlist/array.md @@ -134,7 +134,7 @@ We can choose between two array initialization methods based on our needs: witho const nums = [_]i32{ 1, 3, 2, 5, 4 }; ``` -??? pythontutor "Visualize code execution" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B0%E7%BB%84%0Aarr%20%3D%20%5B0%5D%20*%205%20%20%23%20%5B%200,%200,%200,%200,%200%20%5D%0Anums%20%3D%20%5B1,%203,%202,%205,%204%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/docs/chapter_array_and_linkedlist/index.md b/en/docs/chapter_array_and_linkedlist/index.md index 1a6747c47..709f53a7c 100644 --- a/en/docs/chapter_array_and_linkedlist/index.md +++ b/en/docs/chapter_array_and_linkedlist/index.md @@ -1,6 +1,6 @@ -# Arrays and Linked Lists +# Array and Linked List -![Arrays and Linked Lists](../assets/covers/chapter_array_and_linkedlist.jpg) +![Array and Linked List](../assets/covers/chapter_array_and_linkedlist.jpg) !!! abstract diff --git a/en/docs/chapter_array_and_linkedlist/linked_list.md b/en/docs/chapter_array_and_linkedlist/linked_list.md index 731884866..db98da704 100755 --- a/en/docs/chapter_array_and_linkedlist/linked_list.md +++ b/en/docs/chapter_array_and_linkedlist/linked_list.md @@ -450,7 +450,7 @@ Building a linked list involves two steps: first, initializing each node object; n3.next = &n4; ``` -??? pythontutor "Visualize code execution" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22%E9%93%BE%E8%A1%A8%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%90%8E%E7%BB%A7%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E9%93%BE%E8%A1%A8%201%20-%3E%203%20-%3E%202%20-%3E%205%20-%3E%204%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%90%84%E4%B8%AA%E8%8A%82%E7%82%B9%0A%20%20%20%20n0%20%3D%20ListNode%281%29%0A%20%20%20%20n1%20%3D%20ListNode%283%29%0A%20%20%20%20n2%20%3D%20ListNode%282%29%0A%20%20%20%20n3%20%3D%20ListNode%285%29%0A%20%20%20%20n4%20%3D%20ListNode%284%29%0A%20%20%20%20%23%20%E6%9E%84%E5%BB%BA%E8%8A%82%E7%82%B9%E4%B9%8B%E9%97%B4%E7%9A%84%E5%BC%95%E7%94%A8%0A%20%20%20%20n0.next%20%3D%20n1%0A%20%20%20%20n1.next%20%3D%20n2%0A%20%20%20%20n2.next%20%3D%20n3%0A%20%20%20%20n3.next%20%3D%20n4&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/docs/chapter_array_and_linkedlist/list.md b/en/docs/chapter_array_and_linkedlist/list.md index 7fbf5ab8a..cb920f88c 100755 --- a/en/docs/chapter_array_and_linkedlist/list.md +++ b/en/docs/chapter_array_and_linkedlist/list.md @@ -156,7 +156,7 @@ We typically use two initialization methods: "without initial values" and "with try nums.appendSlice(&[_]i32{ 1, 3, 2, 5, 4 }); ``` -??? pythontutor "Visualize Execution" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20%23%20%E6%97%A0%E5%88%9D%E5%A7%8B%E5%80%BC%0A%20%20%20%20nums1%20%3D%20%5B%5D%0A%20%20%20%20%23%20%E6%9C%89%E5%88%9D%E5%A7%8B%E5%80%BC%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false @@ -297,7 +297,7 @@ Since a list is essentially an array, we can access and update elements in $O(1) nums.items[1] = 0; // Update element at index 1 to 0 ``` -??? pythontutor "Visualize Execution" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%0A%20%20%20%20%23%20%E8%AE%BF%E9%97%AE%E5%85%83%E7%B4%A0%0A%20%20%20%20num%20%3D%20nums%5B1%5D%20%20%23%20%E8%AE%BF%E9%97%AE%E7%B4%A2%E5%BC%95%201%20%E5%A4%84%E7%9A%84%E5%85%83%E7%B4%A0%0A%0A%20%20%20%20%23%20%E6%9B%B4%E6%96%B0%E5%85%83%E7%B4%A0%0A%20%20%20%20nums%5B1%5D%20%3D%200%20%20%20%20%23%20%E5%B0%86%E7%B4%A2%E5%BC%95%201%20%E5%A4%84%E7%9A%84%E5%85%83%E7%B4%A0%E6%9B%B4%E6%96%B0%E4%B8%BA%200&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false @@ -571,7 +571,7 @@ Compared to arrays, lists can freely add and delete elements. Adding an element _ = nums.orderedRemove(3); // Delete element at index 3 ``` -??? pythontutor "Visualize Execution" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E6%9C%89%E5%88%9D%E5%A7%8B%E5%80%BC%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%B8%85%E7%A9%BA%E5%88%97%E8%A1%A8%0A%20%20%20%20nums.clear%28%29%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%9C%A8%E5%B0%BE%E9%83%A8%E6%B7%BB%E5%8A%A0%E5%85%83%E7%B4%A0%0A%20%20%20%20nums.append%281%29%0A%20%20%20%20nums.append%283%29%0A%20%20%20%20nums.append%282%29%0A%20%20%20%20nums.append%285%29%0A%20%20%20%20nums.append%284%29%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%9C%A8%E4%B8%AD%E9%97%B4%E6%8F%92%E5%85%A5%E5%85%83%E7%B4%A0%0A%20%20%20%20nums.insert%283,%206%29%20%20%23%20%E5%9C%A8%E7%B4%A2%E5%BC%95%203%20%E5%A4%84%E6%8F%92%E5%85%A5%E6%95%B0%E5%AD%97%206%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E5%85%83%E7%B4%A0%0A%20%20%20%20nums.pop%283%29%20%20%20%20%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E7%B4%A2%E5%BC%95%203%20%E5%A4%84%E7%9A%84%E5%85%83%E7%B4%A0&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false @@ -789,7 +789,7 @@ Like arrays, lists can be traversed by index or by directly iterating through el } ``` -??? pythontutor "Visualize Execution" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E9%80%9A%E8%BF%87%E7%B4%A2%E5%BC%95%E9%81%8D%E5%8E%86%E5%88%97%E8%A1%A8%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20nums%5Bi%5D%0A%0A%20%20%20%20%23%20%E7%9B%B4%E6%8E%A5%E9%81%8D%E5%8E%86%E5%88%97%E8%A1%A8%E5%85%83%E7%B4%A0%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20num&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false @@ -910,7 +910,7 @@ Given a new list `nums1`, we can concatenate it to the end of the original list. try nums.insertSlice(nums.items.len, nums1.items); // Concatenate list nums1 to the end of nums ``` -??? pythontutor "Visualize Execution" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%8B%BC%E6%8E%A5%E4%B8%A4%E4%B8%AA%E5%88%97%E8%A1%A8%0A%20%20%20%20nums1%20%3D%20%5B6,%208,%207,%2010,%209%5D%0A%20%20%20%20nums%20%2B%3D%20nums1%20%20%23%20%E5%B0%86%E5%88%97%E8%A1%A8%20nums1%20%E6%8B%BC%E6%8E%A5%E5%88%B0%20nums%20%E4%B9%8B%E5%90%8E&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false @@ -1015,7 +1015,7 @@ After sorting a list, we can use "binary search" and "two-pointer" algorithms, w std.sort.sort(i32, nums.items, {}, comptime std.sort.asc(i32)); ``` -??? pythontutor "Visualize Execution" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%8E%92%E5%BA%8F%E5%88%97%E8%A1%A8%0A%20%20%20%20nums.sort%28%29%20%20%23%20%E6%8E%92%E5%BA%8F%E5%90%8E%EF%BC%8C%E5%88%97%E8%A1%A8%E5%85%83%E7%B4%A0%E4%BB%8E%E5%B0%8F%E5%88%B0%E5%A4%A7%E6%8E%92%E5%88%97&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/docs/chapter_array_and_linkedlist/summary.md b/en/docs/chapter_array_and_linkedlist/summary.md index d856f7889..53ab5a25d 100644 --- a/en/docs/chapter_array_and_linkedlist/summary.md +++ b/en/docs/chapter_array_and_linkedlist/summary.md @@ -1,6 +1,6 @@ # Summary -### Key Takeaways +### Key Review - Arrays and linked lists are two fundamental data structures, representing two different ways data can be stored in computer memory: contiguous memory storage and scattered memory storage. The characteristics of the two complement each other. - Arrays support random access and use less memory; however, inserting and deleting elements is inefficient, and the length is immutable after initialization. @@ -28,7 +28,7 @@ Linked lists are composed of nodes, with nodes connected through references (poi In contrast, array elements must be of the same type, so that the corresponding element position can be obtained by calculating the offset. For example, if an array contains both `int` and `long` types, with individual elements occupying 4 bytes and 8 bytes respectively, then the following formula cannot be used to calculate the offset, because the array contains two different "element lengths". ```shell -# Element memory address = Array memory address (first element memory address) + Element length * Element index +# Element Memory Address = Array Memory Address (first Element Memory address) + Element Length * Element Index ``` **Q**: After deleting node `P`, do we need to set `P.next` to `None`? diff --git a/en/docs/chapter_backtracking/n_queens_problem.md b/en/docs/chapter_backtracking/n_queens_problem.md index 4c2fc1275..2c4122293 100644 --- a/en/docs/chapter_backtracking/n_queens_problem.md +++ b/en/docs/chapter_backtracking/n_queens_problem.md @@ -1,4 +1,4 @@ -# n-queens problem +# N-Queens Problem !!! question @@ -12,7 +12,7 @@ The figure below illustrates the three constraints of this problem: **multiple q ![Constraints of the n-queens problem](n_queens_problem.assets/n_queens_constraints.png) -### Row-by-row placement strategy +### Row-By-Row Placement Strategy Since both the number of queens and the number of rows on the chessboard are $n$, we can easily derive a conclusion: **each row of the chessboard allows and only allows exactly one queen to be placed**. @@ -24,7 +24,7 @@ The figure below shows the row-by-row placement process for the 4-queens problem Essentially, **the row-by-row placement strategy serves a pruning function**, as it avoids all search branches where multiple queens appear in the same row. -### Column and diagonal pruning +### Column and Diagonal Pruning To satisfy the column constraint, we can use a boolean array `cols` of length $n$ to record whether each column has a queen. Before each placement decision, we use `cols` to prune columns that already have queens, and dynamically update the state of `cols` during backtracking. @@ -40,7 +40,7 @@ Similarly, **for all squares on an anti-diagonal, the sum $row + col$ is a const ![Handling column and diagonal constraints](n_queens_problem.assets/n_queens_cols_diagonals.png) -### Code implementation +### Code Implementation Please note that in an $n$-dimensional square matrix, the range of $row - col$ is $[-n + 1, n - 1]$, and the range of $row + col$ is $[0, 2n - 2]$. Therefore, the number of both main diagonals and anti-diagonals is $2n - 1$, meaning the length of both arrays `diags1` and `diags2` is $2n - 1$. diff --git a/en/docs/chapter_computational_complexity/iteration_and_recursion.md b/en/docs/chapter_computational_complexity/iteration_and_recursion.md index 232eb9c82..0c76de86b 100644 --- a/en/docs/chapter_computational_complexity/iteration_and_recursion.md +++ b/en/docs/chapter_computational_complexity/iteration_and_recursion.md @@ -6,7 +6,7 @@ In algorithms, repeatedly executing a task is very common and closely related to Iteration is a control structure for repeatedly executing a task. In iteration, a program repeatedly executes a segment of code under certain conditions until those conditions are no longer satisfied. -### for Loop +### For Loop The `for` loop is one of the most common forms of iteration, **suitable for use when the number of iterations is known in advance**. @@ -22,7 +22,7 @@ The figure below shows the flowchart of this summation function. The number of operations in this summation function is proportional to the input data size $n$, or has a "linear relationship". In fact, **time complexity describes precisely this "linear relationship"**. Related content will be introduced in detail in the next section. -### while Loop +### While Loop Similar to the `for` loop, the `while` loop is also a method for implementing iteration. In a `while` loop, the program first checks the condition in each round; if the condition is true, it continues execution, otherwise it ends the loop. diff --git a/en/docs/chapter_computational_complexity/time_complexity.md b/en/docs/chapter_computational_complexity/time_complexity.md index 7d8419eeb..2586d98f7 100644 --- a/en/docs/chapter_computational_complexity/time_complexity.md +++ b/en/docs/chapter_computational_complexity/time_complexity.md @@ -1,4 +1,4 @@ -# Time complexity +# Time Complexity Runtime can intuitively and accurately reflect the efficiency of an algorithm. If we want to accurately estimate the runtime of a piece of code, how should we proceed? @@ -224,7 +224,7 @@ $$ In reality, however, **counting an algorithm's runtime is neither reasonable nor realistic**. First, we do not want to tie the estimated time to the running platform, because algorithms need to run on various different platforms. Second, it is difficult to know the runtime of each type of operation, which brings great difficulty to the estimation process. -## Counting time growth trends +## Counting Time Growth Trends Time complexity analysis does not count the algorithm's runtime, **but rather counts the growth trend of the algorithm's runtime as the data volume increases**. @@ -536,7 +536,7 @@ Compared to directly counting the algorithm's runtime, what are the characterist - **The derivation method for time complexity is simpler**. Obviously, the running platform and the types of computational operations are both unrelated to the growth trend of the algorithm's runtime. Therefore, in time complexity analysis, we can simply treat the execution time of all computational operations as the same "unit time", thus simplifying "counting computational operation runtime" to "counting the number of computational operations", which greatly reduces the difficulty of estimation. - **Time complexity also has certain limitations**. For example, although algorithms `A` and `C` have the same time complexity, their actual runtimes differ significantly. Similarly, although algorithm `B` has a higher time complexity than `C`, when the input data size $n$ is small, algorithm `B` is clearly superior to algorithm `C`. In such cases, it is often difficult to judge the efficiency of algorithms based solely on time complexity. Of course, despite the above issues, complexity analysis remains the most effective and commonly used method for evaluating algorithm efficiency. -## Asymptotic upper bound of functions +## Asymptotic Upper Bound of Functions Given a function with input size $n$: @@ -755,13 +755,13 @@ As shown in the figure below, calculating the asymptotic upper bound is to find ![Asymptotic upper bound of a function](time_complexity.assets/asymptotic_upper_bound.png) -## Derivation method +## Derivation Method The asymptotic upper bound has a bit of mathematical flavor. If you feel you haven't fully understood it, don't worry. We can first master the derivation method, and gradually grasp its mathematical meaning through continuous practice. According to the definition, after determining $f(n)$, we can obtain the time complexity $O(f(n))$. So how do we determine the asymptotic upper bound $f(n)$? Overall, it is divided into two steps: first count the number of operations, then determine the asymptotic upper bound. -### Step 1: Count the number of operations +### Step 1: Count the Number of Operations For code, count from top to bottom line by line. However, since the constant coefficient $c$ in $c \cdot f(n)$ above can be of any size, **coefficients and constant terms in the number of operations $T(n)$ can all be ignored**. According to this principle, the following counting simplification techniques can be summarized. @@ -1043,7 +1043,7 @@ T(n) & = n^2 + n & \text{Simplified count (o.O)} \end{aligned} $$ -### Step 2: Determine the asymptotic upper bound +### Step 2: Determine the Asymptotic Upper Bound **Time complexity is determined by the highest-order term in $T(n)$**. This is because as $n$ tends to infinity, the highest-order term will play a dominant role, and the influence of other terms can be ignored. @@ -1059,7 +1059,7 @@ The table below shows some examples, where some exaggerated values are used to e | $n^3 + 10000n^2$ | $O(n^3)$ | | $2^n + 10000n^{10000}$ | $O(2^n)$ | -## Common types +## Common Types Let the input data size be $n$. Common time complexity types are shown in the figure below (arranged in order from low to high). @@ -1072,7 +1072,7 @@ $$ ![Common time complexity types](time_complexity.assets/time_complexity_common_types.png) -### Constant order $O(1)$ +### Constant Order $O(1)$ The number of operations in constant order is independent of the input data size $n$, meaning it does not change as $n$ changes. @@ -1082,7 +1082,7 @@ In the following function, although the number of operations `size` may be large [file]{time_complexity}-[class]{}-[func]{constant} ``` -### Linear order $O(n)$ +### Linear Order $O(n)$ The number of operations in linear order grows linearly relative to the input data size $n$. Linear order typically appears in single-layer loops: @@ -1098,7 +1098,7 @@ Operations such as traversing arrays and traversing linked lists have a time com It is worth noting that **the input data size $n$ should be determined according to the type of input data**. For example, in the first example, the variable $n$ is the input data size; in the second example, the array length $n$ is the data size. -### Quadratic order $O(n^2)$ +### Quadratic Order $O(n^2)$ The number of operations in quadratic order grows quadratically relative to the input data size $n$. Quadratic order typically appears in nested loops, where both the outer and inner loops have a time complexity of $O(n)$, resulting in an overall time complexity of $O(n^2)$: @@ -1116,7 +1116,7 @@ Taking bubble sort as an example, the outer loop executes $n - 1$ times, and the [file]{time_complexity}-[class]{}-[func]{bubble_sort} ``` -### Exponential order $O(2^n)$ +### Exponential Order $O(2^n)$ Biological "cell division" is a typical example of exponential order growth: the initial state is $1$ cell, after one round of division it becomes $2$, after two rounds it becomes $4$, and so on; after $n$ rounds of division there are $2^n$ cells. @@ -1136,7 +1136,7 @@ In actual algorithms, exponential order often appears in recursive functions. Fo Exponential order growth is very rapid and is common in exhaustive methods (brute force search, backtracking, etc.). For problems with large data scales, exponential order is unacceptable and typically requires dynamic programming or greedy algorithms to solve. -### Logarithmic order $O(\log n)$ +### Logarithmic Order $O(\log n)$ In contrast to exponential order, logarithmic order reflects the situation of "reducing to half each round". Let the input data size be $n$. Since it is reduced to half each round, the number of loops is $\log_2 n$, which is the inverse function of $2^n$. @@ -1166,7 +1166,7 @@ Logarithmic order commonly appears in algorithms based on the divide-and-conquer That is to say, the base $m$ can be converted without affecting the complexity. Therefore, we usually omit the base $m$ and denote logarithmic order simply as $O(\log n)$. -### Linearithmic order $O(n \log n)$ +### Linearithmic Order $O(n \log n)$ Linearithmic order commonly appears in nested loops, where the time complexities of the two layers of loops are $O(\log n)$ and $O(n)$ respectively. The relevant code is as follows: @@ -1180,7 +1180,7 @@ The figure below shows how linearithmic order is generated. Each level of the bi Mainstream sorting algorithms typically have a time complexity of $O(n \log n)$, such as quicksort, merge sort, and heap sort. -### Factorial order $O(n!)$ +### Factorial Order $O(n!)$ Factorial order corresponds to the mathematical "permutation" problem. Given $n$ distinct elements, find all possible permutation schemes; the number of schemes is: @@ -1198,7 +1198,7 @@ Factorials are typically implemented using recursion. As shown in the figure bel Note that because when $n \geq 4$ we always have $n! > 2^n$, factorial order grows faster than exponential order, and is also unacceptable for large $n$. -## Worst, best, and average time complexities +## Worst, Best, and Average Time Complexities **The time efficiency of an algorithm is often not fixed, but is related to the distribution of the input data**. Suppose we input an array `nums` of length $n$, where `nums` consists of numbers from $1$ to $n$, with each number appearing only once, but the element order is randomly shuffled. The task is to return the index of element $1$. We can draw the following conclusions. diff --git a/en/docs/chapter_data_structure/character_encoding.md b/en/docs/chapter_data_structure/character_encoding.md index 36cd6cec3..43cbd694f 100644 --- a/en/docs/chapter_data_structure/character_encoding.md +++ b/en/docs/chapter_data_structure/character_encoding.md @@ -2,7 +2,7 @@ In computers, all data is stored in binary form, and character `char` is no exception. To represent characters, we need to establish a "character set" that defines a one-to-one correspondence between each character and binary numbers. With a character set, computers can convert binary numbers to characters by looking up the table. -## ASCII Character Set +## Ascii Character Set ASCII code is the earliest character set, with the full name American Standard Code for Information Interchange. It uses 7 binary bits (the lower 7 bits of one byte) to represent a character, and can represent a maximum of 128 different characters. As shown in the figure below, ASCII code includes uppercase and lowercase English letters, numbers 0 ~ 9, some punctuation marks, and some control characters (such as newline and tab). @@ -12,7 +12,7 @@ However, **ASCII code can only represent English**. With the globalization of co Worldwide, a batch of EASCII character sets suitable for different regions have appeared successively. The first 128 characters of these character sets are unified as ASCII code, and the last 128 characters are defined differently to adapt to the needs of different languages. -## GBK Character Set +## Gbk Character Set Later, people found that **EASCII code still cannot meet the character quantity requirements of many languages**. For example, there are nearly one hundred thousand Chinese characters, and several thousand are used daily. In 1980, the China National Standardization Administration released the GB2312 character set, which included 6,763 Chinese characters, basically meeting the needs for computer processing of Chinese characters. @@ -36,7 +36,7 @@ For the above problem, **a straightforward solution is to store all characters a However, ASCII code has already proven to us that encoding English only requires 1 byte. If the above scheme is adopted, the size of English text will be twice that under ASCII encoding, which is very wasteful of memory space. Therefore, we need a more efficient Unicode encoding method. -## UTF-8 Encoding +## Utf-8 Encoding Currently, UTF-8 has become the most widely used Unicode encoding method internationally. **It is a variable-length encoding** that uses 1 to 4 bytes to represent a character, depending on the complexity of the character. ASCII characters only require 1 byte, Latin and Greek letters require 2 bytes, commonly used Chinese characters require 3 bytes, and some other rare characters require 4 bytes. diff --git a/en/docs/chapter_data_structure/index.md b/en/docs/chapter_data_structure/index.md index c05957eb3..82ef70b96 100644 --- a/en/docs/chapter_data_structure/index.md +++ b/en/docs/chapter_data_structure/index.md @@ -1,6 +1,6 @@ -# Data Structure +# Data Structures -![Data structure](../assets/covers/chapter_data_structure.jpg) +![Data structures](../assets/covers/chapter_data_structure.jpg) !!! abstract diff --git a/en/docs/chapter_data_structure/summary.md b/en/docs/chapter_data_structure/summary.md index 28bcbd815..a32225860 100644 --- a/en/docs/chapter_data_structure/summary.md +++ b/en/docs/chapter_data_structure/summary.md @@ -1,6 +1,6 @@ # Summary -### Key review +### Key Review - Data structures can be classified from two perspectives: logical structure and physical structure. Logical structure describes the logical relationships between data elements, while physical structure describes how data is stored in computer memory. - Common logical structures include linear, tree, and network structures. We typically classify data structures as linear (arrays, linked lists, stacks, queues) and non-linear (trees, graphs, heaps) based on their logical structure. The implementation of hash tables may involve both linear and non-linear data structures. diff --git a/en/docs/chapter_divide_and_conquer/binary_search_recur.md b/en/docs/chapter_divide_and_conquer/binary_search_recur.md index e54ac1b8f..8076fb6d0 100644 --- a/en/docs/chapter_divide_and_conquer/binary_search_recur.md +++ b/en/docs/chapter_divide_and_conquer/binary_search_recur.md @@ -1,4 +1,4 @@ -# Divide and conquer search strategy +# Divide and Conquer Search Strategy We have already learned that search algorithms are divided into two major categories. @@ -18,7 +18,7 @@ The divide and conquer strategy of binary search is as follows. Divide and conquer can improve search efficiency because brute-force search can only eliminate one option per round, **while divide and conquer search can eliminate half of the options per round**. -### Implementing binary search based on divide and conquer +### Implementing Binary Search Based on Divide and Conquer In previous sections, binary search was implemented based on iteration. Now we implement it based on divide and conquer (recursion). diff --git a/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md b/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md index bec390f9f..5b01d72ee 100644 --- a/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -1,4 +1,4 @@ -# Building a binary tree problem +# Building a Binary Tree Problem !!! question @@ -6,7 +6,7 @@ ![Example data for building a binary tree](build_binary_tree_problem.assets/build_tree_example.png) -### Determining if it is a divide and conquer problem +### Determining If It Is a Divide and Conquer Problem The original problem is defined as constructing a binary tree from `preorder` and `inorder`, which is a typical divide and conquer problem. @@ -14,7 +14,7 @@ The original problem is defined as constructing a binary tree from `preorder` an - **Subproblems are independent**: The left and right subtrees are independent of each other; there is no overlap between them. When constructing the left subtree, we only need to focus on the parts of the inorder and preorder traversals corresponding to the left subtree. The same applies to the right subtree. - **Solutions of subproblems can be merged**: Once we have the left and right subtrees (solutions of subproblems), we can link them to the root node to obtain the solution to the original problem. -### How to divide subtrees +### How to Divide Subtrees Based on the above analysis, this problem can be solved using divide and conquer, **but how do we divide the left and right subtrees through the preorder traversal `preorder` and inorder traversal `inorder`**? @@ -31,7 +31,7 @@ Using the data from the figure above as an example, we can obtain the division r ![Dividing subtrees in preorder and inorder traversals](build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png) -### Describing subtree intervals based on variables +### Describing Subtree Intervals Based on Variables Based on the above division method, **we have obtained the index intervals of the root node, left subtree, and right subtree in `preorder` and `inorder`**. To describe these index intervals, we need to use several pointer variables. @@ -53,7 +53,7 @@ Please note that $(m-l)$ in the right subtree root node index means "the number ![Index interval representation of root node and left and right subtrees](build_binary_tree_problem.assets/build_tree_division_pointers.png) -### Code implementation +### Code Implementation To improve the efficiency of querying $m$, we use a hash table `hmap` to store the mapping from elements in the `inorder` array to their indices: diff --git a/en/docs/chapter_divide_and_conquer/divide_and_conquer.md b/en/docs/chapter_divide_and_conquer/divide_and_conquer.md index 70872021a..a00456170 100644 --- a/en/docs/chapter_divide_and_conquer/divide_and_conquer.md +++ b/en/docs/chapter_divide_and_conquer/divide_and_conquer.md @@ -1,4 +1,4 @@ -# Divide and conquer algorithms +# Divide and Conquer Algorithms Divide and conquer is a very important and common algorithm strategy. Divide and conquer is typically implemented based on recursion, consisting of two steps: "divide" and "conquer". @@ -12,7 +12,7 @@ As shown in the figure below, "merge sort" is one of the typical applications of ![Divide and conquer strategy of merge sort](divide_and_conquer.assets/divide_and_conquer_merge_sort.png) -## How to determine divide and conquer problems +## How to Determine Divide and Conquer Problems Whether a problem is suitable for solving with divide and conquer can usually be determined based on the following criteria. @@ -26,13 +26,13 @@ Clearly, merge sort satisfies these three criteria. 2. **Subproblems are independent**: Each subarray can be sorted independently (subproblems can be solved independently). 3. **Solutions of subproblems can be merged**: Two sorted subarrays (solutions of subproblems) can be merged into one sorted array (solution of the original problem). -## Improving efficiency through divide and conquer +## Improving Efficiency Through Divide and Conquer **Divide and conquer can not only effectively solve algorithmic problems but often also improve algorithm efficiency**. In sorting algorithms, quick sort, merge sort, and heap sort are faster than selection, bubble, and insertion sort because they apply the divide and conquer strategy. This raises the question: **Why can divide and conquer improve algorithm efficiency, and what is the underlying logic**? In other words, why is dividing a large problem into multiple subproblems, solving the subproblems, and merging their solutions more efficient than directly solving the original problem? This question can be discussed from two aspects: operation count and parallel computation. -### Operation count optimization +### Operation Count Optimization Taking "bubble sort" as an example, processing an array of length $n$ requires $O(n^2)$ time. Suppose we divide the array into two subarrays from the midpoint as shown in the figure below, the division requires $O(n)$ time, sorting each subarray requires $O((n / 2)^2)$ time, and merging the two subarrays requires $O(n)$ time, resulting in an overall time complexity of: @@ -58,7 +58,7 @@ Going further, **what if we continuously divide the subarrays from their midpoin Thinking further, **what if we set multiple division points** and evenly divide the original array into $k$ subarrays? This situation is very similar to "bucket sort", which is well-suited for sorting massive amounts of data, with a theoretical time complexity of $O(n + k)$. -### Parallel computation optimization +### Parallel Computation Optimization We know that the subproblems generated by divide and conquer are independent of each other, **so they can typically be solved in parallel**. This means divide and conquer can not only reduce the time complexity of algorithms, **but also benefits from parallel optimization by operating systems**. @@ -68,7 +68,7 @@ For example, in the "bucket sort" shown in the figure below, we evenly distribut ![Parallel computation in bucket sort](divide_and_conquer.assets/divide_and_conquer_parallel_computing.png) -## Common applications of divide and conquer +## Common Applications of Divide and Conquer On one hand, divide and conquer can be used to solve many classic algorithmic problems. diff --git a/en/docs/chapter_divide_and_conquer/hanota_problem.md b/en/docs/chapter_divide_and_conquer/hanota_problem.md index b6797c73d..57eb6affd 100644 --- a/en/docs/chapter_divide_and_conquer/hanota_problem.md +++ b/en/docs/chapter_divide_and_conquer/hanota_problem.md @@ -1,4 +1,4 @@ -# Hanota problem +# Hanota Problem In merge sort and building binary trees, we decompose the original problem into two subproblems, each half the size of the original problem. However, for the hanota problem, we adopt a different decomposition strategy. @@ -14,7 +14,7 @@ In merge sort and building binary trees, we decompose the original problem into **We denote the hanota problem of size $i$ as $f(i)$**. For example, $f(3)$ represents moving $3$ discs from `A` to `C`. -### Considering the base cases +### Considering the Base Cases As shown in the figure below, for problem $f(1)$, when there is only one disc, we can move it directly from `A` to `C`. @@ -44,7 +44,7 @@ As shown in the figure below, for problem $f(2)$, when there are two discs, **si The process of solving problem $f(2)$ can be summarized as: **moving two discs from `A` to `C` with the help of `B`**. Here, `C` is called the target pillar, and `B` is called the buffer pillar. -### Subproblem decomposition +### Subproblem Decomposition For problem $f(3)$, when there are three discs, the situation becomes slightly more complex. @@ -78,7 +78,7 @@ For these two subproblems $f(n-1)$, **we can recursively divide them in the same ![Divide and conquer strategy for solving the hanota problem](hanota_problem.assets/hanota_divide_and_conquer.png) -### Code implementation +### Code Implementation In the code, we declare a recursive function `dfs(i, src, buf, tar)`, whose purpose is to move the top $i$ discs from pillar `src` to target pillar `tar` with the help of buffer pillar `buf`: diff --git a/en/docs/chapter_divide_and_conquer/index.md b/en/docs/chapter_divide_and_conquer/index.md index 7fea57852..66cab2877 100644 --- a/en/docs/chapter_divide_and_conquer/index.md +++ b/en/docs/chapter_divide_and_conquer/index.md @@ -1,4 +1,4 @@ -# Divide and conquer +# Divide and Conquer ![Divide and conquer](../assets/covers/chapter_divide_and_conquer.jpg) diff --git a/en/docs/chapter_divide_and_conquer/summary.md b/en/docs/chapter_divide_and_conquer/summary.md index d9150b731..bd573986a 100644 --- a/en/docs/chapter_divide_and_conquer/summary.md +++ b/en/docs/chapter_divide_and_conquer/summary.md @@ -1,5 +1,7 @@ # Summary +### Key Review + - Divide and conquer is a common algorithm design strategy, consisting of two phases: divide (partition) and conquer (merge), typically implemented based on recursion. - The criteria for determining whether a problem is a divide and conquer problem include: whether the problem can be decomposed, whether subproblems are independent, and whether subproblems can be merged. - Merge sort is a typical application of the divide and conquer strategy. It recursively divides an array into two equal-length subarrays until only one element remains, then merges them layer by layer to complete the sorting. diff --git a/en/docs/chapter_dynamic_programming/dp_problem_features.md b/en/docs/chapter_dynamic_programming/dp_problem_features.md index 5dd013da9..83e5e2bed 100644 --- a/en/docs/chapter_dynamic_programming/dp_problem_features.md +++ b/en/docs/chapter_dynamic_programming/dp_problem_features.md @@ -1,4 +1,4 @@ -# Characteristics of dynamic programming problems +# Characteristics of Dynamic Programming Problems In the previous section, we learned how dynamic programming solves the original problem by decomposing it into subproblems. In fact, subproblem decomposition is a general algorithmic approach, with different emphases in divide and conquer, dynamic programming, and backtracking. @@ -8,7 +8,7 @@ In the previous section, we learned how dynamic programming solves the original In fact, dynamic programming is commonly used to solve optimization problems, which not only contain overlapping subproblems but also have two other major characteristics: optimal substructure and no aftereffects. -## Optimal substructure +## Optimal Substructure We make a slight modification to the stair climbing problem to make it more suitable for demonstrating the concept of optimal substructure. @@ -48,7 +48,7 @@ This problem can also be space-optimized, compressing from one dimension to zero [file]{min_cost_climbing_stairs_dp}-[class]{}-[func]{min_cost_climbing_stairs_dp_comp} ``` -## No aftereffects +## No Aftereffects No aftereffects is one of the important characteristics that enable dynamic programming to solve problems effectively. Its definition is: **given a certain state, its future development is only related to the current state and has nothing to do with all past states**. diff --git a/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md b/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md index 344c76211..bdfe8c69a 100644 --- a/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md @@ -1,11 +1,11 @@ -# Dynamic programming problem-solving approach +# Dynamic Programming Problem-Solving Approach The previous two sections introduced the main characteristics of dynamic programming problems. Next, let us explore two more practical issues together. 1. How to determine whether a problem is a dynamic programming problem? 2. What is the complete process for solving a dynamic programming problem, and where should we start? -## Problem determination +## Problem Determination Generally speaking, if a problem contains overlapping subproblems, optimal substructure, and satisfies no aftereffects, then it is usually suitable for solving with dynamic programming. However, it is difficult to directly extract these characteristics from the problem description. Therefore, we usually relax the conditions and **first observe whether the problem is suitable for solving with backtracking (exhaustive search)**. @@ -25,7 +25,7 @@ Correspondingly, there are also some "penalty points". If a problem satisfies the decision tree model and has relatively obvious "bonus points", we can assume it is a dynamic programming problem and verify it during the solving process. -## Problem-solving steps +## Problem-Solving Steps The problem-solving process for dynamic programming varies depending on the nature and difficulty of the problem, but generally follows these steps: describe decisions, define states, establish the $dp$ table, derive state transition equations, determine boundary conditions, etc. @@ -89,7 +89,7 @@ As shown in the figure below, since each cell is transferred from the cell to it Based on the above analysis, we can directly write the dynamic programming code. However, subproblem decomposition is a top-down approach, so implementing in the order "brute force search $\rightarrow$ memoization $\rightarrow$ dynamic programming" is more aligned with thinking habits. -### Method 1: Brute force search +### Method 1: Brute Force Search Starting from state $[i, j]$, continuously decompose into smaller states $[i-1, j]$ and $[i, j-1]$. The recursive function includes the following elements. @@ -124,7 +124,7 @@ As shown in the figure below, after introducing memoization, all subproblem solu ![Memoization recursion tree](dp_solution_pipeline.assets/min_path_sum_dfs_mem.png) -### Method 3: Dynamic programming +### Method 3: Dynamic Programming Implement the dynamic programming solution based on iteration, as shown in the code below: @@ -172,7 +172,7 @@ The array `dp` has size $n \times m$, **thus the space complexity is $O(nm)$**. === "<12>" ![min_path_sum_dp_step12](dp_solution_pipeline.assets/min_path_sum_dp_step12.png) -### Space optimization +### Space Optimization Since each cell is only related to the cell to its left and the cell above it, we can use a single-row array to implement the $dp$ table. diff --git a/en/docs/chapter_dynamic_programming/edit_distance_problem.md b/en/docs/chapter_dynamic_programming/edit_distance_problem.md index d360ebc89..57906e4b1 100644 --- a/en/docs/chapter_dynamic_programming/edit_distance_problem.md +++ b/en/docs/chapter_dynamic_programming/edit_distance_problem.md @@ -1,4 +1,4 @@ -# Edit distance problem +# Edit Distance Problem Edit distance, also known as Levenshtein distance, refers to the minimum number of edits required to transform one string into another, commonly used in information retrieval and natural language processing to measure the similarity between two sequences. @@ -20,7 +20,7 @@ From the perspective of the decision tree, the goal of this problem is to find t ![Representing edit distance problem based on decision tree model](edit_distance_problem.assets/edit_distance_decision_tree.png) -### Dynamic programming approach +### Dynamic Programming Approach **Step 1: Think about the decisions in each round, define the state, and thus obtain the $dp$ table** @@ -65,7 +65,7 @@ When both strings are empty, the number of edit steps is $0$, i.e., $dp[0, 0] = Observing the state transition equation, the solution $dp[i, j]$ depends on solutions to the left, above, and upper-left, so the entire $dp$ table can be traversed in order through two nested loops. -### Code implementation +### Code Implementation ```src [file]{edit_distance}-[class]{}-[func]{edit_distance_dp} @@ -118,7 +118,7 @@ As shown in the figure below, the state transition process for the edit distance === "<15>" ![edit_distance_dp_step15](edit_distance_problem.assets/edit_distance_dp_step15.png) -### Space optimization +### Space Optimization Since $dp[i, j]$ is transferred from the solutions above $dp[i-1, j]$, to the left $dp[i, j-1]$, and to the upper-left $dp[i-1, j-1]$, forward traversal will lose the upper-left solution $dp[i-1, j-1]$, and reverse traversal cannot build $dp[i, j-1]$ in advance, so neither traversal order is feasible. diff --git a/en/docs/chapter_dynamic_programming/index.md b/en/docs/chapter_dynamic_programming/index.md index 29219fab3..44adcd642 100644 --- a/en/docs/chapter_dynamic_programming/index.md +++ b/en/docs/chapter_dynamic_programming/index.md @@ -1,4 +1,4 @@ -# Dynamic programming +# Dynamic Programming ![Dynamic programming](../assets/covers/chapter_dynamic_programming.jpg) diff --git a/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md b/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md index a3bb8cbd6..4435348cf 100644 --- a/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md +++ b/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md @@ -1,4 +1,4 @@ -# Introduction to dynamic programming +# Introduction to Dynamic Programming Dynamic programming is an important algorithmic paradigm that decomposes a problem into a series of smaller subproblems and avoids redundant computation by storing the solutions to subproblems, thereby significantly improving time efficiency. @@ -18,7 +18,7 @@ The goal of this problem is to find the number of ways, **we can consider using [file]{climbing_stairs_backtrack}-[class]{}-[func]{climbing_stairs_backtrack} ``` -## Method 1: Brute force search +## Method 1: Brute Force Search Backtracking algorithms typically do not explicitly decompose problems, but rather treat solving the problem as a series of decision steps, searching for all possible solutions through trial and pruning. @@ -73,7 +73,7 @@ Observe the figure below, **after memoization, all overlapping subproblems only ![Recursion tree with memoization](intro_to_dynamic_programming.assets/climbing_stairs_dfs_memo_tree.png) -## Method 3: Dynamic programming +## Method 3: Dynamic Programming **Memoization is a "top-down" method**: we start from the original problem (root node), recursively decompose larger subproblems into smaller ones, until reaching the smallest known subproblems (leaf nodes). Afterward, by backtracking, we collect the solutions to the subproblems layer by layer to construct the solution to the original problem. @@ -97,7 +97,7 @@ Based on the above content, we can summarize the commonly used terminology in dy - The states corresponding to the smallest subproblems (the $1$st and $2$nd steps) are called initial states. - The recurrence formula $dp[i] = dp[i-1] + dp[i-2]$ is called the state transition equation. -## Space optimization +## Space Optimization Observant readers may have noticed that **since $dp[i]$ is only related to $dp[i-1]$ and $dp[i-2]$, we do not need to use an array `dp` to store the solutions to all subproblems**, but can simply use two variables to roll forward. The code is as follows: diff --git a/en/docs/chapter_dynamic_programming/knapsack_problem.md b/en/docs/chapter_dynamic_programming/knapsack_problem.md index cd8e8ed1c..77ff86a84 100644 --- a/en/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/en/docs/chapter_dynamic_programming/knapsack_problem.md @@ -1,4 +1,4 @@ -# 0-1 knapsack problem +# 0-1 Knapsack Problem The knapsack problem is an excellent introductory problem for dynamic programming and is one of the most common problem forms in dynamic programming. It has many variants, such as the 0-1 knapsack problem, the unbounded knapsack problem, and the multiple knapsack problem. @@ -47,7 +47,7 @@ The current state $[i, c]$ is transferred from the state above $[i-1, c]$ and th Based on the above analysis, we will next implement the brute force search, memoization, and dynamic programming solutions in order. -### Method 1: Brute force search +### Method 1: Brute Force Search The search code includes the following elements. @@ -80,7 +80,7 @@ The figure below shows the search branches pruned in memoization. ![Memoization recursion tree for 0-1 knapsack problem](knapsack_problem.assets/knapsack_dfs_mem.png) -### Method 3: Dynamic programming +### Method 3: Dynamic Programming Dynamic programming is essentially the process of filling the $dp$ table during state transitions. The code is as follows: @@ -132,7 +132,7 @@ As shown in the figure below, both time complexity and space complexity are dete === "<14>" ![knapsack_dp_step14](knapsack_problem.assets/knapsack_dp_step14.png) -### Space optimization +### Space Optimization Since each state is only related to the state in the row above it, we can use two arrays rolling forward to reduce the space complexity from $O(n^2)$ to $O(n)$. diff --git a/en/docs/chapter_dynamic_programming/summary.md b/en/docs/chapter_dynamic_programming/summary.md index 5503dde43..f0683d734 100644 --- a/en/docs/chapter_dynamic_programming/summary.md +++ b/en/docs/chapter_dynamic_programming/summary.md @@ -1,5 +1,7 @@ # Summary +### Key Review + - Dynamic programming decomposes problems and avoids redundant computation by storing the solutions to subproblems, thereby significantly improving computational efficiency. - Without considering time constraints, all dynamic programming problems can be solved using backtracking (brute force search), but the recursion tree contains a large number of overlapping subproblems, resulting in extremely low efficiency. By introducing a memo list, we can store the solutions to all computed subproblems, ensuring that overlapping subproblems are only computed once. - Memoization is a top-down recursive solution, while the corresponding dynamic programming is a bottom-up iterative solution, similar to "filling in a table". Since the current state only depends on certain local states, we can eliminate one dimension of the $dp$ table to reduce space complexity. diff --git a/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md b/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md index 56a54d0e6..2b8afa524 100644 --- a/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md +++ b/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md @@ -1,8 +1,8 @@ -# Unbounded knapsack problem +# Unbounded Knapsack Problem In this section, we first solve another common knapsack problem: the unbounded knapsack, and then explore a special case of it: the coin change problem. -## Unbounded knapsack problem +## Unbounded Knapsack Problem !!! question @@ -10,7 +10,7 @@ In this section, we first solve another common knapsack problem: the unbounded k ![Example data for unbounded knapsack problem](unbounded_knapsack_problem.assets/unbounded_knapsack_example.png) -### Dynamic programming approach +### Dynamic Programming Approach The unbounded knapsack problem is very similar to the 0-1 knapsack problem, **differing only in that there is no limit on the number of times an item can be selected**. @@ -28,7 +28,7 @@ $$ dp[i, c] = \max(dp[i-1, c], dp[i, c - wgt[i-1]] + val[i-1]) $$ -### Code implementation +### Code Implementation Comparing the code for the two problems, there is one change in state transition from $i-1$ to $i$, with everything else identical: @@ -36,7 +36,7 @@ Comparing the code for the two problems, there is one change in state transition [file]{unbounded_knapsack}-[class]{}-[func]{unbounded_knapsack_dp} ``` -### Space optimization +### Space Optimization Since the current state is transferred from states on the left and above, **after space optimization, each row in the $dp$ table should be traversed in forward order**. @@ -66,7 +66,7 @@ The code implementation is relatively simple, just delete the first dimension of [file]{unbounded_knapsack}-[class]{}-[func]{unbounded_knapsack_dp_comp} ``` -## Coin change problem +## Coin Change Problem The knapsack problem represents a large class of dynamic programming problems and has many variants, such as the coin change problem. @@ -76,7 +76,7 @@ The knapsack problem represents a large class of dynamic programming problems an ![Example data for coin change problem](unbounded_knapsack_problem.assets/coin_change_example.png) -### Dynamic programming approach +### Dynamic Programming Approach **The coin change problem can be viewed as a special case of the unbounded knapsack problem**, with the following connections and differences. @@ -107,7 +107,7 @@ When the target amount is $0$, the minimum number of coins needed to make it up When there are no coins, **it is impossible to make up any amount $> 0$**, which is an invalid solution. To enable the $\min()$ function in the state transition equation to identify and filter out invalid solutions, we consider using $+ \infty$ to represent them, i.e., set all $dp[0, a]$ in the first row to $+ \infty$. -### Code implementation +### Code Implementation Most programming languages do not provide a $+ \infty$ variable, and can only use the maximum value of integer type `int` as a substitute. However, this can lead to large number overflow: the $+ 1$ operation in the state transition equation may cause overflow. @@ -164,7 +164,7 @@ The figure below shows the dynamic programming process for coin change, which is === "<15>" ![coin_change_dp_step15](unbounded_knapsack_problem.assets/coin_change_dp_step15.png) -### Space optimization +### Space Optimization The space optimization for the coin change problem is handled in the same way as the unbounded knapsack problem: @@ -172,7 +172,7 @@ The space optimization for the coin change problem is handled in the same way as [file]{coin_change}-[class]{}-[func]{coin_change_dp_comp} ``` -## Coin change problem II +## Coin Change Problem Ii !!! question @@ -180,7 +180,7 @@ The space optimization for the coin change problem is handled in the same way as ![Example data for coin change problem II](unbounded_knapsack_problem.assets/coin_change_ii_example.png) -### Dynamic programming approach +### Dynamic Programming Approach Compared to the previous problem, this problem's goal is to find the number of combinations, so the subproblem becomes: **the number of combinations among the first $i$ types of coins that can make up amount $a$**. The $dp$ table remains a two-dimensional matrix of size $(n+1) \times (amt + 1)$. @@ -192,13 +192,13 @@ $$ When the target amount is $0$, no coins need to be selected to make up the target amount, so all $dp[i, 0]$ in the first column should be initialized to $1$. When there are no coins, it is impossible to make up any amount $>0$, so all $dp[0, a]$ in the first row equal $0$. -### Code implementation +### Code Implementation ```src [file]{coin_change_ii}-[class]{}-[func]{coin_change_ii_dp} ``` -### Space optimization +### Space Optimization The space optimization is handled in the same way, just delete the coin dimension: diff --git a/en/docs/chapter_graph/graph.md b/en/docs/chapter_graph/graph.md index 73422ff73..51cf1d3f1 100644 --- a/en/docs/chapter_graph/graph.md +++ b/en/docs/chapter_graph/graph.md @@ -14,7 +14,7 @@ If we view vertices as nodes and edges as references (pointers) connecting the n ![Relationships among linked lists, trees, and graphs](graph.assets/linkedlist_tree_graph.png) -## Common types and terminology of graphs +## Common Types and Terminology of Graphs Graphs can be divided into undirected graphs and directed graphs based on whether edges have direction, as shown in the figure below. @@ -40,11 +40,11 @@ Graph data structures include the following commonly used terms. - Path: The sequence of edges from vertex A to vertex B is called a "path" from A to B. In the figure above, the edge sequence 1-5-2-4 is a path from vertex 1 to vertex 4. - Degree: The number of edges a vertex has. For directed graphs, in-degree indicates how many edges point to the vertex, and out-degree indicates how many edges point out from the vertex. -## Representation of graphs +## Representation of Graphs Common representations of graphs include "adjacency matrices" and "adjacency lists". The following uses undirected graphs as examples. -### Adjacency matrix +### Adjacency Matrix Given a graph with $n$ vertices, an adjacency matrix uses an $n \times n$ matrix to represent the graph, where each row (column) represents a vertex, and matrix elements represent edges, using $1$ or $0$ to indicate whether an edge exists between two vertices. @@ -60,7 +60,7 @@ Adjacency matrices have the following properties. When using adjacency matrices to represent graphs, we can directly access matrix elements to obtain edges, resulting in highly efficient addition, deletion, lookup, and modification operations, all with a time complexity of $O(1)$. However, the space complexity of the matrix is $O(n^2)$, which consumes significant memory. -### Adjacency list +### Adjacency List An adjacency list uses $n$ linked lists to represent a graph, with linked list nodes representing vertices. The $i$-th linked list corresponds to vertex $i$ and stores all adjacent vertices of that vertex (vertices connected to that vertex). The figure below shows an example of a graph stored using an adjacency list. @@ -70,7 +70,7 @@ Adjacency lists only store edges that actually exist, and the total number of ed Observing the figure above, **the structure of adjacency lists is very similar to "chaining" in hash tables, so we can adopt similar methods to optimize efficiency**. For example, when linked lists are long, they can be converted to AVL trees or red-black trees, thereby optimizing time efficiency from $O(n)$ to $O(\log n)$; linked lists can also be converted to hash tables, thereby reducing time complexity to $O(1)$. -## Common applications of graphs +## Common Applications of Graphs As shown in the table below, many real-world systems can be modeled using graphs, and corresponding problems can be reduced to graph computation problems. diff --git a/en/docs/chapter_graph/graph_operations.md b/en/docs/chapter_graph/graph_operations.md index c73079550..e35eba12b 100644 --- a/en/docs/chapter_graph/graph_operations.md +++ b/en/docs/chapter_graph/graph_operations.md @@ -1,8 +1,8 @@ -# Basic operations on graphs +# Basic Operations on Graphs Basic operations on graphs can be divided into operations on "edges" and operations on "vertices". Under the two representation methods of "adjacency matrix" and "adjacency list", the implementation methods differ. -## Implementation based on adjacency matrix +## Implementation Based on Adjacency Matrix Given an undirected graph with $n$ vertices, the various operations are implemented as shown in the figure below. @@ -32,7 +32,7 @@ The following is the implementation code for graphs represented using an adjacen [file]{graph_adjacency_matrix}-[class]{graph_adj_mat}-[func]{} ``` -## Implementation based on adjacency list +## Implementation Based on Adjacency List Given an undirected graph with a total of $n$ vertices and $m$ edges, the various operations can be implemented as shown in the figure below. @@ -68,7 +68,7 @@ Additionally, we use the `Vertex` class to represent vertices in the adjacency l [file]{graph_adjacency_list}-[class]{graph_adj_list}-[func]{} ``` -## Efficiency comparison +## Efficiency Comparison Assuming the graph has $n$ vertices and $m$ edges, the table below compares the time efficiency and space efficiency of adjacency matrices and adjacency lists. Note that the adjacency list (linked list) corresponds to the implementation in this text, while the adjacency list (hash table) refers specifically to the implementation where all linked lists are replaced with hash tables. diff --git a/en/docs/chapter_graph/graph_traversal.md b/en/docs/chapter_graph/graph_traversal.md index a1e413f86..ebed38f6d 100644 --- a/en/docs/chapter_graph/graph_traversal.md +++ b/en/docs/chapter_graph/graph_traversal.md @@ -1,16 +1,16 @@ -# Graph traversal +# Graph Traversal Trees represent "one-to-many" relationships, while graphs have a higher degree of freedom and can represent any "many-to-many" relationships. Therefore, we can view trees as a special case of graphs. Clearly, **tree traversal operations are also a special case of graph traversal operations**. Both graphs and trees require the application of search algorithms to implement traversal operations. Graph traversal methods can also be divided into two types: breadth-first traversal and depth-first traversal. -## Breadth-first search +## Breadth-First Search **Breadth-first search is a near-to-far traversal method that, starting from a certain node, always prioritizes visiting the nearest vertices and expands outward layer by layer**. As shown in the figure below, starting from the top-left vertex, first traverse all adjacent vertices of that vertex, then traverse all adjacent vertices of the next vertex, and so on, until all vertices have been visited. ![Breadth-first search of a graph](graph_traversal.assets/graph_bfs.png) -### Algorithm implementation +### Algorithm Implementation BFS is typically implemented with the help of a queue, as shown in the code below. The queue has a "first in, first out" property, which aligns with the BFS idea of "near to far". @@ -67,19 +67,19 @@ The code is relatively abstract; it is recommended to refer to the figure below Not unique. Breadth-first search only requires traversing in a "near to far" order, **and the traversal order of vertices at the same distance can be arbitrarily shuffled**. Taking the figure above as an example, the visit order of vertices $1$ and $3$ can be swapped, as can the visit order of vertices $2$, $4$, and $6$. -### Complexity analysis +### Complexity Analysis **Time complexity**: All vertices will be enqueued and dequeued once, using $O(|V|)$ time; in the process of traversing adjacent vertices, since it is an undirected graph, all edges will be visited $2$ times, using $O(2|E|)$ time; overall using $O(|V| + |E|)$ time. **Space complexity**: The list `res`, hash set `visited`, and queue `que` can contain at most $|V|$ vertices, using $O(|V|)$ space. -## Depth-first search +## Depth-First Search **Depth-first search is a traversal method that prioritizes going as far as possible, then backtracks when no path remains**. As shown in the figure below, starting from the top-left vertex, visit an adjacent vertex of the current vertex, continuing until reaching a dead end, then return and continue going as far as possible before returning again, and so on, until all vertices have been traversed. ![Depth-first search of a graph](graph_traversal.assets/graph_dfs.png) -### Algorithm implementation +### Algorithm Implementation This "go as far as possible then return" algorithm paradigm is typically implemented using recursion. Similar to breadth-first search, in depth-first search we also need a hash set `visited` to record visited vertices and avoid revisiting. @@ -133,7 +133,7 @@ To deepen understanding, it is recommended to combine the figure below with the Taking tree traversal as an example, "root $\rightarrow$ left $\rightarrow$ right", "left $\rightarrow$ root $\rightarrow$ right", and "left $\rightarrow$ right $\rightarrow$ root" correspond to pre-order, in-order, and post-order traversals, respectively. They represent three different traversal priorities, yet all three belong to depth-first search. -### Complexity analysis +### Complexity Analysis **Time complexity**: All vertices will be visited $1$ time, using $O(|V|)$ time; all edges will be visited $2$ times, using $O(2|E|)$ time; overall using $O(|V| + |E|)$ time. diff --git a/en/docs/chapter_graph/summary.md b/en/docs/chapter_graph/summary.md index af1ab4a5a..e28307f89 100644 --- a/en/docs/chapter_graph/summary.md +++ b/en/docs/chapter_graph/summary.md @@ -1,6 +1,6 @@ # Summary -### Key review +### Key Review - Graphs consist of vertices and edges and can be represented as a set of vertices and a set of edges. - Compared to linear relationships (linked lists) and divide-and-conquer relationships (trees), network relationships (graphs) have a higher degree of freedom and are therefore more complex. diff --git a/en/docs/chapter_greedy/fractional_knapsack_problem.md b/en/docs/chapter_greedy/fractional_knapsack_problem.md index 9914076b3..1004b6dcb 100644 --- a/en/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/en/docs/chapter_greedy/fractional_knapsack_problem.md @@ -1,4 +1,4 @@ -# Fractional knapsack problem +# Fractional Knapsack Problem !!! question @@ -15,7 +15,7 @@ The difference is that this problem allows selecting only a portion of an item. ![Value of items per unit weight](fractional_knapsack_problem.assets/fractional_knapsack_unit_value.png) -### Greedy strategy determination +### Greedy Strategy Determination Maximizing the total value of items in the knapsack **is essentially maximizing the value per unit weight of items**. From this, we can derive the greedy strategy shown in the figure below. @@ -25,7 +25,7 @@ Maximizing the total value of items in the knapsack **is essentially maximizing ![Greedy strategy for the fractional knapsack problem](fractional_knapsack_problem.assets/fractional_knapsack_greedy_strategy.png) -### Code implementation +### Code Implementation We created an `Item` class to facilitate sorting items by unit value. We loop to make greedy selections, breaking when the knapsack is full and returning the solution: @@ -39,7 +39,7 @@ Apart from sorting, in the worst case the entire item list needs to be traversed Since an `Item` object list is initialized, **the space complexity is $O(n)$**. -### Correctness proof +### Correctness Proof Using proof by contradiction. Suppose item $x$ has the highest unit value, and some algorithm yields a maximum value of `res`, but this solution does not include item $x$. diff --git a/en/docs/chapter_greedy/greedy_algorithm.md b/en/docs/chapter_greedy/greedy_algorithm.md index 9568a6799..545a2fb31 100644 --- a/en/docs/chapter_greedy/greedy_algorithm.md +++ b/en/docs/chapter_greedy/greedy_algorithm.md @@ -1,4 +1,4 @@ -# Greedy algorithm +# Greedy Algorithm Greedy algorithm is a common algorithm for solving optimization problems. Its basic idea is to make the seemingly best choice at each decision stage of the problem, that is, to greedily make locally optimal decisions in hopes of obtaining a globally optimal solution. Greedy algorithms are simple and efficient, and are widely applied in many practical problems. @@ -25,7 +25,7 @@ The implementation code is as follows: You might exclaim: So clean! The greedy algorithm solves the coin change problem in about ten lines of code. -## Advantages and limitations of greedy algorithms +## Advantages and Limitations of Greedy Algorithms **Greedy algorithms are not only straightforward and simple to implement, but are also usually very efficient**. In the code above, if the smallest coin denomination is $\min(coins)$, the greedy choice loops at most $amt / \min(coins)$ times, giving a time complexity of $O(amt / \min(coins))$. This is an order of magnitude smaller than the time complexity of the dynamic programming solution $O(n \times amt)$. @@ -44,7 +44,7 @@ Generally, the applicability of greedy algorithms falls into the following two s 1. **Can guarantee finding the optimal solution**: In this situation, greedy algorithms are often the best choice, because they tend to be more efficient than backtracking and dynamic programming. 2. **Can find an approximate optimal solution**: Greedy algorithms are also applicable in this situation. For many complex problems, finding the global optimal solution is very difficult, and being able to find a suboptimal solution with high efficiency is also very good. -## Characteristics of greedy algorithms +## Characteristics of Greedy Algorithms So the question arises: what kind of problems are suitable for solving with greedy algorithms? Or in other words, under what conditions can greedy algorithms guarantee finding the optimal solution? @@ -65,7 +65,7 @@ For example, in the coin change problem, although we can easily provide countere Pearson, D. A polynomial-time algorithm for the change-making problem[J]. Operations Research Letters, 2005, 33(3): 231-234. -## Steps for solving problems with greedy algorithms +## Steps for Solving Problems with Greedy Algorithms The problem-solving process for greedy problems can generally be divided into the following three steps. @@ -82,7 +82,7 @@ To ensure correctness, we should rigorously mathematically prove the greedy stra However, correctness proofs may also not be easy. If we have no clue, we usually choose to debug the code based on test cases, step by step modifying and verifying the greedy strategy. -## Typical problems solved by greedy algorithms +## Typical Problems Solved by Greedy Algorithms Greedy algorithms are often applied to optimization problems that satisfy greedy choice property and optimal substructure. Below are some typical greedy algorithm problems. diff --git a/en/docs/chapter_greedy/max_capacity_problem.md b/en/docs/chapter_greedy/max_capacity_problem.md index fc24c51dd..9c1eff0f3 100644 --- a/en/docs/chapter_greedy/max_capacity_problem.md +++ b/en/docs/chapter_greedy/max_capacity_problem.md @@ -1,4 +1,4 @@ -# Max capacity problem +# Max Capacity Problem !!! question @@ -20,7 +20,7 @@ $$ Let the array length be $n$, then the number of combinations of two partitions (total number of states) is $C_n^2 = \frac{n(n - 1)}{2}$. Most directly, **we can exhaustively enumerate all states** to find the maximum capacity, with time complexity $O(n^2)$. -### Greedy strategy determination +### Greedy Strategy Determination This problem has a more efficient solution. As shown in the figure below, select a state $[i, j]$ where index $i < j$ and height $ht[i] < ht[j]$, meaning $i$ is the short partition and $j$ is the long partition. @@ -72,7 +72,7 @@ The figure below shows the execution process of the greedy strategy. === "<9>" ![max_capacity_greedy_step9](max_capacity_problem.assets/max_capacity_greedy_step9.png) -### Code implementation +### Code Implementation The code loops at most $n$ rounds, **therefore the time complexity is $O(n)$**. @@ -82,7 +82,7 @@ Variables $i$, $j$, and $res$ use a constant amount of extra space, **therefore [file]{max_capacity}-[class]{}-[func]{max_capacity} ``` -### Correctness proof +### Correctness Proof The reason greedy is faster than exhaustive enumeration is that each round of greedy selection "skips" some states. diff --git a/en/docs/chapter_greedy/max_product_cutting_problem.md b/en/docs/chapter_greedy/max_product_cutting_problem.md index 3c2239517..60f72dc14 100644 --- a/en/docs/chapter_greedy/max_product_cutting_problem.md +++ b/en/docs/chapter_greedy/max_product_cutting_problem.md @@ -1,4 +1,4 @@ -# Max product cutting problem +# Max Product Cutting Problem !!! question @@ -20,7 +20,7 @@ $$ We need to think about: how large should the splitting count $m$ be, and what should each $n_i$ be? -### Greedy strategy determination +### Greedy Strategy Determination Based on experience, the product of two integers is often greater than their sum. Suppose we split out a factor of $2$ from $n$, then their product is $2(n-2)$. We compare this product with $n$: @@ -53,7 +53,7 @@ In summary, the following greedy strategies can be derived. 3. When the remainder is $2$, do not continue splitting, keep it. 4. When the remainder is $1$, since $2 \times 2 > 1 \times 3$, the last $3$ should be replaced with $2$. -### Code implementation +### Code Implementation As shown in the figure below, we don't need to use loops to split the integer, but can use integer division to get the count of $3$s as $a$, and modulo operation to get the remainder as $b$, at which point we have: @@ -76,7 +76,7 @@ Please note that for the edge case of $n \leq 3$, a $1$ must be split out, with Variables $a$ and $b$ use a constant amount of extra space, **therefore the space complexity is $O(1)$**. -### Correctness proof +### Correctness Proof Using proof by contradiction, only analyzing the case where $n \geq 4$. diff --git a/en/docs/chapter_greedy/summary.md b/en/docs/chapter_greedy/summary.md index 1b72a00de..cbdca517a 100644 --- a/en/docs/chapter_greedy/summary.md +++ b/en/docs/chapter_greedy/summary.md @@ -1,5 +1,7 @@ # Summary +### Key Review + - Greedy algorithms are typically used to solve optimization problems. The principle is to make locally optimal decisions at each decision stage in hopes of obtaining a globally optimal solution. - Greedy algorithms iteratively make one greedy choice after another, transforming the problem into a smaller subproblem in each round, until the problem is solved. - Greedy algorithms are not only simple to implement, but also have high problem-solving efficiency. Compared to dynamic programming, greedy algorithms typically have lower time complexity. diff --git a/en/docs/chapter_hashing/hash_algorithm.md b/en/docs/chapter_hashing/hash_algorithm.md index 415989529..19bebea6a 100644 --- a/en/docs/chapter_hashing/hash_algorithm.md +++ b/en/docs/chapter_hashing/hash_algorithm.md @@ -1,4 +1,4 @@ -# Hash algorithm +# Hash Algorithm The previous two sections introduced the working principle of hash tables and the methods to handle hash collisions. However, both open addressing and separate chaining **can only ensure that the hash table functions normally when hash collisions occur, but cannot reduce the frequency of hash collisions**. @@ -16,7 +16,7 @@ Observing the above formula, when the hash table capacity `capacity` is fixed, * This means that, to reduce the probability of hash collisions, we should focus on the design of the hash algorithm `hash()`. -## Goals of hash algorithms +## Goals of Hash Algorithms To achieve a "fast and stable" hash table data structure, hash algorithms should have the following characteristics: @@ -37,7 +37,7 @@ For cryptographic applications, to prevent reverse engineering such as deducing Note that **"uniform distribution" and "collision resistance" are two independent concepts**. Satisfying uniform distribution does not necessarily mean collision resistance. For example, under random input `key`, the hash function `key % 100` can produce a uniformly distributed output. However, this hash algorithm is too simple, and all `key` with the same last two digits will have the same output, making it easy to deduce a usable `key` from the hash value, thereby cracking the password. -## Design of hash algorithms +## Design of Hash Algorithms The design of hash algorithms is a complex issue that requires consideration of many factors. However, for some less demanding scenarios, we can also design some simple hash algorithms. @@ -78,7 +78,7 @@ It is worth noting that if the `key` is guaranteed to be randomly and uniformly In summary, we usually choose a prime number as the modulus, and this prime number should be large enough to eliminate periodic patterns as much as possible, enhancing the robustness of the hash algorithm. -## Common hash algorithms +## Common Hash Algorithms It is not hard to see that the simple hash algorithms mentioned above are quite "fragile" and far from reaching the design goals of hash algorithms. For example, since addition and XOR obey the commutative law, additive hash and XOR hash cannot distinguish strings with the same content but in different order, which may exacerbate hash collisions and cause security issues. @@ -100,7 +100,7 @@ Over the past century, hash algorithms have been in a continuous process of upgr | Security Level | Low, has been successfully attacked | Low, has been successfully attacked | High | High | | Applications | Abandoned, still used for data integrity checks | Abandoned | Cryptocurrency transaction verification, digital signatures, etc. | Can be used to replace SHA-2 | -# Hash values in data structures +# Hash Values in Data Structures We know that the keys in a hash table can be of various data types such as integers, decimals, or strings. Programming languages usually provide built-in hash algorithms for these data types to calculate the bucket indices in the hash table. Taking Python as an example, we can use the `hash()` function to compute the hash values for various data types. diff --git a/en/docs/chapter_hashing/hash_collision.md b/en/docs/chapter_hashing/hash_collision.md index abc8a0106..bf61008d2 100644 --- a/en/docs/chapter_hashing/hash_collision.md +++ b/en/docs/chapter_hashing/hash_collision.md @@ -1,4 +1,4 @@ -# Hash collision +# Hash Collision The previous section mentioned that, **in most cases, the input space of a hash function is much larger than the output space**, so theoretically, hash collisions are inevitable. For example, if the input space is all integers and the output space is the array capacity size, then multiple integers will inevitably be mapped to the same bucket index. @@ -9,7 +9,7 @@ Hash collisions can lead to incorrect query results, severely impacting the usab The main methods for improving the structure of hash tables include "separate chaining" and "open addressing". -## Separate chaining +## Separate Chaining In the original hash table, each bucket can store only one key-value pair. Separate chaining converts a single element into a linked list, treating key-value pairs as linked list nodes and storing all colliding key-value pairs in the same linked list. The figure below shows an example of a separate chaining hash table. @@ -37,13 +37,13 @@ The code below provides a simple implementation of a separate chaining hash tabl It's worth noting that when the linked list is very long, the query efficiency $O(n)$ is poor. **In this case, the list can be converted to an "AVL tree" or "Red-Black tree"** to optimize the time complexity of the query operation to $O(\log n)$. -## Open addressing +## Open Addressing Open addressing does not introduce additional data structures but instead handles hash collisions through "multiple probes". The probing methods mainly include linear probing, quadratic probing, and double hashing. Let's use linear probing as an example to introduce the mechanism of open addressing hash tables. -### Linear probing +### Linear Probing Linear probing uses a fixed-step linear search for probing, and its operation method differs from ordinary hash tables. @@ -72,7 +72,7 @@ The code below implements an open addressing (linear probing) hash table with la [file]{hash_map_open_addressing}-[class]{hash_map_open_addressing}-[func]{} ``` -### Quadratic probing +### Quadratic Probing Quadratic probing is similar to linear probing and is one of the common strategies for open addressing. When a collision occurs, quadratic probing does not simply skip a fixed number of steps but skips a number of steps equal to the "square of the number of probes", i.e., $1, 4, 9, \dots$ steps. @@ -86,7 +86,7 @@ However, quadratic probing is not perfect: - Clustering still exists, i.e., some positions are more likely to be occupied than others. - Due to the growth of squares, quadratic probing may not probe the entire hash table, meaning that even if there are empty buckets in the hash table, quadratic probing may not be able to access them. -### Double hashing +### Double Hashing As the name suggests, the double hashing method uses multiple hash functions $f_1(x)$, $f_2(x)$, $f_3(x)$, $\dots$ for probing. @@ -99,7 +99,7 @@ Compared to linear probing, the double hashing method is less prone to clusterin Please note that open addressing (linear probing, quadratic probing, and double hashing) hash tables all have the problem of "cannot directly delete elements". -## Choice of programming languages +## Choice of Programming Languages Different programming languages adopt different hash table implementation strategies. Here are a few examples: diff --git a/en/docs/chapter_hashing/hash_map.md b/en/docs/chapter_hashing/hash_map.md index ab15e1e8c..441a00439 100755 --- a/en/docs/chapter_hashing/hash_map.md +++ b/en/docs/chapter_hashing/hash_map.md @@ -1,4 +1,4 @@ -# Hash table +# Hash Table A hash table, also known as a hash map, establishes a mapping between keys `key` and values `value`, enabling efficient element retrieval. Specifically, when we input a key `key` into a hash table, we can retrieve the corresponding value `value` in $O(1)$ time. @@ -22,7 +22,7 @@ In addition to hash tables, arrays and linked lists can also implement query fun As observed, **the time complexity for insertion, deletion, search, and modification operations in a hash table is $O(1)$**, which is very efficient. -## Common hash table operations +## Common Hash Table Operations Common operations on hash tables include: initialization, query operations, adding key-value pairs, and deleting key-value pairs. Example code is as follows: @@ -34,11 +34,11 @@ Common operations on hash tables include: initialization, query operations, addi # Add operation # Add key-value pair (key, value) to hash table - hmap[12836] = "小哈" - hmap[15937] = "小啰" - hmap[16750] = "小算" - hmap[13276] = "小法" - hmap[10583] = "小鸭" + hmap[12836] = "XiaoHa" + hmap[15937] = "XiaoLuo" + hmap[16750] = "XiaoSuan" + hmap[13276] = "XiaoFa" + hmap[10583] = "XiaoYa" # Query operation # Input key into hash table to get value @@ -57,11 +57,11 @@ Common operations on hash tables include: initialization, query operations, addi /* Add operation */ // Add key-value pair (key, value) to hash table - map[12836] = "小哈"; - map[15937] = "小啰"; - map[16750] = "小算"; - map[13276] = "小法"; - map[10583] = "小鸭"; + map[12836] = "XiaoHa"; + map[15937] = "XiaoLuo"; + map[16750] = "XiaoSuan"; + map[13276] = "XiaoFa"; + map[10583] = "XiaoYa"; /* Query operation */ // Input key into hash table to get value @@ -80,11 +80,11 @@ Common operations on hash tables include: initialization, query operations, addi /* Add operation */ // Add key-value pair (key, value) to hash table - map.put(12836, "小哈"); - map.put(15937, "小啰"); - map.put(16750, "小算"); - map.put(13276, "小法"); - map.put(10583, "小鸭"); + map.put(12836, "XiaoHa"); + map.put(15937, "XiaoLuo"); + map.put(16750, "XiaoSuan"); + map.put(13276, "XiaoFa"); + map.put(10583, "XiaoYa"); /* Query operation */ // Input key into hash table to get value @@ -102,11 +102,11 @@ Common operations on hash tables include: initialization, query operations, addi Dictionary map = new() { /* Add operation */ // Add key-value pair (key, value) to hash table - { 12836, "小哈" }, - { 15937, "小啰" }, - { 16750, "小算" }, - { 13276, "小法" }, - { 10583, "小鸭" } + { 12836, "XiaoHa" }, + { 15937, "XiaoLuo" }, + { 16750, "XiaoSuan" }, + { 13276, "XiaoFa" }, + { 10583, "XiaoYa" } }; /* Query operation */ @@ -126,11 +126,11 @@ Common operations on hash tables include: initialization, query operations, addi /* Add operation */ // Add key-value pair (key, value) to hash table - hmap[12836] = "小哈" - hmap[15937] = "小啰" - hmap[16750] = "小算" - hmap[13276] = "小法" - hmap[10583] = "小鸭" + hmap[12836] = "XiaoHa" + hmap[15937] = "XiaoLuo" + hmap[16750] = "XiaoSuan" + hmap[13276] = "XiaoFa" + hmap[10583] = "XiaoYa" /* Query operation */ // Input key into hash table to get value @@ -149,11 +149,11 @@ Common operations on hash tables include: initialization, query operations, addi /* Add operation */ // Add key-value pair (key, value) to hash table - map[12836] = "小哈" - map[15937] = "小啰" - map[16750] = "小算" - map[13276] = "小法" - map[10583] = "小鸭" + map[12836] = "XiaoHa" + map[15937] = "XiaoLuo" + map[16750] = "XiaoSuan" + map[13276] = "XiaoFa" + map[10583] = "XiaoYa" /* Query operation */ // Input key into hash table to get value @@ -171,11 +171,11 @@ Common operations on hash tables include: initialization, query operations, addi const map = new Map(); /* Add operation */ // Add key-value pair (key, value) to hash table - map.set(12836, '小哈'); - map.set(15937, '小啰'); - map.set(16750, '小算'); - map.set(13276, '小法'); - map.set(10583, '小鸭'); + map.set(12836, 'XiaoHa'); + map.set(15937, 'XiaoLuo'); + map.set(16750, 'XiaoSuan'); + map.set(13276, 'XiaoFa'); + map.set(10583, 'XiaoYa'); /* Query operation */ // Input key into hash table to get value @@ -193,11 +193,11 @@ Common operations on hash tables include: initialization, query operations, addi const map = new Map(); /* Add operation */ // Add key-value pair (key, value) to hash table - map.set(12836, '小哈'); - map.set(15937, '小啰'); - map.set(16750, '小算'); - map.set(13276, '小法'); - map.set(10583, '小鸭'); + map.set(12836, 'XiaoHa'); + map.set(15937, 'XiaoLuo'); + map.set(16750, 'XiaoSuan'); + map.set(13276, 'XiaoFa'); + map.set(10583, 'XiaoYa'); console.info('\nAfter adding, hash table is\nKey -> Value'); console.info(map); @@ -221,11 +221,11 @@ Common operations on hash tables include: initialization, query operations, addi /* Add operation */ // Add key-value pair (key, value) to hash table - map[12836] = "小哈"; - map[15937] = "小啰"; - map[16750] = "小算"; - map[13276] = "小法"; - map[10583] = "小鸭"; + map[12836] = "XiaoHa"; + map[15937] = "XiaoLuo"; + map[16750] = "XiaoSuan"; + map[13276] = "XiaoFa"; + map[10583] = "XiaoYa"; /* Query operation */ // Input key into hash table to get value @@ -246,11 +246,11 @@ Common operations on hash tables include: initialization, query operations, addi /* Add operation */ // Add key-value pair (key, value) to hash table - map.insert(12836, "小哈".to_string()); - map.insert(15937, "小啰".to_string()); - map.insert(16750, "小算".to_string()); - map.insert(13279, "小法".to_string()); - map.insert(10583, "小鸭".to_string()); + map.insert(12836, "XiaoHa".to_string()); + map.insert(15937, "XiaoLuo".to_string()); + map.insert(16750, "XiaoSuan".to_string()); + map.insert(13279, "XiaoFa".to_string()); + map.insert(10583, "XiaoYa".to_string()); /* Query operation */ // Input key into hash table to get value @@ -275,11 +275,11 @@ Common operations on hash tables include: initialization, query operations, addi /* Add operation */ // Add key-value pair (key, value) to hash table - map[12836] = "小哈" - map[15937] = "小啰" - map[16750] = "小算" - map[13276] = "小法" - map[10583] = "小鸭" + map[12836] = "XiaoHa" + map[15937] = "XiaoLuo" + map[16750] = "XiaoSuan" + map[13276] = "XiaoFa" + map[10583] = "XiaoYa" /* Query operation */ // Input key into hash table to get value @@ -298,11 +298,11 @@ Common operations on hash tables include: initialization, query operations, addi # Add operation # Add key-value pair (key, value) to hash table - hmap[12836] = "小哈" - hmap[15937] = "小啰" - hmap[16750] = "小算" - hmap[13276] = "小法" - hmap[10583] = "小鸭" + hmap[12836] = "XiaoHa" + hmap[15937] = "XiaoLuo" + hmap[16750] = "XiaoSuan" + hmap[13276] = "XiaoFa" + hmap[10583] = "XiaoYa" # Query operation # Input key into hash table to get value @@ -550,7 +550,7 @@ There are three common ways to traverse a hash table: traversing key-value pairs https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%93%88%E5%B8%8C%E8%A1%A8%0A%20%20%20%20hmap%20%3D%20%7B%7D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%B7%BB%E5%8A%A0%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%9C%A8%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E6%B7%BB%E5%8A%A0%E9%94%AE%E5%80%BC%E5%AF%B9%20%28key,%20value%29%0A%20%20%20%20hmap%5B12836%5D%20%3D%20%22%E5%B0%8F%E5%93%88%22%0A%20%20%20%20hmap%5B15937%5D%20%3D%20%22%E5%B0%8F%E5%95%B0%22%0A%20%20%20%20hmap%5B16750%5D%20%3D%20%22%E5%B0%8F%E7%AE%97%22%0A%20%20%20%20hmap%5B13276%5D%20%3D%20%22%E5%B0%8F%E6%B3%95%22%0A%20%20%20%20hmap%5B10583%5D%20%3D%20%22%E5%B0%8F%E9%B8%AD%22%0A%20%20%20%20%0A%20%20%20%20%23%20%E9%81%8D%E5%8E%86%E5%93%88%E5%B8%8C%E8%A1%A8%0A%20%20%20%20%23%20%E9%81%8D%E5%8E%86%E9%94%AE%E5%80%BC%E5%AF%B9%20key-%3Evalue%0A%20%20%20%20for%20key,%20value%20in%20hmap.items%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key,%20%22-%3E%22,%20value%29%0A%20%20%20%20%23%20%E5%8D%95%E7%8B%AC%E9%81%8D%E5%8E%86%E9%94%AE%20key%0A%20%20%20%20for%20key%20in%20hmap.keys%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key%29%0A%20%20%20%20%23%20%E5%8D%95%E7%8B%AC%E9%81%8D%E5%8E%86%E5%80%BC%20value%0A%20%20%20%20for%20value%20in%20hmap.values%28%29%3A%0A%20%20%20%20%20%20%20%20print%28value%29&cumulative=false&curInstr=8&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -## Simple hash table implementation +## Simple Hash Table Implementation Let's first consider the simplest case: **implementing a hash table using only an array**. In a hash table, each empty position in the array is called a bucket, and each bucket can store a key-value pair. Therefore, the query operation is to find the bucket corresponding to `key` and retrieve the `value` from the bucket. @@ -577,7 +577,7 @@ The following code implements a simple hash table. Here, we encapsulate `key` an [file]{array_hash_map}-[class]{array_hash_map}-[func]{} ``` -## Hash collision and resizing +## Hash Collision and Resizing Fundamentally, the role of a hash function is to map the input space consisting of all `key`s to the output space consisting of all array indices, and the input space is often much larger than the output space. Therefore, **theoretically there must be cases where "multiple inputs correspond to the same output"**. diff --git a/en/docs/chapter_hashing/summary.md b/en/docs/chapter_hashing/summary.md index bc98f04bd..e4bc7a805 100644 --- a/en/docs/chapter_hashing/summary.md +++ b/en/docs/chapter_hashing/summary.md @@ -1,6 +1,6 @@ # Summary -### Key review +### Key Review - Given an input `key`, a hash table can retrieve the corresponding `value` in $O(1)$ time, which is highly efficient. - Common hash table operations include querying, adding key-value pairs, deleting key-value pairs, and traversing the hash table. diff --git a/en/docs/chapter_heap/build_heap.md b/en/docs/chapter_heap/build_heap.md index fc9a8409c..693aa849c 100644 --- a/en/docs/chapter_heap/build_heap.md +++ b/en/docs/chapter_heap/build_heap.md @@ -1,8 +1,8 @@ -# Heap construction operation +# Heap Construction Operation In some cases, we want to build a heap using all elements of a list, and this process is called "heap construction operation." -## Implementing with element insertion +## Implementing with Element Insertion We first create an empty heap, then iterate through the list, performing the "element insertion operation" on each element in sequence. This means adding the element to the bottom of the heap and then performing "bottom-to-top" heapify on that element. @@ -10,7 +10,7 @@ Each time an element is inserted into the heap, the heap's length increases by o Given $n$ elements, each element's insertion operation takes $O(\log{n})$ time, so the time complexity of this heap construction method is $O(n \log n)$. -## Implementing through heapify traversal +## Implementing Through Heapify Traversal In fact, we can implement a more efficient heap construction method in two steps. @@ -27,7 +27,7 @@ It's worth noting that **since leaf nodes have no children, they are naturally v [file]{my_heap}-[class]{max_heap}-[func]{__init__} ``` -## Complexity analysis +## Complexity Analysis Next, let's attempt to derive the time complexity of this second heap construction method. diff --git a/en/docs/chapter_heap/heap.md b/en/docs/chapter_heap/heap.md index 0e0f130a5..824ef136a 100644 --- a/en/docs/chapter_heap/heap.md +++ b/en/docs/chapter_heap/heap.md @@ -13,7 +13,7 @@ As a special case of a complete binary tree, heaps have the following characteri - We call the root node of the binary tree the "heap top" and the bottom-rightmost node the "heap bottom." - For max heaps (min heaps), the value of the heap top element (root node) is the largest (smallest). -## Common heap operations +## Common Heap Operations It should be noted that many programming languages provide a priority queue, which is an abstract data structure defined as a queue with priority sorting. @@ -418,11 +418,11 @@ Similar to "ascending order" and "descending order" in sorting algorithms, we ca https://pythontutor.com/render.html#code=import%20heapq%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B0%8F%E9%A1%B6%E5%A0%86%0A%20%20%20%20min_heap,%20flag%20%3D%20%5B%5D,%201%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%A4%A7%E9%A1%B6%E5%A0%86%0A%20%20%20%20max_heap,%20flag%20%3D%20%5B%5D,%20-1%0A%20%20%20%20%0A%20%20%20%20%23%20Python%20%E7%9A%84%20heapq%20%E6%A8%A1%E5%9D%97%E9%BB%98%E8%AE%A4%E5%AE%9E%E7%8E%B0%E5%B0%8F%E9%A1%B6%E5%A0%86%0A%20%20%20%20%23%20%E8%80%83%E8%99%91%E5%B0%86%E2%80%9C%E5%85%83%E7%B4%A0%E5%8F%96%E8%B4%9F%E2%80%9D%E5%90%8E%E5%86%8D%E5%85%A5%E5%A0%86%EF%BC%8C%E8%BF%99%E6%A0%B7%E5%B0%B1%E5%8F%AF%E4%BB%A5%E5%B0%86%E5%A4%A7%E5%B0%8F%E5%85%B3%E7%B3%BB%E9%A2%A0%E5%80%92%EF%BC%8C%E4%BB%8E%E8%80%8C%E5%AE%9E%E7%8E%B0%E5%A4%A7%E9%A1%B6%E5%A0%86%0A%20%20%20%20%23%20%E5%9C%A8%E6%9C%AC%E7%A4%BA%E4%BE%8B%E4%B8%AD%EF%BC%8Cflag%20%3D%201%20%E6%97%B6%E5%AF%B9%E5%BA%94%E5%B0%8F%E9%A1%B6%E5%A0%86%EF%BC%8Cflag%20%3D%20-1%20%E6%97%B6%E5%AF%B9%E5%BA%94%E5%A4%A7%E9%A1%B6%E5%A0%86%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%85%A5%E5%A0%86%0A%20%20%20%20heapq.heappush%28max_heap,%20flag%20*%201%29%0A%20%20%20%20heapq.heappush%28max_heap,%20flag%20*%203%29%0A%20%20%20%20heapq.heappush%28max_heap,%20flag%20*%202%29%0A%20%20%20%20heapq.heappush%28max_heap,%20flag%20*%205%29%0A%20%20%20%20heapq.heappush%28max_heap,%20flag%20*%204%29%0A%20%20%20%20%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E5%A0%86%E9%A1%B6%E5%85%83%E7%B4%A0%0A%20%20%20%20peek%20%3D%20flag%20*%20max_heap%5B0%5D%20%23%205%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%A0%86%E9%A1%B6%E5%85%83%E7%B4%A0%E5%87%BA%E5%A0%86%0A%20%20%20%20%23%20%E5%87%BA%E5%A0%86%E5%85%83%E7%B4%A0%E4%BC%9A%E5%BD%A2%E6%88%90%E4%B8%80%E4%B8%AA%E4%BB%8E%E5%A4%A7%E5%88%B0%E5%B0%8F%E7%9A%84%E5%BA%8F%E5%88%97%0A%20%20%20%20val%20%3D%20flag%20*%20heapq.heappop%28max_heap%29%20%23%205%0A%20%20%20%20val%20%3D%20flag%20*%20heapq.heappop%28max_heap%29%20%23%204%0A%20%20%20%20val%20%3D%20flag%20*%20heapq.heappop%28max_heap%29%20%23%203%0A%20%20%20%20val%20%3D%20flag%20*%20heapq.heappop%28max_heap%29%20%23%202%0A%20%20%20%20val%20%3D%20flag%20*%20heapq.heappop%28max_heap%29%20%23%201%0A%20%20%20%20%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E5%A0%86%E5%A4%A7%E5%B0%8F%0A%20%20%20%20size%20%3D%20len%28max_heap%29%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E5%A0%86%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%0A%20%20%20%20is_empty%20%3D%20not%20max_heap%0A%20%20%20%20%0A%20%20%20%20%23%20%E8%BE%93%E5%85%A5%E5%88%97%E8%A1%A8%E5%B9%B6%E5%BB%BA%E5%A0%86%0A%20%20%20%20min_heap%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20heapq.heapify%28min_heap%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -## Implementation of the heap +## Implementation of the Heap The following implementation is of a max heap. To convert it to a min heap, simply invert all size logic comparisons (for example, replace $\geq$ with $\leq$). Interested readers are encouraged to implement this on their own. -### Heap storage and representation +### Heap Storage and Representation As mentioned in the "Binary Tree" chapter, complete binary trees are well-suited for array representation. Since heaps are a type of complete binary tree, **we will use arrays to store heaps**. @@ -438,7 +438,7 @@ We can encapsulate the index mapping formula into functions for convenient subse [file]{my_heap}-[class]{max_heap}-[func]{parent} ``` -### Accessing the heap top element +### Accessing the Heap Top Element The heap top element is the root node of the binary tree, which is also the first element of the list: @@ -446,7 +446,7 @@ The heap top element is the root node of the binary tree, which is also the firs [file]{my_heap}-[class]{max_heap}-[func]{peek} ``` -### Inserting an element into the heap +### Inserting an Element Into the Heap Given an element `val`, we first add it to the bottom of the heap. After addition, since `val` may be larger than other elements in the heap, the heap's property may be violated. **Therefore, it's necessary to repair the path from the inserted node to the root node**. This operation is called heapify. @@ -485,7 +485,7 @@ Given a total of $n$ nodes, the tree height is $O(\log n)$. Thus, the number of [file]{my_heap}-[class]{max_heap}-[func]{sift_up} ``` -### Removing the heap top element +### Removing the Heap Top Element The heap top element is the root node of the binary tree, which is the first element of the list. If we directly remove the first element from the list, all node indexes in the binary tree would change, making subsequent repair with heapify difficult. To minimize changes in element indexes, we use the following steps. @@ -531,7 +531,7 @@ Similar to the element insertion operation, the time complexity of the heap top [file]{my_heap}-[class]{max_heap}-[func]{sift_down} ``` -## Common applications of heaps +## Common Applications of Heaps - **Priority queue**: Heaps are typically the preferred data structure for implementing priority queues, with both enqueue and dequeue operations having a time complexity of $O(\log n)$, and the heap construction operation having $O(n)$, all of which are highly efficient. - **Heap sort**: Given a set of data, we can build a heap with them and then continuously perform element removal operations to obtain sorted data. However, we usually use a more elegant approach to implement heap sort, as detailed in the "Heap Sort" chapter. diff --git a/en/docs/chapter_heap/summary.md b/en/docs/chapter_heap/summary.md index 6c65ea733..bea6dbf1d 100644 --- a/en/docs/chapter_heap/summary.md +++ b/en/docs/chapter_heap/summary.md @@ -1,6 +1,6 @@ # Summary -### Key review +### Key Review - A heap is a complete binary tree that can be categorized as a max heap or min heap based on its property. The heap top element of a max heap (min heap) is the largest (smallest). - A priority queue is defined as a queue with priority sorting, typically implemented using heaps. diff --git a/en/docs/chapter_heap/top_k.md b/en/docs/chapter_heap/top_k.md index 746241fd5..c4a800ff5 100644 --- a/en/docs/chapter_heap/top_k.md +++ b/en/docs/chapter_heap/top_k.md @@ -1,4 +1,4 @@ -# Top-k problem +# Top-K Problem !!! question @@ -6,7 +6,7 @@ For this problem, we'll first introduce two solutions with relatively straightforward approaches, then introduce a more efficient heap-based solution. -## Method 1: Iterative selection +## Method 1: Iterative Selection We can perform $k$ rounds of traversal as shown in the figure below, extracting the $1^{st}$, $2^{nd}$, $\dots$, $k^{th}$ largest elements in each round, with a time complexity of $O(nk)$. diff --git a/en/docs/chapter_introduction/algorithms_are_everywhere.md b/en/docs/chapter_introduction/algorithms_are_everywhere.md index ca85b7971..b6f7008e1 100644 --- a/en/docs/chapter_introduction/algorithms_are_everywhere.md +++ b/en/docs/chapter_introduction/algorithms_are_everywhere.md @@ -1,4 +1,4 @@ -# Algorithms are everywhere +# Algorithms Are Everywhere When we hear the term "algorithm," we naturally think of mathematics. However, many algorithms do not involve complex mathematics but rely more on basic logic, which can be seen everywhere in our daily lives. diff --git a/en/docs/chapter_introduction/index.md b/en/docs/chapter_introduction/index.md index f6a58672a..a663d0c39 100644 --- a/en/docs/chapter_introduction/index.md +++ b/en/docs/chapter_introduction/index.md @@ -1,6 +1,6 @@ -# Introduction to algorithms +# Encounter with Algorithms -![Introduction to algorithms](../assets/covers/chapter_introduction.jpg) +![Encounter with Algorithms](../assets/covers/chapter_introduction.jpg) !!! abstract diff --git a/en/docs/chapter_introduction/summary.md b/en/docs/chapter_introduction/summary.md index 65160bfea..75258b946 100644 --- a/en/docs/chapter_introduction/summary.md +++ b/en/docs/chapter_introduction/summary.md @@ -1,5 +1,7 @@ # Summary +### Key Review + - Algorithms are ubiquitous in daily life and are not distant, esoteric knowledge. In fact, we have already learned many algorithms unconsciously and use them to solve problems big and small in life. - The principle of looking up a dictionary is consistent with the binary search algorithm. Binary search embodies the important algorithmic idea of divide and conquer. - The process of organizing playing cards is very similar to the insertion sort algorithm. Insertion sort is suitable for sorting small datasets. diff --git a/en/docs/chapter_introduction/what_is_dsa.md b/en/docs/chapter_introduction/what_is_dsa.md index 7af5f3ad6..fdf24966f 100644 --- a/en/docs/chapter_introduction/what_is_dsa.md +++ b/en/docs/chapter_introduction/what_is_dsa.md @@ -1,6 +1,6 @@ -# What is an algorithm +# What Is an Algorithm -## Algorithm definition +## Algorithm Definition An algorithm is a set of instructions or operational steps that solves a specific problem within a finite amount of time. It has the following characteristics. @@ -8,7 +8,7 @@ An algorithm is a set of instructions or operational steps that solves a - It is feasible and can be completed within a finite number of steps, time, and memory space. - Each step has a definite meaning, and under the same input and operating conditions, the output is always the same. -## Data structure definition +## Data Structure Definition A data structure is a way of organizing and storing data, covering the data content, relationships between data, and methods for data operations. It has the following design objectives. @@ -21,7 +21,7 @@ A data structure is a way of organizing and storing data, covering the da - Compared to arrays, linked lists are more convenient for data addition and deletion operations but sacrifice data access speed. - Compared to linked lists, graphs provide richer logical information but require larger memory space. -## The relationship between data structures and algorithms +## The Relationship Between Data Structures and Algorithms As shown in the figure below, data structures and algorithms are highly related and tightly coupled, specifically manifested in the following three aspects. diff --git a/en/docs/chapter_preface/about_the_book.md b/en/docs/chapter_preface/about_the_book.md index 5e98f36fe..2f8931b0d 100644 --- a/en/docs/chapter_preface/about_the_book.md +++ b/en/docs/chapter_preface/about_the_book.md @@ -1,4 +1,4 @@ -# About this book +# About This Book This project aims to create an open-source, free, beginner-friendly introductory tutorial on data structures and algorithms. @@ -6,7 +6,7 @@ This project aims to create an open-source, free, beginner-friendly introductory - The source code can be run with one click, helping readers improve their programming skills through practice and understand how algorithms work and the underlying implementation of data structures. - We encourage readers to learn from each other, and everyone is welcome to ask questions and share insights in the comments section, making progress together through discussion and exchange. -## Target audience +## Target Audience If you are an algorithm beginner who has never been exposed to algorithms, or if you already have some problem-solving experience and have a vague understanding of data structures and algorithms, oscillating between knowing and not knowing, then this book is tailor-made for you! @@ -18,7 +18,7 @@ If you are an algorithm "expert," we look forward to receiving your valuable sug You need to have at least a programming foundation in any language, and be able to read and write simple code. -## Content structure +## Content Structure The main content of this book is shown in the figure below. @@ -47,6 +47,6 @@ During the creation of this book, I received help from many people. During the writing process, I read many textbooks and articles on data structures and algorithms. These works provided excellent examples for this book and ensured the accuracy and quality of the book's content. I would like to thank all the teachers and predecessors for their outstanding contributions! -This book advocates a learning method that combines hands and brain, and in this regard I was deeply inspired by [*Dive into Deep Learning*](https://github.com/d2l-ai/d2l-zh). I highly recommend this excellent work to all readers. +This book advocates a learning method that combines hands and brain, and in this regard I was deeply inspired by [Dive into Deep Learning](https://github.com/d2l-ai/d2l-zh). I highly recommend this excellent work to all readers. **Heartfelt thanks to my parents, it is your support and encouragement that has given me the opportunity to do this interesting thing**. diff --git a/en/docs/chapter_preface/suggestions.md b/en/docs/chapter_preface/suggestions.md index d15472c47..c087c019c 100644 --- a/en/docs/chapter_preface/suggestions.md +++ b/en/docs/chapter_preface/suggestions.md @@ -1,10 +1,10 @@ -# How to use this book +# How to Use This Book !!! tip For the best reading experience, it is recommended that you read through this section. -## Writing style conventions +## Writing Style Conventions - Titles marked with `*` are optional sections with relatively difficult content. If you have limited time, you can skip them first. - Technical terms will be in bold (in paper and PDF versions) or underlined (in web versions), such as array. It is recommended to memorize them for reading literature. @@ -191,7 +191,7 @@ // comment ``` -## Learning efficiently with animated illustrations +## Learning Efficiently with Animated Illustrations Compared to text, videos and images have higher information density and structural organization, making them easier to understand. In this book, **key and difficult knowledge will mainly be presented in the form of animated illustrations**, with text serving as explanation and supplement. @@ -199,7 +199,7 @@ If you find that a section of content provides animated illustrations as shown i ![Example of animated illustrations](../index.assets/animation.gif) -## Deepening understanding through code practice +## Deepening Understanding Through Code Practice The accompanying code for this book is hosted in the [GitHub repository](https://github.com/krahets/hello-algo). As shown in the figure below, **the source code comes with test cases and can be run with one click**. @@ -231,7 +231,7 @@ In addition to running code locally, **the web version also supports visual runn ![Visual running of Python code](suggestions.assets/pythontutor_example.png) -## Growing together through questions and discussions +## Growing Together Through Questions and Discussions When reading this book, please do not easily skip knowledge points that you have not learned well. **Feel free to ask your questions in the comments section**, and my friends and I will do our best to answer you, and generally reply within two days. @@ -239,7 +239,7 @@ As shown in the figure below, the web version has a comments section at the bott ![Example of comments section](../index.assets/comment.gif) -## Algorithm learning roadmap +## Algorithm Learning Roadmap From an overall perspective, we can divide the process of learning data structures and algorithms into three stages. diff --git a/en/docs/chapter_preface/summary.md b/en/docs/chapter_preface/summary.md index 1dc45a96d..766d47d7c 100644 --- a/en/docs/chapter_preface/summary.md +++ b/en/docs/chapter_preface/summary.md @@ -1,5 +1,7 @@ # Summary +### Key Review + - The main audience of this book is algorithm beginners. If you already have a certain foundation, this book can help you systematically review algorithm knowledge, and the source code in the book can also be used as a "problem-solving toolkit." - The content of the book mainly includes three parts: complexity analysis, data structures, and algorithms, covering most topics in this field. - For algorithm novices, reading an introductory book during the initial learning stage is crucial, as it can help you avoid many detours. diff --git a/en/docs/chapter_searching/binary_search.md b/en/docs/chapter_searching/binary_search.md index e9d6eb61e..2a5e5570c 100644 --- a/en/docs/chapter_searching/binary_search.md +++ b/en/docs/chapter_searching/binary_search.md @@ -1,4 +1,4 @@ -# Binary search +# Binary Search Binary search is an efficient searching algorithm based on the divide-and-conquer strategy. It leverages the orderliness of data to reduce the search range by half in each round until the target element is found or the search interval becomes empty. @@ -53,7 +53,7 @@ The code is shown below: **Space complexity is $O(1)$**: Pointers $i$ and $j$ use constant-size space. -## Interval representation methods +## Interval Representation Methods In addition to the closed interval mentioned above, another common interval representation is the "left-closed right-open" interval, defined as $[0, n)$, meaning the left boundary includes itself while the right boundary does not. Under this representation, the interval $[i, j)$ is empty when $i = j$. @@ -69,7 +69,7 @@ Since both the left and right boundaries in the "closed interval" representation ![Two interval definitions](binary_search.assets/binary_search_ranges.png) -## Advantages and limitations +## Advantages and Limitations Binary search performs well in both time and space aspects. diff --git a/en/docs/chapter_searching/binary_search_edge.md b/en/docs/chapter_searching/binary_search_edge.md index b2fe2a23c..acdf0a2d0 100644 --- a/en/docs/chapter_searching/binary_search_edge.md +++ b/en/docs/chapter_searching/binary_search_edge.md @@ -1,6 +1,6 @@ -# Binary search edge cases +# Binary Search Edge Cases -## Finding the left boundary +## Finding the Left Boundary !!! question @@ -19,13 +19,13 @@ When either of these situations occurs, simply return $-1$. The code is shown be [file]{binary_search_edge}-[class]{}-[func]{binary_search_left_edge} ``` -## Finding the right boundary +## Finding the Right Boundary So how do we find the rightmost `target`? The most direct approach is to modify the code and replace the pointer shrinking operation in the `nums[m] == target` case. The code is omitted here; interested readers can implement it themselves. Below we introduce two more clever methods. -### Reusing left boundary search +### Reusing Left Boundary Search In fact, we can use the function for finding the leftmost element to find the rightmost element. The specific method is: **Convert finding the rightmost `target` into finding the leftmost `target + 1`**. @@ -39,7 +39,7 @@ Note that the returned insertion point is $i$, so we need to subtract $1$ from i [file]{binary_search_edge}-[class]{}-[func]{binary_search_right_edge} ``` -### Converting to element search +### Converting to Element Search We know that when the array does not contain `target`, $i$ and $j$ will eventually point to the first elements greater than and less than `target`, respectively. diff --git a/en/docs/chapter_searching/binary_search_insertion.md b/en/docs/chapter_searching/binary_search_insertion.md index 710c3c328..38e0e649d 100644 --- a/en/docs/chapter_searching/binary_search_insertion.md +++ b/en/docs/chapter_searching/binary_search_insertion.md @@ -1,8 +1,8 @@ -# Binary search insertion point +# Binary Search Insertion Point Binary search can not only be used to search for target elements but also to solve many variant problems, such as searching for the insertion position of a target element. -## Case without duplicate elements +## Case Without Duplicate Elements !!! question @@ -26,7 +26,7 @@ Therefore, when the binary search ends, we must have: $i$ points to the first el [file]{binary_search_insertion}-[class]{}-[func]{binary_search_insertion_simple} ``` -## Case with duplicate elements +## Case with Duplicate Elements !!! question diff --git a/en/docs/chapter_searching/replace_linear_by_hashing.md b/en/docs/chapter_searching/replace_linear_by_hashing.md index 7b0855ebb..5a2f76964 100644 --- a/en/docs/chapter_searching/replace_linear_by_hashing.md +++ b/en/docs/chapter_searching/replace_linear_by_hashing.md @@ -1,4 +1,4 @@ -# Hash optimization strategy +# Hash Optimization Strategy In algorithm problems, **we often reduce the time complexity of algorithms by replacing linear search with hash-based search**. Let's use an algorithm problem to deepen our understanding. @@ -6,7 +6,7 @@ In algorithm problems, **we often reduce the time complexity of algorithms by re Given an integer array `nums` and a target element `target`, search for two elements in the array whose "sum" equals `target`, and return their array indices. Any solution will do. -## Linear search: trading time for space +## Linear Search: Trading Time for Space Consider directly traversing all possible combinations. As shown in the figure below, we open a two-layer loop and judge in each round whether the sum of two integers equals `target`. If so, return their indices. @@ -20,7 +20,7 @@ The code is shown below: This method has a time complexity of $O(n^2)$ and a space complexity of $O(1)$, which is very time-consuming with large data volumes. -## Hash-based search: trading space for time +## Hash-Based Search: Trading Space for Time Consider using a hash table where key-value pairs are array elements and element indices respectively. Loop through the array, performing the steps shown in the figure below in each round: diff --git a/en/docs/chapter_searching/searching_algorithm_revisited.md b/en/docs/chapter_searching/searching_algorithm_revisited.md index 4a24b8ab9..adc3d4cc8 100644 --- a/en/docs/chapter_searching/searching_algorithm_revisited.md +++ b/en/docs/chapter_searching/searching_algorithm_revisited.md @@ -1,4 +1,4 @@ -# Searching algorithms revisited +# Searching Algorithms Revisited Searching algorithms are used to search for one or a group of elements that meet specific conditions in data structures (such as arrays, linked lists, trees, or graphs). @@ -9,7 +9,7 @@ Searching algorithms can be divided into the following two categories based on t It's not hard to see that these topics have all been covered in previous chapters, so searching algorithms are not unfamiliar to us. In this section, we will approach from a more systematic perspective and re-examine searching algorithms. -## Brute-force search +## Brute-Force Search Brute-force search locates target elements by traversing each element of the data structure. @@ -20,7 +20,7 @@ The advantage of brute-force search is that it is simple and has good generality However, **the time complexity of such algorithms is $O(n)$**, where $n$ is the number of elements, so performance is poor when dealing with large amounts of data. -## Adaptive search +## Adaptive Search Adaptive search utilizes the unique properties of data (such as orderliness) to optimize the search process, thereby locating target elements more efficiently. @@ -36,7 +36,7 @@ However, **using these algorithms often requires data preprocessing**. For examp Adaptive search algorithms are often called lookup algorithms, **mainly used to quickly retrieve target elements in specific data structures**. -## Search method selection +## Search Method Selection Given a dataset of size $n$, we can use linear search, binary search, tree search, hash-based search, and other methods to search for the target element. The working principles of each method are shown in the figure below. diff --git a/en/docs/chapter_searching/summary.md b/en/docs/chapter_searching/summary.md index aa834a598..3d8b31c0a 100644 --- a/en/docs/chapter_searching/summary.md +++ b/en/docs/chapter_searching/summary.md @@ -1,5 +1,7 @@ # Summary +### Key Review + - Binary search relies on data orderliness and progressively reduces the search interval by half through loops. It requires input data to be sorted and is only applicable to arrays or data structures based on array implementations. - Brute-force search locates data by traversing the data structure. Linear search is applicable to arrays and linked lists, while breadth-first search and depth-first search are applicable to graphs and trees. Such algorithms have good generality and require no data preprocessing, but have a relatively high time complexity of $O(n)$. - Hash-based search, tree search, and binary search are efficient search methods that can quickly locate target elements in specific data structures. Such algorithms are highly efficient with time complexity reaching $O(\log n)$ or even $O(1)$, but typically require additional data structures. diff --git a/en/docs/chapter_sorting/bubble_sort.md b/en/docs/chapter_sorting/bubble_sort.md index 9873539c1..2fdbf0260 100644 --- a/en/docs/chapter_sorting/bubble_sort.md +++ b/en/docs/chapter_sorting/bubble_sort.md @@ -1,4 +1,4 @@ -# Bubble sort +# Bubble Sort Bubble sort (bubble sort) achieves sorting by continuously comparing and swapping adjacent elements. This process is like bubbles rising from the bottom to the top, hence the name bubble sort. @@ -25,7 +25,7 @@ As shown in the figure below, the bubbling process can be simulated using elemen === "<7>" ![bubble_operation_step7](bubble_sort.assets/bubble_operation_step7.png) -## Algorithm flow +## Algorithm Flow Assume the array has length $n$. The steps of bubble sort are shown in the figure below. @@ -42,7 +42,7 @@ Example code is as follows: [file]{bubble_sort}-[class]{}-[func]{bubble_sort} ``` -## Efficiency optimization +## Efficiency Optimization We notice that if no swap operations are performed during a certain round of "bubbling", it means the array has already completed sorting and can directly return the result. Therefore, we can add a flag `flag` to monitor this situation and return immediately once it occurs. @@ -52,7 +52,7 @@ After optimization, the worst-case time complexity and average time complexity o [file]{bubble_sort}-[class]{}-[func]{bubble_sort_with_flag} ``` -## Algorithm characteristics +## Algorithm Characteristics - **Time complexity of $O(n^2)$, adaptive sorting**: The array lengths traversed in each round of "bubbling" are $n - 1$, $n - 2$, $\dots$, $2$, $1$, totaling $(n - 1) n / 2$. After introducing the `flag` optimization, the best-case time complexity can reach $O(n)$. - **Space complexity of $O(1)$, in-place sorting**: Pointers $i$ and $j$ use a constant amount of extra space. diff --git a/en/docs/chapter_sorting/bucket_sort.md b/en/docs/chapter_sorting/bucket_sort.md index e0d617520..aca0fe479 100644 --- a/en/docs/chapter_sorting/bucket_sort.md +++ b/en/docs/chapter_sorting/bucket_sort.md @@ -1,10 +1,10 @@ -# Bucket sort +# Bucket Sort The several sorting algorithms mentioned earlier all belong to "comparison-based sorting algorithms", which achieve sorting by comparing the size of elements. The time complexity of such sorting algorithms cannot exceed $O(n \log n)$. Next, we will explore several "non-comparison sorting algorithms", whose time complexity can reach linear order. Bucket sort (bucket sort) is a typical application of the divide-and-conquer strategy. It works by setting up buckets with size order, each bucket corresponding to a data range, evenly distributing data to each bucket; then, sorting within each bucket separately; finally, merging all data in the order of the buckets. -## Algorithm flow +## Algorithm Flow Consider an array of length $n$, whose elements are floating-point numbers in the range $[0, 1)$. The flow of bucket sort is shown in the figure below. @@ -20,7 +20,7 @@ The code is as follows: [file]{bucket_sort}-[class]{}-[func]{bucket_sort} ``` -## Algorithm characteristics +## Algorithm Characteristics Bucket sort is suitable for processing very large data volumes. For example, if the input data contains 1 million elements and system memory cannot load all the data at once, the data can be divided into 1000 buckets, each bucket sorted separately, and then the results merged. @@ -28,7 +28,7 @@ Bucket sort is suitable for processing very large data volumes. For example, if - **Space complexity of $O(n + k)$, non-in-place sorting**: Additional space is required for $k$ buckets and a total of $n$ elements. - Whether bucket sort is stable depends on whether the algorithm for sorting elements within buckets is stable. -## How to achieve even distribution +## How to Achieve Even Distribution Theoretically, bucket sort can achieve $O(n)$ time complexity. **The key is to evenly distribute elements to each bucket**, because real data is often not evenly distributed. For example, if we want to evenly distribute all products on Taobao into 10 buckets by price range, there may be very many products below 100 yuan and very few above 1000 yuan. If the price intervals are evenly divided into 10, the difference in the number of products in each bucket will be very large. diff --git a/en/docs/chapter_sorting/counting_sort.md b/en/docs/chapter_sorting/counting_sort.md index 81a157a6a..5ab813678 100644 --- a/en/docs/chapter_sorting/counting_sort.md +++ b/en/docs/chapter_sorting/counting_sort.md @@ -1,8 +1,8 @@ -# Counting sort +# Counting Sort Counting sort (counting sort) achieves sorting by counting the number of elements, typically applied to integer arrays. -## Simple implementation +## Simple Implementation Let's start with a simple example. Given an array `nums` of length $n$, where the elements are all "non-negative integers", the overall flow of counting sort is shown in the figure below. @@ -22,7 +22,7 @@ The code is as follows: From the perspective of bucket sort, we can regard each index of the counting array `counter` in counting sort as a bucket, and the process of counting quantities as distributing each element to the corresponding bucket. Essentially, counting sort is a special case of bucket sort for integer data. -## Complete implementation +## Complete Implementation Observant readers may have noticed that **if the input data is objects, step `3.` above becomes invalid**. Suppose the input data is product objects, and we want to sort the products by price (a member variable of the class), but the above algorithm can only give the sorting result of prices. @@ -69,7 +69,7 @@ The implementation code of counting sort is as follows: [file]{counting_sort}-[class]{}-[func]{counting_sort} ``` -## Algorithm characteristics +## Algorithm Characteristics - **Time complexity of $O(n + m)$, non-adaptive sorting**: Involves traversing `nums` and traversing `counter`, both using linear time. Generally, $n \gg m$, and time complexity tends toward $O(n)$. - **Space complexity of $O(n + m)$, non-in-place sorting**: Uses arrays `res` and `counter` of lengths $n$ and $m$ respectively. diff --git a/en/docs/chapter_sorting/heap_sort.md b/en/docs/chapter_sorting/heap_sort.md index 18415f78e..9fc79a86f 100644 --- a/en/docs/chapter_sorting/heap_sort.md +++ b/en/docs/chapter_sorting/heap_sort.md @@ -1,4 +1,4 @@ -# Heap sort +# Heap Sort !!! tip @@ -11,7 +11,7 @@ Although the above method is feasible, it requires an additional array to save the popped elements, which is quite wasteful of space. In practice, we usually use a more elegant implementation method. -## Algorithm flow +## Algorithm Flow Assume the array length is $n$. The flow of heap sort is shown in the figure below. @@ -66,7 +66,7 @@ In the code implementation, we use the same top-to-bottom heapify function `sift [file]{heap_sort}-[class]{}-[func]{heap_sort} ``` -## Algorithm characteristics +## Algorithm Characteristics - **Time complexity of $O(n \log n)$, non-adaptive sorting**: The build heap operation uses $O(n)$ time. Extracting the largest element from the heap has a time complexity of $O(\log n)$, looping a total of $n - 1$ rounds. - **Space complexity of $O(1)$, in-place sorting**: A few pointer variables use $O(1)$ space. Element swapping and heapify operations are both performed on the original array. diff --git a/en/docs/chapter_sorting/insertion_sort.md b/en/docs/chapter_sorting/insertion_sort.md index 19c9bbb55..e954ae5a9 100644 --- a/en/docs/chapter_sorting/insertion_sort.md +++ b/en/docs/chapter_sorting/insertion_sort.md @@ -1,4 +1,4 @@ -# Insertion sort +# Insertion Sort Insertion sort (insertion sort) is a simple sorting algorithm that works very similarly to the process of manually organizing a deck of cards. @@ -8,7 +8,7 @@ The figure below shows the operation flow of inserting an element into the array ![Single insertion operation](insertion_sort.assets/insertion_operation.png) -## Algorithm flow +## Algorithm Flow The overall flow of insertion sort is shown in the figure below. @@ -25,13 +25,13 @@ Example code is as follows: [file]{insertion_sort}-[class]{}-[func]{insertion_sort} ``` -## Algorithm characteristics +## Algorithm Characteristics - **Time complexity of $O(n^2)$, adaptive sorting**: In the worst case, each insertion operation requires loops of $n - 1$, $n-2$, $\dots$, $2$, $1$, summing to $(n - 1) n / 2$, so the time complexity is $O(n^2)$. When encountering ordered data, the insertion operation will terminate early. When the input array is completely ordered, insertion sort achieves the best-case time complexity of $O(n)$. - **Space complexity of $O(1)$, in-place sorting**: Pointers $i$ and $j$ use a constant amount of extra space. - **Stable sorting**: During the insertion operation process, we insert elements to the right of equal elements, without changing their order. -## Advantages of insertion sort +## Advantages of Insertion Sort The time complexity of insertion sort is $O(n^2)$, while the time complexity of quick sort, which we will learn about next, is $O(n \log n)$. Although insertion sort has a higher time complexity, **insertion sort is usually faster for smaller data volumes**. diff --git a/en/docs/chapter_sorting/merge_sort.md b/en/docs/chapter_sorting/merge_sort.md index 0edd25c11..f4cf8d3fc 100644 --- a/en/docs/chapter_sorting/merge_sort.md +++ b/en/docs/chapter_sorting/merge_sort.md @@ -1,4 +1,4 @@ -# Merge sort +# Merge Sort Merge sort (merge sort) is a sorting algorithm based on the divide-and-conquer strategy, which includes the "divide" and "merge" phases shown in the figure below. @@ -7,7 +7,7 @@ ![Divide and merge phases of merge sort](merge_sort.assets/merge_sort_overview.png) -## Algorithm flow +## Algorithm Flow As shown in the figure below, the "divide phase" recursively splits the array from the midpoint into two sub-arrays from top to bottom. @@ -57,13 +57,13 @@ The implementation of merge sort is shown in the code below. Note that the inter [file]{merge_sort}-[class]{}-[func]{merge_sort} ``` -## Algorithm characteristics +## Algorithm Characteristics - **Time complexity of $O(n \log n)$, non-adaptive sorting**: The division produces a recursion tree of height $\log n$, and the total number of merge operations at each level is $n$, so the overall time complexity is $O(n \log n)$. - **Space complexity of $O(n)$, non-in-place sorting**: The recursion depth is $\log n$, using $O(\log n)$ size of stack frame space. The merge operation requires the aid of an auxiliary array, using $O(n)$ size of additional space. - **Stable sorting**: In the merge process, the order of equal elements remains unchanged. -## Linked list sorting +## Linked List Sorting For linked lists, merge sort has significant advantages over other sorting algorithms, **and can optimize the space complexity of linked list sorting tasks to $O(1)$**. diff --git a/en/docs/chapter_sorting/quick_sort.md b/en/docs/chapter_sorting/quick_sort.md index cfc0121fa..51def23c1 100644 --- a/en/docs/chapter_sorting/quick_sort.md +++ b/en/docs/chapter_sorting/quick_sort.md @@ -1,4 +1,4 @@ -# Quick sort +# Quick Sort Quick sort (quick sort) is a sorting algorithm based on the divide-and-conquer strategy, which operates efficiently and is widely applied. @@ -45,7 +45,7 @@ After sentinel partitioning is complete, the original array is divided into thre [file]{quick_sort}-[class]{quick_sort}-[func]{partition} ``` -## Algorithm flow +## Algorithm Flow The overall flow of quick sort is shown in the figure below. @@ -59,13 +59,13 @@ The overall flow of quick sort is shown in the figure below. [file]{quick_sort}-[class]{quick_sort}-[func]{quick_sort} ``` -## Algorithm characteristics +## Algorithm Characteristics - **Time complexity of $O(n \log n)$, non-adaptive sorting**: In the average case, the number of recursive levels of sentinel partitioning is $\log n$, and the total number of loops at each level is $n$, using $O(n \log n)$ time overall. In the worst case, each round of sentinel partitioning divides an array of length $n$ into two sub-arrays of length $0$ and $n - 1$, at which point the number of recursive levels reaches $n$, the number of loops at each level is $n$, and the total time used is $O(n^2)$. - **Space complexity of $O(n)$, in-place sorting**: In the case where the input array is completely reversed, the worst recursive depth reaches $n$, using $O(n)$ stack frame space. The sorting operation is performed on the original array without the aid of an additional array. - **Non-stable sorting**: In the last step of sentinel partitioning, the pivot may be swapped to the right of equal elements. -## Why is quick sort fast +## Why Is Quick Sort Fast From the name, we can see that quick sort should have certain advantages in terms of efficiency. Although the average time complexity of quick sort is the same as "merge sort" and "heap sort", quick sort is usually more efficient, mainly for the following reasons. @@ -73,7 +73,7 @@ From the name, we can see that quick sort should have certain advantages in term - **High cache utilization**: When performing sentinel partitioning operations, the system can load the entire sub-array into the cache, so element access efficiency is relatively high. Algorithms like "heap sort" require jump-style access to elements, thus lacking this characteristic. - **Small constant coefficient of complexity**: Among the three algorithms mentioned above, quick sort has the smallest total number of operations such as comparisons, assignments, and swaps. This is similar to the reason why "insertion sort" is faster than "bubble sort". -## Pivot optimization +## Pivot Optimization **Quick sort may have reduced time efficiency for certain inputs**. Take an extreme example: suppose the input array is completely reversed. Since we select the leftmost element as the pivot, after sentinel partitioning is complete, the pivot is swapped to the rightmost end of the array, causing the left sub-array length to be $n - 1$ and the right sub-array length to be $0$. If we recurse down like this, each round of sentinel partitioning will have a sub-array length of $0$, the divide-and-conquer strategy fails, and quick sort degrades to a form approximate to "bubble sort". @@ -89,7 +89,7 @@ Example code is as follows: [file]{quick_sort}-[class]{quick_sort_median}-[func]{partition} ``` -## Recursive depth optimization +## Recursive Depth Optimization **For certain inputs, quick sort may occupy more space**. Taking a completely ordered input array as an example, let the length of the sub-array in recursion be $m$. Each round of sentinel partitioning will produce a left sub-array of length $0$ and a right sub-array of length $m - 1$, which means that the problem scale reduced per recursive call is very small (only one element is reduced), and the height of the recursion tree will reach $n - 1$, at which point $O(n)$ size of stack frame space is required. diff --git a/en/docs/chapter_sorting/radix_sort.md b/en/docs/chapter_sorting/radix_sort.md index 39b9dff77..d150b69ed 100644 --- a/en/docs/chapter_sorting/radix_sort.md +++ b/en/docs/chapter_sorting/radix_sort.md @@ -1,10 +1,10 @@ -# Radix sort +# Radix Sort The previous section introduced counting sort, which is suitable for situations where the data volume $n$ is large but the data range $m$ is small. Suppose we need to sort $n = 10^6$ student IDs, and the student ID is an 8-digit number, which means the data range $m = 10^8$ is very large. Using counting sort would require allocating a large amount of memory space, whereas radix sort can avoid this situation. Radix sort (radix sort) has a core idea consistent with counting sort, which also achieves sorting by counting quantities. Building on this, radix sort utilizes the progressive relationship between the digits of numbers, sorting each digit in turn to obtain the final sorting result. -## Algorithm flow +## Algorithm Flow Taking student ID data as an example, assume the lowest digit is the $1$st digit and the highest digit is the $8$th digit. The flow of radix sort is shown in the figure below. @@ -32,7 +32,7 @@ Additionally, we need to slightly modify the counting sort code to make it sort In successive sorting rounds, the result of a later round will override the result of an earlier round. For example, if the first round result is $a < b$, while the second round result is $a > b$, then the second round's result will replace the first round's result. Since higher-order digits have higher priority than lower-order digits, we should sort the lower digits first and then sort the higher digits. -## Algorithm characteristics +## Algorithm Characteristics Compared to counting sort, radix sort is suitable for larger numerical ranges, **but the prerequisite is that the data must be representable in a fixed number of digits, and the number of digits should not be too large**. For example, floating-point numbers are not suitable for radix sort because their number of digits $k$ may be too large, potentially leading to time complexity $O(nk) \gg O(n^2)$. diff --git a/en/docs/chapter_sorting/selection_sort.md b/en/docs/chapter_sorting/selection_sort.md index 03af7d454..eb02a8e17 100644 --- a/en/docs/chapter_sorting/selection_sort.md +++ b/en/docs/chapter_sorting/selection_sort.md @@ -1,4 +1,4 @@ -# Selection sort +# Selection Sort Selection sort (selection sort) works very simply: it opens a loop, and in each round, selects the smallest element from the unsorted interval and places it at the end of the sorted interval. @@ -49,7 +49,7 @@ In the code, we use $k$ to record the smallest element within the unsorted inter [file]{selection_sort}-[class]{}-[func]{selection_sort} ``` -## Algorithm characteristics +## Algorithm Characteristics - **Time complexity of $O(n^2)$, non-adaptive sorting**: The outer loop has $n - 1$ rounds in total. The length of the unsorted interval in the first round is $n$, and the length of the unsorted interval in the last round is $2$. That is, each round of the outer loop contains $n$, $n - 1$, $\dots$, $3$, $2$ inner loop iterations, summing to $\frac{(n - 1)(n + 2)}{2}$. - **Space complexity of $O(1)$, in-place sorting**: Pointers $i$ and $j$ use a constant amount of extra space. diff --git a/en/docs/chapter_sorting/sorting_algorithm.md b/en/docs/chapter_sorting/sorting_algorithm.md index d93ff3082..f3d3591b7 100644 --- a/en/docs/chapter_sorting/sorting_algorithm.md +++ b/en/docs/chapter_sorting/sorting_algorithm.md @@ -1,4 +1,4 @@ -# Sorting algorithm +# Sorting Algorithm Sorting algorithm (sorting algorithm) is used to arrange a group of data in a specific order. Sorting algorithms have extensive applications because ordered data can usually be searched, analyzed, and processed more efficiently. @@ -6,7 +6,7 @@ As shown in the figure below, data types in sorting algorithms can be integers, ![Data type and criterion examples](sorting_algorithm.assets/sorting_examples.png) -## Evaluation dimensions +## Evaluation Dimensions **Execution efficiency**: We expect the time complexity of sorting algorithms to be as low as possible, with a smaller total number of operations (reducing the constant factor in time complexity). For large data volumes, execution efficiency is particularly important. @@ -17,7 +17,7 @@ As shown in the figure below, data types in sorting algorithms can be integers, Stable sorting is a necessary condition for multi-level sorting scenarios. Suppose we have a table storing student information, where column 1 and column 2 are name and age, respectively. In this case, unstable sorting may cause the ordered nature of the input data to be lost: ```shell -# Input data is sorted by name +# Input Data Is Sorted by Name # (name, age) ('A', 19) ('B', 18) @@ -25,9 +25,9 @@ Stable sorting is a necessary condition for multi-level sorting scenarios. Suppo ('D', 19) ('E', 23) -# Assuming we use an unstable sorting algorithm to sort the list by age, -# in the result, the relative positions of ('D', 19) and ('A', 19) are changed, -# and the property that the input data is sorted by name is lost +# Assuming We Use an Unstable Sorting Algorithm to Sort the List by Age, +# In the Result, the Relative Positions of ('D', 19) and ('A', 19) Are Changed, +# And the Property That the Input Data Is Sorted by Name Is Lost ('B', 18) ('D', 19) ('A', 19) @@ -39,7 +39,7 @@ Stable sorting is a necessary condition for multi-level sorting scenarios. Suppo **Comparison-based or not**: Comparison-based sorting relies on comparison operators ($<$, $=$, $>$) to determine the relative order of elements, thereby sorting the entire array, with a theoretical optimal time complexity of $O(n \log n)$. Non-comparison sorting does not use comparison operators and can achieve a time complexity of $O(n)$, but its versatility is relatively limited. -## Ideal sorting algorithm +## Ideal Sorting Algorithm **Fast execution, in-place, stable, adaptive, good versatility**. Clearly, no sorting algorithm has been discovered to date that combines all of these characteristics. Therefore, when selecting a sorting algorithm, it is necessary to decide based on the specific characteristics of the data and the requirements of the problem. diff --git a/en/docs/chapter_sorting/summary.md b/en/docs/chapter_sorting/summary.md index 5ea33fb81..566f8ffc0 100644 --- a/en/docs/chapter_sorting/summary.md +++ b/en/docs/chapter_sorting/summary.md @@ -1,6 +1,6 @@ # Summary -### Key review +### Key Review - Bubble sort achieves sorting by swapping adjacent elements. By adding a flag to enable early return, we can optimize the best-case time complexity of bubble sort to $O(n)$. - Insertion sort completes sorting by inserting elements from the unsorted interval into the correct position in the sorted interval each round. Although the time complexity of insertion sort is $O(n^2)$, it is very popular in small data volume sorting tasks because it involves relatively few unit operations. diff --git a/en/docs/chapter_stack_and_queue/deque.md b/en/docs/chapter_stack_and_queue/deque.md index 3a87e195d..7c14bea4b 100644 --- a/en/docs/chapter_stack_and_queue/deque.md +++ b/en/docs/chapter_stack_and_queue/deque.md @@ -389,7 +389,7 @@ Similarly, we can directly use the deque classes already implemented in programm ``` -??? pythontutor "Visualize Execution" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=from%20collections%20import%20deque%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%0A%20%20%20%20deq%20%3D%20deque%28%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%85%A5%E9%98%9F%0A%20%20%20%20deq.append%282%29%20%20%23%20%E6%B7%BB%E5%8A%A0%E8%87%B3%E9%98%9F%E5%B0%BE%0A%20%20%20%20deq.append%285%29%0A%20%20%20%20deq.append%284%29%0A%20%20%20%20deq.appendleft%283%29%20%20%23%20%E6%B7%BB%E5%8A%A0%E8%87%B3%E9%98%9F%E9%A6%96%0A%20%20%20%20deq.appendleft%281%29%0A%20%20%20%20print%28%22%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%20deque%20%3D%22,%20deq%29%0A%0A%20%20%20%20%23%20%E8%AE%BF%E9%97%AE%E5%85%83%E7%B4%A0%0A%20%20%20%20front%20%3D%20deq%5B0%5D%20%20%23%20%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%0A%20%20%20%20print%28%22%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%20front%20%3D%22,%20front%29%0A%20%20%20%20rear%20%3D%20deq%5B-1%5D%20%20%23%20%E9%98%9F%E5%B0%BE%E5%85%83%E7%B4%A0%0A%20%20%20%20print%28%22%E9%98%9F%E5%B0%BE%E5%85%83%E7%B4%A0%20rear%20%3D%22,%20rear%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%87%BA%E9%98%9F%0A%20%20%20%20pop_front%20%3D%20deq.popleft%28%29%20%20%23%20%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%E5%87%BA%E9%98%9F%0A%20%20%20%20print%28%22%E9%98%9F%E9%A6%96%E5%87%BA%E9%98%9F%E5%85%83%E7%B4%A0%20%20pop_front%20%3D%22,%20pop_front%29%0A%20%20%20%20print%28%22%E9%98%9F%E9%A6%96%E5%87%BA%E9%98%9F%E5%90%8E%20deque%20%3D%22,%20deq%29%0A%20%20%20%20pop_rear%20%3D%20deq.pop%28%29%20%20%23%20%E9%98%9F%E5%B0%BE%E5%85%83%E7%B4%A0%E5%87%BA%E9%98%9F%0A%20%20%20%20print%28%22%E9%98%9F%E5%B0%BE%E5%87%BA%E9%98%9F%E5%85%83%E7%B4%A0%20%20pop_rear%20%3D%22,%20pop_rear%29%0A%20%20%20%20print%28%22%E9%98%9F%E5%B0%BE%E5%87%BA%E9%98%9F%E5%90%8E%20deque%20%3D%22,%20deq%29%0A%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E7%9A%84%E9%95%BF%E5%BA%A6%0A%20%20%20%20size%20%3D%20len%28deq%29%0A%20%20%20%20print%28%22%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E9%95%BF%E5%BA%A6%20size%20%3D%22,%20size%29%0A%0A%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%0A%20%20%20%20is_empty%20%3D%20len%28deq%29%20%3D%3D%200%0A%20%20%20%20print%28%22%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%20%3D%22,%20is_empty%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/docs/chapter_stack_and_queue/queue.md b/en/docs/chapter_stack_and_queue/queue.md index 8f43c8d9b..da16ca4bf 100755 --- a/en/docs/chapter_stack_and_queue/queue.md +++ b/en/docs/chapter_stack_and_queue/queue.md @@ -362,7 +362,7 @@ We can directly use the ready-made queue classes in programming languages: ``` -??? pythontutor "Visualize Execution" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=from%20collections%20import%20deque%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E9%98%9F%E5%88%97%0A%20%20%20%20%23%20%E5%9C%A8%20Python%20%E4%B8%AD%EF%BC%8C%E6%88%91%E4%BB%AC%E4%B8%80%E8%88%AC%E5%B0%86%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E7%B1%BB%20deque%20%E7%9C%8B%E4%BD%9C%E9%98%9F%E5%88%97%E4%BD%BF%E7%94%A8%0A%20%20%20%20%23%20%E8%99%BD%E7%84%B6%20queue.Queue%28%29%20%E6%98%AF%E7%BA%AF%E6%AD%A3%E7%9A%84%E9%98%9F%E5%88%97%E7%B1%BB%EF%BC%8C%E4%BD%86%E4%B8%8D%E5%A4%AA%E5%A5%BD%E7%94%A8%0A%20%20%20%20que%20%3D%20deque%28%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%85%A5%E9%98%9F%0A%20%20%20%20que.append%281%29%0A%20%20%20%20que.append%283%29%0A%20%20%20%20que.append%282%29%0A%20%20%20%20que.append%285%29%0A%20%20%20%20que.append%284%29%0A%20%20%20%20print%28%22%E9%98%9F%E5%88%97%20que%20%3D%22,%20que%29%0A%0A%20%20%20%20%23%20%E8%AE%BF%E9%97%AE%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%0A%20%20%20%20front%20%3D%20que%5B0%5D%0A%20%20%20%20print%28%22%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%20front%20%3D%22,%20front%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%87%BA%E9%98%9F%0A%20%20%20%20pop%20%3D%20que.popleft%28%29%0A%20%20%20%20print%28%22%E5%87%BA%E9%98%9F%E5%85%83%E7%B4%A0%20pop%20%3D%22,%20pop%29%0A%20%20%20%20print%28%22%E5%87%BA%E9%98%9F%E5%90%8E%20que%20%3D%22,%20que%29%0A%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E9%98%9F%E5%88%97%E7%9A%84%E9%95%BF%E5%BA%A6%0A%20%20%20%20size%20%3D%20len%28que%29%0A%20%20%20%20print%28%22%E9%98%9F%E5%88%97%E9%95%BF%E5%BA%A6%20size%20%3D%22,%20size%29%0A%0A%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E9%98%9F%E5%88%97%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%0A%20%20%20%20is_empty%20%3D%20len%28que%29%20%3D%3D%200%0A%20%20%20%20print%28%22%E9%98%9F%E5%88%97%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%20%3D%22,%20is_empty%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/docs/chapter_stack_and_queue/stack.md b/en/docs/chapter_stack_and_queue/stack.md index 32f8f90d6..3c5bd9695 100755 --- a/en/docs/chapter_stack_and_queue/stack.md +++ b/en/docs/chapter_stack_and_queue/stack.md @@ -355,7 +355,7 @@ Typically, we can directly use the built-in stack class provided by the programm ``` -??? pythontutor "Visualize Execution" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%A0%88%0A%20%20%20%20%23%20Python%20%E6%B2%A1%E6%9C%89%E5%86%85%E7%BD%AE%E7%9A%84%E6%A0%88%E7%B1%BB%EF%BC%8C%E5%8F%AF%E4%BB%A5%E6%8A%8A%20list%20%E5%BD%93%E4%BD%9C%E6%A0%88%E6%9D%A5%E4%BD%BF%E7%94%A8%0A%20%20%20%20stack%20%3D%20%5B%5D%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%85%A5%E6%A0%88%0A%20%20%20%20stack.append%281%29%0A%20%20%20%20stack.append%283%29%0A%20%20%20%20stack.append%282%29%0A%20%20%20%20stack.append%285%29%0A%20%20%20%20stack.append%284%29%0A%20%20%20%20print%28%22%E6%A0%88%20stack%20%3D%22,%20stack%29%0A%0A%20%20%20%20%23%20%E8%AE%BF%E9%97%AE%E6%A0%88%E9%A1%B6%E5%85%83%E7%B4%A0%0A%20%20%20%20peek%20%3D%20stack%5B-1%5D%0A%20%20%20%20print%28%22%E6%A0%88%E9%A1%B6%E5%85%83%E7%B4%A0%20peek%20%3D%22,%20peek%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%87%BA%E6%A0%88%0A%20%20%20%20pop%20%3D%20stack.pop%28%29%0A%20%20%20%20print%28%22%E5%87%BA%E6%A0%88%E5%85%83%E7%B4%A0%20pop%20%3D%22,%20pop%29%0A%20%20%20%20print%28%22%E5%87%BA%E6%A0%88%E5%90%8E%20stack%20%3D%22,%20stack%29%0A%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E6%A0%88%E7%9A%84%E9%95%BF%E5%BA%A6%0A%20%20%20%20size%20%3D%20len%28stack%29%0A%20%20%20%20print%28%22%E6%A0%88%E7%9A%84%E9%95%BF%E5%BA%A6%20size%20%3D%22,%20size%29%0A%0A%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%0A%20%20%20%20is_empty%20%3D%20len%28stack%29%20%3D%3D%200%0A%20%20%20%20print%28%22%E6%A0%88%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%20%3D%22,%20is_empty%29&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false diff --git a/en/docs/chapter_tree/array_representation_of_tree.md b/en/docs/chapter_tree/array_representation_of_tree.md index 5c1bc0433..a882066da 100644 --- a/en/docs/chapter_tree/array_representation_of_tree.md +++ b/en/docs/chapter_tree/array_representation_of_tree.md @@ -1,10 +1,10 @@ -# Array representation of binary trees +# Array Representation of Binary Trees Under the linked list representation, the storage unit of a binary tree is a node `TreeNode`, and nodes are connected by pointers. The previous section introduced the basic operations of binary trees under the linked list representation. So, can we use an array to represent a binary tree? The answer is yes. -## Representing perfect binary trees +## Representing Perfect Binary Trees Let's analyze a simple case first. Given a perfect binary tree, we store all nodes in an array according to the order of level-order traversal, where each node corresponds to a unique array index. @@ -14,7 +14,7 @@ Based on the characteristics of level-order traversal, we can derive a "mapping **The mapping formula plays a role similar to the node references (pointers) in linked lists**. Given any node in the array, we can access its left (right) child node using the mapping formula. -## Representing any binary tree +## Representing Any Binary Tree Perfect binary trees are a special case; in the middle levels of a binary tree, there are typically many `None` values. Since the level-order traversal sequence does not include these `None` values, we cannot infer the number and distribution of `None` values based on this sequence alone. **This means multiple binary tree structures can correspond to the same level-order traversal sequence**. @@ -151,7 +151,7 @@ The following code implements a binary tree based on array representation, inclu [file]{array_binary_tree}-[class]{array_binary_tree}-[func]{} ``` -## Advantages and limitations +## Advantages and Limitations The array representation of binary trees has the following advantages: diff --git a/en/docs/chapter_tree/avl_tree.md b/en/docs/chapter_tree/avl_tree.md index 1d6d1ca56..90b710a7f 100644 --- a/en/docs/chapter_tree/avl_tree.md +++ b/en/docs/chapter_tree/avl_tree.md @@ -1,4 +1,4 @@ -# AVL tree * +# Avl Tree * In the "Binary Search Tree" section, we mentioned that after multiple insertion and removal operations, a binary search tree may degenerate into a linked list. In this case, the time complexity of all operations degrades from $O(\log n)$ to $O(n)$. @@ -12,11 +12,11 @@ For example, in the perfect binary tree shown in the figure below, after inserti In 1962, G. M. Adelson-Velsky and E. M. Landis proposed the AVL tree in their paper "An algorithm for the organization of information". The paper described in detail a series of operations ensuring that after continuously adding and removing nodes, the AVL tree does not degenerate, thus keeping the time complexity of various operations at the $O(\log n)$ level. In other words, in scenarios requiring frequent insertions, deletions, searches, and modifications, the AVL tree can always maintain efficient data operation performance, making it very valuable in applications. -## Common terminology in AVL trees +## Common Terminology in Avl Trees An AVL tree is both a binary search tree and a balanced binary tree, simultaneously satisfying all the properties of these two types of binary trees, hence it is a balanced binary search tree. -### Node height +### Node Height Since the operations related to AVL trees require obtaining node heights, we need to add a `height` variable to the node class: @@ -240,7 +240,7 @@ The "node height" refers to the distance from that node to its farthest leaf nod [file]{avl_tree}-[class]{avl_tree}-[func]{update_height} ``` -### Node balance factor +### Node Balance Factor The balance factor of a node is defined as the height of the node's left subtree minus the height of its right subtree, and the balance factor of a null node is defined as $0$. We also encapsulate the function to obtain the node's balance factor for convenient subsequent use: @@ -252,13 +252,13 @@ The balance factor of a node is defined as the height of the node's left Let the balance factor be $f$, then the balance factor of any node in an AVL tree satisfies $-1 \le f \le 1$. -## Rotations in AVL trees +## Rotations in Avl Trees The characteristic of AVL trees lies in the "rotation" operation, which can restore balance to unbalanced nodes without affecting the inorder traversal sequence of the binary tree. In other words, **rotation operations can both maintain the property of a "binary search tree" and make the tree return to a "balanced binary tree"**. We call nodes with a balance factor absolute value $> 1$ "unbalanced nodes". Depending on the imbalance situation, rotation operations are divided into four types: right rotation, left rotation, left rotation then right rotation, and right rotation then left rotation. Below we describe these rotation operations in detail. -### Right rotation +### Right Rotation As shown in the figure below, the value below the node is the balance factor. From bottom to top, the first unbalanced node in the binary tree is "node 3". We focus on the subtree with this unbalanced node as the root, denoting the node as `node` and its left child as `child`, and perform a "right rotation" operation. After the right rotation is completed, the subtree regains balance and still maintains the properties of a binary search tree. @@ -284,7 +284,7 @@ As shown in the figure below, when the `child` node has a right child (denoted a [file]{avl_tree}-[class]{avl_tree}-[func]{right_rotate} ``` -### Left rotation +### Left Rotation Correspondingly, if considering the "mirror" of the above unbalanced binary tree, the "left rotation" operation shown in the figure below needs to be performed. @@ -300,19 +300,19 @@ It can be observed that **right rotation and left rotation operations are mirror [file]{avl_tree}-[class]{avl_tree}-[func]{left_rotate} ``` -### Left rotation then right rotation +### Left Rotation Then Right Rotation For the unbalanced node 3 in the figure below, using either left rotation or right rotation alone cannot restore the subtree to balance. In this case, a "left rotation" needs to be performed on `child` first, followed by a "right rotation" on `node`. ![Left-right rotation](avl_tree.assets/avltree_left_right_rotate.png) -### Right rotation then left rotation +### Right Rotation Then Left Rotation As shown in the figure below, for the mirror case of the above unbalanced binary tree, a "right rotation" needs to be performed on `child` first, then a "left rotation" on `node`. ![Right-left rotation](avl_tree.assets/avltree_right_left_rotate.png) -### Choice of rotation +### Choice of Rotation The four imbalances shown in the figure below correspond one-to-one with the above cases, requiring right rotation, left rotation then right rotation, right rotation then left rotation, and left rotation operations respectively. @@ -335,9 +335,9 @@ For ease of use, we encapsulate the rotation operations into a function. **With [file]{avl_tree}-[class]{avl_tree}-[func]{rotate} ``` -## Common operations in AVL trees +## Common Operations in Avl Trees -### Node insertion +### Node Insertion The node insertion operation in AVL trees is similar in principle to that in binary search trees. The only difference is that after inserting a node in an AVL tree, a series of unbalanced nodes may appear on the path from that node to the root. Therefore, **we need to start from this node and perform rotation operations from bottom to top, restoring balance to all unbalanced nodes**. The code is as follows: @@ -345,7 +345,7 @@ The node insertion operation in AVL trees is similar in principle to that in bin [file]{avl_tree}-[class]{avl_tree}-[func]{insert_helper} ``` -### Node removal +### Node Removal Similarly, on the basis of the binary search tree's node removal method, rotation operations need to be performed from bottom to top to restore balance to all unbalanced nodes. The code is as follows: @@ -353,11 +353,11 @@ Similarly, on the basis of the binary search tree's node removal method, rotatio [file]{avl_tree}-[class]{avl_tree}-[func]{remove_helper} ``` -### Node search +### Node Search The node search operation in AVL trees is consistent with that in binary search trees, and will not be elaborated here. -## Typical applications of AVL trees +## Typical Applications of Avl Trees - Organizing and storing large-scale data, suitable for scenarios with high-frequency searches and low-frequency insertions and deletions. - Used to build index systems in databases. diff --git a/en/docs/chapter_tree/binary_search_tree.md b/en/docs/chapter_tree/binary_search_tree.md index d9fd02a0a..182fd1838 100755 --- a/en/docs/chapter_tree/binary_search_tree.md +++ b/en/docs/chapter_tree/binary_search_tree.md @@ -1,4 +1,4 @@ -# Binary search tree +# Binary Search Tree As shown in the figure below, a binary search tree satisfies the following conditions. @@ -7,11 +7,11 @@ As shown in the figure below, a binary search tree satisfies the followin ![Binary search tree](binary_search_tree.assets/binary_search_tree.png) -## Operations on a binary search tree +## Operations on a Binary Search Tree We encapsulate the binary search tree as a class `BinarySearchTree` and declare a member variable `root` pointing to the tree's root node. -### Searching for a node +### Searching for a Node Given a target node value `num`, we can search according to the properties of the binary search tree. As shown in the figure below, we declare a node `cur` and start from the binary tree's root node `root`, looping to compare the node value `cur.val` with `num`. @@ -37,7 +37,7 @@ The search operation in a binary search tree works on the same principle as the [file]{binary_search_tree}-[class]{binary_search_tree}-[func]{search} ``` -### Inserting a node +### Inserting a Node Given an element `num` to be inserted, in order to maintain the property of the binary search tree "left subtree < root node < right subtree," the insertion process is as shown in the figure below. @@ -57,7 +57,7 @@ In the code implementation, note the following two points: Similar to searching for a node, inserting a node uses $O(\log n)$ time. -### Removing a node +### Removing a Node First, find the target node in the binary tree, then remove it. Similar to node insertion, we need to ensure that after the removal operation is completed, the binary search tree's property of "left subtree $<$ root node $<$ right subtree" is still maintained. Therefore, depending on the number of child nodes the target node has, we divide it into 0, 1, and 2 three cases, and execute the corresponding node removal operations. @@ -94,7 +94,7 @@ The node removal operation also uses $O(\log n)$ time, where finding the node to [file]{binary_search_tree}-[class]{binary_search_tree}-[func]{remove} ``` -### Inorder traversal is ordered +### Inorder Traversal Is Ordered As shown in the figure below, the inorder traversal of a binary tree follows the "left $\rightarrow$ root $\rightarrow$ right" traversal order, while the binary search tree satisfies the "left child node $<$ root node $<$ right child node" size relationship. @@ -104,7 +104,7 @@ Using the property of inorder traversal being ascending, we can obtain ordered d ![Inorder traversal sequence of a binary search tree](binary_search_tree.assets/bst_inorder_traversal.png) -## Efficiency of binary search trees +## Efficiency of Binary Search Trees Given a set of data, we consider using an array or a binary search tree for storage. Observing the table below, all operations in a binary search tree have logarithmic time complexity, providing stable and efficient performance. Arrays are more efficient than binary search trees only in scenarios with high-frequency additions and low-frequency searches and deletions. @@ -122,7 +122,7 @@ However, if we continuously insert and remove nodes in a binary search tree, it ![Degradation of a binary search tree](binary_search_tree.assets/bst_degradation.png) -## Common applications of binary search trees +## Common Applications of Binary Search Trees - Used as multi-level indexes in systems to implement efficient search, insertion, and removal operations. - Serves as the underlying data structure for certain search algorithms. diff --git a/en/docs/chapter_tree/binary_tree.md b/en/docs/chapter_tree/binary_tree.md index cbbe299e2..e2ec37d6b 100644 --- a/en/docs/chapter_tree/binary_tree.md +++ b/en/docs/chapter_tree/binary_tree.md @@ -1,4 +1,4 @@ -# Binary tree +# Binary Tree A binary tree is a non-linear data structure that represents the derivation relationship between "ancestors" and "descendants" and embodies the divide-and-conquer logic of "one divides into two". Similar to a linked list, the basic unit of a binary tree is a node, and each node contains a value, a reference to its left child node, and a reference to its right child node. @@ -213,7 +213,7 @@ Each node has two references (pointers), pointing respectively to the left-ch ![Parent Node, child Node, subtree](binary_tree.assets/binary_tree_definition.png) -## Common terminology of binary trees +## Common Terminology of Binary Trees The commonly used terminology of binary trees is shown in the figure below. @@ -232,9 +232,9 @@ The commonly used terminology of binary trees is shown in the figure below. Please note that we usually define "height" and "depth" as "the number of edges traversed", but some questions or textbooks may define them as "the number of nodes traversed". In this case, both height and depth need to be incremented by 1. -## Basic operations of binary trees +## Basic Operations of Binary Trees -### Initializing a binary tree +### Initializing a Binary Tree Similar to a linked list, the initialization of a binary tree involves first creating the nodes and then establishing the references (pointers) between them. @@ -461,11 +461,11 @@ Similar to a linked list, the initialization of a binary tree involves first cre ``` -??? pythontutor "Code visualization" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22%E4%BA%8C%E5%8F%89%E6%A0%91%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%B7%A6%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20%E5%8F%B3%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BA%8C%E5%8F%89%E6%A0%91%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E8%8A%82%E7%82%B9%0A%20%20%20%20n1%20%3D%20TreeNode%28val%3D1%29%0A%20%20%20%20n2%20%3D%20TreeNode%28val%3D2%29%0A%20%20%20%20n3%20%3D%20TreeNode%28val%3D3%29%0A%20%20%20%20n4%20%3D%20TreeNode%28val%3D4%29%0A%20%20%20%20n5%20%3D%20TreeNode%28val%3D5%29%0A%20%20%20%20%23%20%E6%9E%84%E5%BB%BA%E8%8A%82%E7%82%B9%E4%B9%8B%E9%97%B4%E7%9A%84%E5%BC%95%E7%94%A8%EF%BC%88%E6%8C%87%E9%92%88%EF%BC%89%0A%20%20%20%20n1.left%20%3D%20n2%0A%20%20%20%20n1.right%20%3D%20n3%0A%20%20%20%20n2.left%20%3D%20n4%0A%20%20%20%20n2.right%20%3D%20n5&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false -### Inserting and removing nodes +### Inserting and Removing Nodes Similar to a linked list, inserting and removing nodes in a binary tree can be achieved by modifying pointers. The figure below provides an example. @@ -629,7 +629,7 @@ Similar to a linked list, inserting and removing nodes in a binary tree can be a ``` -??? pythontutor "Code visualization" +??? pythontutor "Code Visualization" https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22%E4%BA%8C%E5%8F%89%E6%A0%91%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%B7%A6%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20%E5%8F%B3%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BA%8C%E5%8F%89%E6%A0%91%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E8%8A%82%E7%82%B9%0A%20%20%20%20n1%20%3D%20TreeNode%28val%3D1%29%0A%20%20%20%20n2%20%3D%20TreeNode%28val%3D2%29%0A%20%20%20%20n3%20%3D%20TreeNode%28val%3D3%29%0A%20%20%20%20n4%20%3D%20TreeNode%28val%3D4%29%0A%20%20%20%20n5%20%3D%20TreeNode%28val%3D5%29%0A%20%20%20%20%23%20%E6%9E%84%E5%BB%BA%E8%8A%82%E7%82%B9%E4%B9%8B%E9%97%B4%E7%9A%84%E5%BC%95%E7%94%A8%EF%BC%88%E6%8C%87%E9%92%88%EF%BC%89%0A%20%20%20%20n1.left%20%3D%20n2%0A%20%20%20%20n1.right%20%3D%20n3%0A%20%20%20%20n2.left%20%3D%20n4%0A%20%20%20%20n2.right%20%3D%20n5%0A%0A%20%20%20%20%23%20%E6%8F%92%E5%85%A5%E4%B8%8E%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9%0A%20%20%20%20p%20%3D%20TreeNode%280%29%0A%20%20%20%20%23%20%E5%9C%A8%20n1%20-%3E%20n2%20%E4%B8%AD%E9%97%B4%E6%8F%92%E5%85%A5%E8%8A%82%E7%82%B9%20P%0A%20%20%20%20n1.left%20%3D%20p%0A%20%20%20%20p.left%20%3D%20n2%0A%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9%20P%0A%20%20%20%20n1.left%20%3D%20n2&cumulative=false&curInstr=37&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false @@ -637,9 +637,9 @@ Similar to a linked list, inserting and removing nodes in a binary tree can be a It should be noted that inserting nodes may change the original logical structure of the binary tree, while removing nodes typically involves removing the node and all its subtrees. Therefore, in a binary tree, insertion and removal are usually performed through a set of operations to achieve meaningful outcomes. -## Common types of binary trees +## Common Types of Binary Trees -### Perfect binary tree +### Perfect Binary Tree As shown in the figure below, a perfect binary tree has all levels completely filled with nodes. In a perfect binary tree, leaf nodes have a degree of $0$, while all other nodes have a degree of $2$. If the tree height is $h$, the total number of nodes is $2^{h+1} - 1$, exhibiting a standard exponential relationship that reflects the common phenomenon of cell division in nature. @@ -649,25 +649,25 @@ As shown in the figure below, a perfect binary tree has all levels comple ![Perfect binary tree](binary_tree.assets/perfect_binary_tree.png) -### Complete binary tree +### Complete Binary Tree As shown in the figure below, a complete binary tree only allows the bottom level to be incompletely filled, and the nodes at the bottom level must be filled continuously from left to right. Note that a perfect binary tree is also a complete binary tree. ![Complete binary tree](binary_tree.assets/complete_binary_tree.png) -### Full binary tree +### Full Binary Tree As shown in the figure below, in a full binary tree, all nodes except leaf nodes have two child nodes. ![Full binary tree](binary_tree.assets/full_binary_tree.png) -### Balanced binary tree +### Balanced Binary Tree As shown in the figure below, in a balanced binary tree, the absolute difference between the height of the left and right subtrees of any node does not exceed 1. ![Balanced binary tree](binary_tree.assets/balanced_binary_tree.png) -## Degeneration of binary trees +## Degeneration of Binary Trees The figure below shows the ideal and degenerate structures of binary trees. When every level of a binary tree is filled, it reaches the "perfect binary tree" state; when all nodes are biased toward one side, the binary tree degenerates into a "linked list". diff --git a/en/docs/chapter_tree/binary_tree_traversal.md b/en/docs/chapter_tree/binary_tree_traversal.md index c0c1308c4..5d553d7c6 100755 --- a/en/docs/chapter_tree/binary_tree_traversal.md +++ b/en/docs/chapter_tree/binary_tree_traversal.md @@ -1,10 +1,10 @@ -# Binary tree traversal +# Binary Tree Traversal From a physical structure perspective, a tree is a data structure based on linked lists. Hence, its traversal method involves accessing nodes one by one through pointers. However, a tree is a non-linear data structure, which makes traversing a tree more complex than traversing a linked list, requiring the assistance of search algorithms. The common traversal methods for binary trees include level-order traversal, pre-order traversal, in-order traversal, and post-order traversal. -## Level-order traversal +## Level-Order Traversal As shown in the figure below, level-order traversal traverses the binary tree from top to bottom, layer by layer. Within each level, it visits nodes from left to right. @@ -12,7 +12,7 @@ Level-order traversal is essentially breadth-first traversal, also known ![Level-order traversal of a binary tree](binary_tree_traversal.assets/binary_tree_bfs.png) -### Code implementation +### Code Implementation Breadth-first traversal is typically implemented with the help of a "queue". The queue follows the "first in, first out" rule, while breadth-first traversal follows the "layer-by-layer progression" rule; the underlying ideas of the two are consistent. The implementation code is as follows: @@ -20,12 +20,12 @@ Breadth-first traversal is typically implemented with the help of a "queue". The [file]{binary_tree_bfs}-[class]{}-[func]{level_order} ``` -### Complexity analysis +### Complexity Analysis - **Time complexity is $O(n)$**: All nodes are visited once, using $O(n)$ time, where $n$ is the number of nodes. - **Space complexity is $O(n)$**: In the worst case, i.e., a full binary tree, before traversing to the bottom level, the queue contains at most $(n + 1) / 2$ nodes simultaneously, occupying $O(n)$ space. -## Preorder, inorder, and postorder traversal +## Preorder, Inorder, and Postorder Traversal Correspondingly, preorder, inorder, and postorder traversals all belong to depth-first traversal, also known as depth-first search (DFS), which embodies a "first go to the end, then backtrack and continue" traversal method. @@ -33,7 +33,7 @@ The figure below shows how depth-first traversal works on a binary tree. **Depth ![Preorder, inorder, and postorder traversal of a binary tree](binary_tree_traversal.assets/binary_tree_dfs.png) -### Code implementation +### Code Implementation Depth-first search is usually implemented based on recursion: @@ -83,7 +83,7 @@ The figure below shows the recursive process of preorder traversal of a binary t === "<11>" ![preorder_step11](binary_tree_traversal.assets/preorder_step11.png) -### Complexity analysis +### Complexity Analysis - **Time complexity is $O(n)$**: All nodes are visited once, using $O(n)$ time. - **Space complexity is $O(n)$**: In the worst case, i.e., the tree degenerates into a linked list, the recursion depth reaches $n$, and the system occupies $O(n)$ stack frame space. diff --git a/en/docs/chapter_tree/summary.md b/en/docs/chapter_tree/summary.md index 22cea3590..2bcbf3881 100644 --- a/en/docs/chapter_tree/summary.md +++ b/en/docs/chapter_tree/summary.md @@ -1,6 +1,6 @@ # Summary -### Key review +### Key Review - A binary tree is a non-linear data structure that embodies the divide-and-conquer logic of "one divides into two". Each binary tree node contains a value and two pointers, which respectively point to its left and right child nodes. - For a certain node in a binary tree, the tree formed by its left (right) child node and all nodes below is called the left (right) subtree of that node. diff --git a/en/mkdocs.yml b/en/mkdocs.yml index 322b55d36..e6b0f8104 100644 --- a/en/mkdocs.yml +++ b/en/mkdocs.yml @@ -37,111 +37,111 @@ extra: # Page tree nav: - - Before starting: + - Before Starting: - chapter_hello_algo/index.md - Chapter 0. Preface: # [icon: material/book-open-outline] - chapter_preface/index.md - - 0.1 About this book: chapter_preface/about_the_book.md - - 0.2 How to use this book: chapter_preface/suggestions.md + - 0.1 About This Book: chapter_preface/about_the_book.md + - 0.2 How to Use This Book: chapter_preface/suggestions.md - 0.3 Summary: chapter_preface/summary.md - - Chapter 1. Introduction to algorithms: + - Chapter 1. Encounter With Algorithms: # [icon: material/calculator-variant-outline] - chapter_introduction/index.md - - 1.1 Algorithms are everywhere: chapter_introduction/algorithms_are_everywhere.md - - 1.2 What is an algorithm: chapter_introduction/what_is_dsa.md + - 1.1 Algorithms Are Everywhere: chapter_introduction/algorithms_are_everywhere.md + - 1.2 What Is an Algorithm: chapter_introduction/what_is_dsa.md - 1.3 Summary: chapter_introduction/summary.md - - Chapter 2. Complexity analysis: + - Chapter 2. Complexity Analysis: # [icon: material/timer-sand] - chapter_computational_complexity/index.md - - 2.1 Algorithm efficiency evaluation: chapter_computational_complexity/performance_evaluation.md - - 2.2 Iteration and recursion: chapter_computational_complexity/iteration_and_recursion.md - - 2.3 Time complexity: chapter_computational_complexity/time_complexity.md - - 2.4 Space complexity: chapter_computational_complexity/space_complexity.md + - 2.1 Algorithm Efficiency Evaluation: chapter_computational_complexity/performance_evaluation.md + - 2.2 Iteration and Recursion: chapter_computational_complexity/iteration_and_recursion.md + - 2.3 Time Complexity: chapter_computational_complexity/time_complexity.md + - 2.4 Space Complexity: chapter_computational_complexity/space_complexity.md - 2.5 Summary: chapter_computational_complexity/summary.md - - Chapter 3. Data structures: + - Chapter 3. Data Structures: # [icon: material/shape-outline] - chapter_data_structure/index.md - - 3.1 Classification of data structures: chapter_data_structure/classification_of_data_structure.md - - 3.2 Basic data types: chapter_data_structure/basic_data_types.md - - 3.3 Number encoding *: chapter_data_structure/number_encoding.md - - 3.4 Character encoding *: chapter_data_structure/character_encoding.md + - 3.1 Classification of Data Structures: chapter_data_structure/classification_of_data_structure.md + - 3.2 Basic Data Types: chapter_data_structure/basic_data_types.md + - 3.3 Number Encoding *: chapter_data_structure/number_encoding.md + - 3.4 Character Encoding *: chapter_data_structure/character_encoding.md - 3.5 Summary: chapter_data_structure/summary.md - - Chapter 4. Array and linked list: + - Chapter 4. Array and Linked List: # [icon: material/view-list-outline] - chapter_array_and_linkedlist/index.md - 4.1 Array: chapter_array_and_linkedlist/array.md - - 4.2 Linked list: chapter_array_and_linkedlist/linked_list.md + - 4.2 Linked List: chapter_array_and_linkedlist/linked_list.md - 4.3 List: chapter_array_and_linkedlist/list.md - - 4.4 Memory and cache *: chapter_array_and_linkedlist/ram_and_cache.md + - 4.4 Memory and Cache *: chapter_array_and_linkedlist/ram_and_cache.md - 4.5 Summary: chapter_array_and_linkedlist/summary.md - - Chapter 5. Stack and queue: + - Chapter 5. Stack and Queue: # [icon: material/stack-overflow] - chapter_stack_and_queue/index.md - 5.1 Stack: chapter_stack_and_queue/stack.md - 5.2 Queue: chapter_stack_and_queue/queue.md - - 5.3 Double-ended queue: chapter_stack_and_queue/deque.md + - 5.3 Double-Ended Queue: chapter_stack_and_queue/deque.md - 5.4 Summary: chapter_stack_and_queue/summary.md - Chapter 6. Hashing: # [icon: material/table-search] - chapter_hashing/index.md - - 6.1 Hash table: chapter_hashing/hash_map.md - - 6.2 Hash collision: chapter_hashing/hash_collision.md - - 6.3 Hash algorithm: chapter_hashing/hash_algorithm.md + - 6.1 Hash Table: chapter_hashing/hash_map.md + - 6.2 Hash Collision: chapter_hashing/hash_collision.md + - 6.3 Hash Algorithm: chapter_hashing/hash_algorithm.md - 6.4 Summary: chapter_hashing/summary.md - Chapter 7. Tree: # [icon: material/graph-outline] - chapter_tree/index.md - - 7.1 Binary tree: chapter_tree/binary_tree.md - - 7.2 Binary tree traversal: chapter_tree/binary_tree_traversal.md - - 7.3 Array representation of tree: chapter_tree/array_representation_of_tree.md - - 7.4 Binary search tree: chapter_tree/binary_search_tree.md - - 7.5 AVL tree *: chapter_tree/avl_tree.md + - 7.1 Binary Tree: chapter_tree/binary_tree.md + - 7.2 Binary Tree Traversal: chapter_tree/binary_tree_traversal.md + - 7.3 Array Representation of Tree: chapter_tree/array_representation_of_tree.md + - 7.4 Binary Search Tree: chapter_tree/binary_search_tree.md + - 7.5 AVL Tree *: chapter_tree/avl_tree.md - 7.6 Summary: chapter_tree/summary.md - Chapter 8. Heap: # [icon: material/family-tree] - chapter_heap/index.md - 8.1 Heap: chapter_heap/heap.md - - 8.2 Building a heap: chapter_heap/build_heap.md - - 8.3 Top-k problem: chapter_heap/top_k.md + - 8.2 Building a Heap: chapter_heap/build_heap.md + - 8.3 Top-K Problem: chapter_heap/top_k.md - 8.4 Summary: chapter_heap/summary.md - Chapter 9. Graph: # [icon: material/graphql] - chapter_graph/index.md - 9.1 Graph: chapter_graph/graph.md - - 9.2 Basic operations on graphs: chapter_graph/graph_operations.md - - 9.3 Graph traversal: chapter_graph/graph_traversal.md + - 9.2 Basic Operations on Graphs: chapter_graph/graph_operations.md + - 9.3 Graph Traversal: chapter_graph/graph_traversal.md - 9.4 Summary: chapter_graph/summary.md - Chapter 10. Searching: # [icon: material/text-search] - chapter_searching/index.md - - 10.1 Binary search: chapter_searching/binary_search.md - - 10.2 Binary search insertion: chapter_searching/binary_search_insertion.md - - 10.3 Binary search edge cases: chapter_searching/binary_search_edge.md - - 10.4 Hash optimization strategy: chapter_searching/replace_linear_by_hashing.md - - 10.5 Search algorithms revisited: chapter_searching/searching_algorithm_revisited.md + - 10.1 Binary Search: chapter_searching/binary_search.md + - 10.2 Binary Search Insertion: chapter_searching/binary_search_insertion.md + - 10.3 Binary Search Edge Cases: chapter_searching/binary_search_edge.md + - 10.4 Hash Optimization Strategy: chapter_searching/replace_linear_by_hashing.md + - 10.5 Search Algorithms Revisited: chapter_searching/searching_algorithm_revisited.md - 10.6 Summary: chapter_searching/summary.md - Chapter 11. Sorting: # [icon: material/sort-ascending] - chapter_sorting/index.md - - 11.1 Sorting algorithms: chapter_sorting/sorting_algorithm.md - - 11.2 Selection sort: chapter_sorting/selection_sort.md - - 11.3 Bubble sort: chapter_sorting/bubble_sort.md - - 11.4 Insertion sort: chapter_sorting/insertion_sort.md - - 11.5 Quick sort: chapter_sorting/quick_sort.md - - 11.6 Merge sort: chapter_sorting/merge_sort.md - - 11.7 Heap sort: chapter_sorting/heap_sort.md - - 11.8 Bucket sort: chapter_sorting/bucket_sort.md - - 11.9 Counting sort: chapter_sorting/counting_sort.md - - 11.10 Radix sort: chapter_sorting/radix_sort.md + - 11.1 Sorting Algorithms: chapter_sorting/sorting_algorithm.md + - 11.2 Selection Sort: chapter_sorting/selection_sort.md + - 11.3 Bubble Sort: chapter_sorting/bubble_sort.md + - 11.4 Insertion Sort: chapter_sorting/insertion_sort.md + - 11.5 Quick Sort: chapter_sorting/quick_sort.md + - 11.6 Merge Sort: chapter_sorting/merge_sort.md + - 11.7 Heap Sort: chapter_sorting/heap_sort.md + - 11.8 Bucket Sort: chapter_sorting/bucket_sort.md + - 11.9 Counting Sort: chapter_sorting/counting_sort.md + - 11.10 Radix Sort: chapter_sorting/radix_sort.md - 11.11 Summary: chapter_sorting/summary.md - - Chapter 12. Divide and conquer: + - Chapter 12. Divide and Conquer: # [icon: material/set-split] - chapter_divide_and_conquer/index.md - - 12.1 Divide and conquer algorithms: chapter_divide_and_conquer/divide_and_conquer.md - - 12.2 Divide and conquer search strategy: chapter_divide_and_conquer/binary_search_recur.md - - 12.3 Building a binary tree problem: chapter_divide_and_conquer/build_binary_tree_problem.md - - 12.4 Hanoi tower problem: chapter_divide_and_conquer/hanota_problem.md + - 12.1 Divide and Conquer Algorithms: chapter_divide_and_conquer/divide_and_conquer.md + - 12.2 Divide and Conquer Search Strategy: chapter_divide_and_conquer/binary_search_recur.md + - 12.3 Building a Binary Tree Problem: chapter_divide_and_conquer/build_binary_tree_problem.md + - 12.4 Hanoi Tower Problem: chapter_divide_and_conquer/hanota_problem.md - 12.5 Summary: chapter_divide_and_conquer/summary.md - Chapter 13. Backtracking: # [icon: material/map-marker-path] @@ -149,30 +149,30 @@ nav: - 13.1 Backtracking Algorithm: chapter_backtracking/backtracking_algorithm.md - 13.2 Permutations Problem: chapter_backtracking/permutations_problem.md - 13.3 Subset-Sum Problem: chapter_backtracking/subset_sum_problem.md - - 13.4 n-queens problem: chapter_backtracking/n_queens_problem.md + - 13.4 N-Queens Problem: chapter_backtracking/n_queens_problem.md - 13.5 Summary: chapter_backtracking/summary.md - - Chapter 14. Dynamic programming: + - Chapter 14. Dynamic Programming: # [icon: material/table-pivot] - chapter_dynamic_programming/index.md - - 14.1 Introduction to dynamic programming: chapter_dynamic_programming/intro_to_dynamic_programming.md - - 14.2 Characteristics of dynamic programming problems: chapter_dynamic_programming/dp_problem_features.md - - 14.3 Dynamic programming problem-solving approach: chapter_dynamic_programming/dp_solution_pipeline.md - - 14.4 0-1 knapsack problem: chapter_dynamic_programming/knapsack_problem.md - - 14.5 Unbounded knapsack problem: chapter_dynamic_programming/unbounded_knapsack_problem.md - - 14.6 Edit distance problem: chapter_dynamic_programming/edit_distance_problem.md + - 14.1 Introduction to Dynamic Programming: chapter_dynamic_programming/intro_to_dynamic_programming.md + - 14.2 Characteristics of Dynamic Programming Problems: chapter_dynamic_programming/dp_problem_features.md + - 14.3 Dynamic Programming Problem-Solving Approach: chapter_dynamic_programming/dp_solution_pipeline.md + - 14.4 0-1 Knapsack Problem: chapter_dynamic_programming/knapsack_problem.md + - 14.5 Unbounded Knapsack Problem: chapter_dynamic_programming/unbounded_knapsack_problem.md + - 14.6 Edit Distance Problem: chapter_dynamic_programming/edit_distance_problem.md - 14.7 Summary: chapter_dynamic_programming/summary.md - Chapter 15. Greedy: # [icon: material/head-heart-outline] - chapter_greedy/index.md - - 15.1 Greedy algorithm: chapter_greedy/greedy_algorithm.md - - 15.2 Fractional knapsack problem: chapter_greedy/fractional_knapsack_problem.md - - 15.3 Maximum capacity problem: chapter_greedy/max_capacity_problem.md - - 15.4 Maximum product cutting problem: chapter_greedy/max_product_cutting_problem.md + - 15.1 Greedy Algorithm: chapter_greedy/greedy_algorithm.md + - 15.2 Fractional Knapsack Problem: chapter_greedy/fractional_knapsack_problem.md + - 15.3 Maximum Capacity Problem: chapter_greedy/max_capacity_problem.md + - 15.4 Maximum Product Cutting Problem: chapter_greedy/max_product_cutting_problem.md - 15.5 Summary: chapter_greedy/summary.md - Chapter 16. Appendix: # [icon: material/help-circle-outline] - chapter_appendix/index.md - - 16.1 Programming environment installation: chapter_appendix/installation.md + - 16.1 Programming Environment Installation: chapter_appendix/installation.md - 16.2 Contributing Together: chapter_appendix/contribution.md - 16.3 Terminology Table: chapter_appendix/terminology.md - References: