mirror of
https://github.com/krahets/hello-algo.git
synced 2026-01-12 00:04:24 +08:00
Translate all code to English (#1836)
* Review the EN heading format. * Fix pythontutor headings. * Fix pythontutor headings. * bug fixes * Fix headings in **/summary.md * Revisit the CN-to-EN translation for Python code using Claude-4.5 * Revisit the CN-to-EN translation for Java code using Claude-4.5 * Revisit the CN-to-EN translation for Cpp code using Claude-4.5. * Fix the dictionary. * Fix cpp code translation for the multipart strings. * Translate Go code to English. * Update workflows to test EN code. * Add EN translation for C. * Add EN translation for CSharp. * Add EN translation for Swift. * Trigger the CI check. * Revert. * Update en/hash_map.md * Add the EN version of Dart code. * Add the EN version of Kotlin code. * Add missing code files. * Add the EN version of JavaScript code. * Add the EN version of TypeScript code. * Fix the workflows. * Add the EN version of Ruby code. * Add the EN version of Rust code. * Update the CI check for the English version code. * Update Python CI check. * Fix cmakelists for en/C code. * Fix Ruby comments
This commit is contained in:
21
.github/workflows/c.yml
vendored
21
.github/workflows/c.yml
vendored
@@ -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 }}
|
||||
|
||||
21
.github/workflows/cpp.yml
vendored
21
.github/workflows/cpp.yml
vendored
@@ -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 }}
|
||||
|
||||
15
.github/workflows/dart.yml
vendored
15
.github/workflows/dart.yml
vendored
@@ -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
|
||||
|
||||
37
.github/workflows/dotnet.yml
vendored
37
.github/workflows/dotnet.yml
vendored
@@ -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
|
||||
|
||||
37
.github/workflows/go.yml
vendored
37
.github/workflows/go.yml
vendored
@@ -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 ./...
|
||||
|
||||
19
.github/workflows/java.yml
vendored
19
.github/workflows/java.yml
vendored
@@ -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
|
||||
|
||||
15
.github/workflows/javascript.yml
vendored
15
.github/workflows/javascript.yml
vendored
@@ -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
|
||||
|
||||
17
.github/workflows/kotlin.yml
vendored
17
.github/workflows/kotlin.yml
vendored
@@ -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
|
||||
|
||||
15
.github/workflows/python.yml
vendored
15
.github/workflows/python.yml
vendored
@@ -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
|
||||
|
||||
30
.github/workflows/ruby.yml
vendored
30
.github/workflows/ruby.yml
vendored
@@ -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
|
||||
|
||||
17
.github/workflows/rust.yml
vendored
17
.github/workflows/rust.yml
vendored
@@ -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
|
||||
|
||||
11
.github/workflows/swift.yml
vendored
11
.github/workflows/swift.yml
vendored
@@ -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 }}
|
||||
|
||||
17
.github/workflows/typescript.yml
vendored
17
.github/workflows/typescript.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 小结
|
||||
|
||||
### 重点回顾
|
||||
|
||||
- 分治是一种常见的算法设计策略,包括分(划分)和治(合并)两个阶段,通常基于递归实现。
|
||||
- 判断是否是分治算法问题的依据包括:问题能否分解、子问题是否独立、子问题能否合并。
|
||||
- 归并排序是分治策略的典型应用,其递归地将数组划分为等长的两个子数组,直到只剩一个元素时开始逐层合并,从而完成排序。
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 小结
|
||||
|
||||
### 重点回顾
|
||||
|
||||
- 动态规划对问题进行分解,并通过存储子问题的解来规避重复计算,提高计算效率。
|
||||
- 不考虑时间的前提下,所有动态规划问题都可以用回溯(暴力搜索)进行求解,但递归树中存在大量的重叠子问题,效率极低。通过引入记忆化列表,可以存储所有计算过的子问题的解,从而保证重叠子问题只被计算一次。
|
||||
- 记忆化搜索是一种从顶至底的递归式解法,而与之对应的动态规划是一种从底至顶的递推式解法,其如同“填写表格”一样。由于当前状态仅依赖某些局部状态,因此我们可以消除 $dp$ 表的一个维度,从而降低空间复杂度。
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 小结
|
||||
|
||||
### 重点回顾
|
||||
|
||||
- 贪心算法通常用于解决最优化问题,其原理是在每个决策阶段都做出局部最优的决策,以期获得全局最优解。
|
||||
- 贪心算法会迭代地做出一个又一个的贪心选择,每轮都将问题转化成一个规模更小的子问题,直到问题被解决。
|
||||
- 贪心算法不仅实现简单,还具有很高的解题效率。相比于动态规划,贪心算法的时间复杂度通常更低。
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 小结
|
||||
|
||||
### 重点回顾
|
||||
|
||||
- 算法在日常生活中无处不在,并不是遥不可及的高深知识。实际上,我们已经在不知不觉中学会了许多算法,用以解决生活中的大小问题。
|
||||
- 查字典的原理与二分查找算法相一致。二分查找算法体现了分而治之的重要算法思想。
|
||||
- 整理扑克的过程与插入排序算法非常类似。插入排序算法适合排序小型数据集。
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 小结
|
||||
|
||||
### 重点回顾
|
||||
|
||||
- 本书的主要受众是算法初学者。如果你已有一定基础,本书能帮助你系统回顾算法知识,书中源代码也可作为“刷题工具库”使用。
|
||||
- 书中内容主要包括复杂度分析、数据结构和算法三部分,涵盖了该领域的大部分主题。
|
||||
- 对于算法新手,在初学阶段阅读一本入门书至关重要,可以少走许多弯路。
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 小结
|
||||
|
||||
### 重点回顾
|
||||
|
||||
- 二分查找依赖数据的有序性,通过循环逐步缩减一半搜索区间来进行查找。它要求输入数据有序,且仅适用于数组或基于数组实现的数据结构。
|
||||
- 暴力搜索通过遍历数据结构来定位数据。线性搜索适用于数组和链表,广度优先搜索和深度优先搜索适用于图和树。此类算法通用性好,无须对数据进行预处理,但时间复杂度 $O(n)$ 较高。
|
||||
- 哈希查找、树查找和二分查找属于高效搜索方法,可在特定数据结构中快速定位目标元素。此类算法效率高,时间复杂度可达 $O(\log n)$ 甚至 $O(1)$ ,但通常需要借助额外数据结构。
|
||||
|
||||
9
en/codes/c/.gitignore
vendored
Normal file
9
en/codes/c/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# Ignore all
|
||||
*
|
||||
# Unignore all with extensions
|
||||
!*.*
|
||||
# Unignore all dirs
|
||||
!*/
|
||||
*.dSYM/
|
||||
|
||||
build/
|
||||
20
en/codes/c/CMakeLists.txt
Normal file
20
en/codes/c/CMakeLists.txt
Normal file
@@ -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)
|
||||
3
en/codes/c/chapter_array_and_linkedlist/CMakeLists.txt
Normal file
3
en/codes/c/chapter_array_and_linkedlist/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
add_executable(array array.c)
|
||||
add_executable(linked_list linked_list.c)
|
||||
add_executable(my_list my_list.c)
|
||||
114
en/codes/c/chapter_array_and_linkedlist/array.c
Normal file
114
en/codes/c/chapter_array_and_linkedlist/array.c
Normal file
@@ -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;
|
||||
}
|
||||
89
en/codes/c/chapter_array_and_linkedlist/linked_list.c
Normal file
89
en/codes/c/chapter_array_and_linkedlist/linked_list.c
Normal file
@@ -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;
|
||||
}
|
||||
163
en/codes/c/chapter_array_and_linkedlist/my_list.c
Normal file
163
en/codes/c/chapter_array_and_linkedlist/my_list.c
Normal file
@@ -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;
|
||||
}
|
||||
10
en/codes/c/chapter_backtracking/CMakeLists.txt
Normal file
10
en/codes/c/chapter_backtracking/CMakeLists.txt
Normal file
@@ -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)
|
||||
95
en/codes/c/chapter_backtracking/n_queens.c
Normal file
95
en/codes/c/chapter_backtracking/n_queens.c
Normal file
@@ -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;
|
||||
}
|
||||
79
en/codes/c/chapter_backtracking/permutations_i.c
Normal file
79
en/codes/c/chapter_backtracking/permutations_i.c
Normal file
@@ -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;
|
||||
}
|
||||
81
en/codes/c/chapter_backtracking/permutations_ii.c
Normal file
81
en/codes/c/chapter_backtracking/permutations_ii.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
78
en/codes/c/chapter_backtracking/subset_sum_i.c
Normal file
78
en/codes/c/chapter_backtracking/subset_sum_i.c
Normal file
@@ -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;
|
||||
}
|
||||
69
en/codes/c/chapter_backtracking/subset_sum_i_naive.c
Normal file
69
en/codes/c/chapter_backtracking/subset_sum_i_naive.c
Normal file
@@ -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;
|
||||
}
|
||||
83
en/codes/c/chapter_backtracking/subset_sum_ii.c
Normal file
83
en/codes/c/chapter_backtracking/subset_sum_ii.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
81
en/codes/c/chapter_computational_complexity/iteration.c
Normal file
81
en/codes/c/chapter_computational_complexity/iteration.c
Normal file
@@ -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;
|
||||
}
|
||||
77
en/codes/c/chapter_computational_complexity/recursion.c
Normal file
77
en/codes/c/chapter_computational_complexity/recursion.c
Normal file
@@ -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;
|
||||
}
|
||||
141
en/codes/c/chapter_computational_complexity/space_complexity.c
Normal file
141
en/codes/c/chapter_computational_complexity/space_complexity.c
Normal file
@@ -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;
|
||||
}
|
||||
179
en/codes/c/chapter_computational_complexity/time_complexity.c
Normal file
179
en/codes/c/chapter_computational_complexity/time_complexity.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
3
en/codes/c/chapter_divide_and_conquer/CMakeLists.txt
Normal file
3
en/codes/c/chapter_divide_and_conquer/CMakeLists.txt
Normal file
@@ -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)
|
||||
47
en/codes/c/chapter_divide_and_conquer/binary_search_recur.c
Normal file
47
en/codes/c/chapter_divide_and_conquer/binary_search_recur.c
Normal file
@@ -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;
|
||||
}
|
||||
61
en/codes/c/chapter_divide_and_conquer/build_tree.c
Normal file
61
en/codes/c/chapter_divide_and_conquer/build_tree.c
Normal file
@@ -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;
|
||||
}
|
||||
74
en/codes/c/chapter_divide_and_conquer/hanota.c
Normal file
74
en/codes/c/chapter_divide_and_conquer/hanota.c
Normal file
@@ -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;
|
||||
}
|
||||
8
en/codes/c/chapter_dynamic_programming/CMakeLists.txt
Normal file
8
en/codes/c/chapter_dynamic_programming/CMakeLists.txt
Normal file
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
32
en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c
Normal file
32
en/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
51
en/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c
Normal file
51
en/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c
Normal file
@@ -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;
|
||||
}
|
||||
92
en/codes/c/chapter_dynamic_programming/coin_change.c
Normal file
92
en/codes/c/chapter_dynamic_programming/coin_change.c
Normal file
@@ -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;
|
||||
}
|
||||
81
en/codes/c/chapter_dynamic_programming/coin_change_ii.c
Normal file
81
en/codes/c/chapter_dynamic_programming/coin_change_ii.c
Normal file
@@ -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;
|
||||
}
|
||||
159
en/codes/c/chapter_dynamic_programming/edit_distance.c
Normal file
159
en/codes/c/chapter_dynamic_programming/edit_distance.c
Normal file
@@ -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;
|
||||
}
|
||||
137
en/codes/c/chapter_dynamic_programming/knapsack.c
Normal file
137
en/codes/c/chapter_dynamic_programming/knapsack.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
134
en/codes/c/chapter_dynamic_programming/min_path_sum.c
Normal file
134
en/codes/c/chapter_dynamic_programming/min_path_sum.c
Normal file
@@ -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;
|
||||
}
|
||||
81
en/codes/c/chapter_dynamic_programming/unbounded_knapsack.c
Normal file
81
en/codes/c/chapter_dynamic_programming/unbounded_knapsack.c
Normal file
@@ -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;
|
||||
}
|
||||
4
en/codes/c/chapter_graph/CMakeLists.txt
Normal file
4
en/codes/c/chapter_graph/CMakeLists.txt
Normal file
@@ -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)
|
||||
171
en/codes/c/chapter_graph/graph_adjacency_list.c
Normal file
171
en/codes/c/chapter_graph/graph_adjacency_list.c
Normal file
@@ -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");
|
||||
}
|
||||
}
|
||||
55
en/codes/c/chapter_graph/graph_adjacency_list_test.c
Normal file
55
en/codes/c/chapter_graph/graph_adjacency_list_test.c
Normal file
@@ -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;
|
||||
}
|
||||
150
en/codes/c/chapter_graph/graph_adjacency_matrix.c
Normal file
150
en/codes/c/chapter_graph/graph_adjacency_matrix.c
Normal file
@@ -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;
|
||||
}
|
||||
116
en/codes/c/chapter_graph/graph_bfs.c
Normal file
116
en/codes/c/chapter_graph/graph_bfs.c
Normal file
@@ -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;
|
||||
}
|
||||
75
en/codes/c/chapter_graph/graph_dfs.c
Normal file
75
en/codes/c/chapter_graph/graph_dfs.c
Normal file
@@ -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;
|
||||
}
|
||||
8
en/codes/c/chapter_greedy/CMakeLists.txt
Normal file
8
en/codes/c/chapter_greedy/CMakeLists.txt
Normal file
@@ -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()
|
||||
60
en/codes/c/chapter_greedy/coin_change_greedy.c
Normal file
60
en/codes/c/chapter_greedy/coin_change_greedy.c
Normal file
@@ -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;
|
||||
}
|
||||
60
en/codes/c/chapter_greedy/fractional_knapsack.c
Normal file
60
en/codes/c/chapter_greedy/fractional_knapsack.c
Normal file
@@ -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;
|
||||
}
|
||||
49
en/codes/c/chapter_greedy/max_capacity.c
Normal file
49
en/codes/c/chapter_greedy/max_capacity.c
Normal file
@@ -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;
|
||||
}
|
||||
38
en/codes/c/chapter_greedy/max_product_cutting.c
Normal file
38
en/codes/c/chapter_greedy/max_product_cutting.c
Normal file
@@ -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;
|
||||
}
|
||||
4
en/codes/c/chapter_hashing/CMakeLists.txt
Normal file
4
en/codes/c/chapter_hashing/CMakeLists.txt
Normal file
@@ -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)
|
||||
215
en/codes/c/chapter_hashing/array_hash_map.c
Normal file
215
en/codes/c/chapter_hashing/array_hash_map.c
Normal file
@@ -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;
|
||||
}
|
||||
213
en/codes/c/chapter_hashing/hash_map_chaining.c
Normal file
213
en/codes/c/chapter_hashing/hash_map_chaining.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* File: hash_map_chaining.c
|
||||
* Created Time: 2023-10-13
|
||||
* Author: SenMing (1206575349@qq.com), krahets (krahets@163.com)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 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;
|
||||
}
|
||||
211
en/codes/c/chapter_hashing/hash_map_open_addressing.c
Normal file
211
en/codes/c/chapter_hashing/hash_map_open_addressing.c
Normal file
@@ -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;
|
||||
}
|
||||
68
en/codes/c/chapter_hashing/simple_hash.c
Normal file
68
en/codes/c/chapter_hashing/simple_hash.c
Normal file
@@ -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;
|
||||
}
|
||||
2
en/codes/c/chapter_heap/CMakeLists.txt
Normal file
2
en/codes/c/chapter_heap/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
add_executable(my_heap_test my_heap_test.c)
|
||||
add_executable(top_k top_k.c)
|
||||
152
en/codes/c/chapter_heap/my_heap.c
Normal file
152
en/codes/c/chapter_heap/my_heap.c
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
41
en/codes/c/chapter_heap/my_heap_test.c
Normal file
41
en/codes/c/chapter_heap/my_heap_test.c
Normal file
@@ -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;
|
||||
}
|
||||
73
en/codes/c/chapter_heap/top_k.c
Normal file
73
en/codes/c/chapter_heap/top_k.c
Normal file
@@ -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;
|
||||
}
|
||||
4
en/codes/c/chapter_searching/CMakeLists.txt
Normal file
4
en/codes/c/chapter_searching/CMakeLists.txt
Normal file
@@ -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)
|
||||
59
en/codes/c/chapter_searching/binary_search.c
Normal file
59
en/codes/c/chapter_searching/binary_search.c
Normal file
@@ -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;
|
||||
}
|
||||
67
en/codes/c/chapter_searching/binary_search_edge.c
Normal file
67
en/codes/c/chapter_searching/binary_search_edge.c
Normal file
@@ -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;
|
||||
}
|
||||
68
en/codes/c/chapter_searching/binary_search_insertion.c
Normal file
68
en/codes/c/chapter_searching/binary_search_insertion.c
Normal file
@@ -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;
|
||||
}
|
||||
86
en/codes/c/chapter_searching/two_sum.c
Normal file
86
en/codes/c/chapter_searching/two_sum.c
Normal file
@@ -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;
|
||||
}
|
||||
9
en/codes/c/chapter_sorting/CMakeLists.txt
Normal file
9
en/codes/c/chapter_sorting/CMakeLists.txt
Normal file
@@ -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)
|
||||
61
en/codes/c/chapter_sorting/bubble_sort.c
Normal file
61
en/codes/c/chapter_sorting/bubble_sort.c
Normal file
@@ -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;
|
||||
}
|
||||
57
en/codes/c/chapter_sorting/bucket_sort.c
Normal file
57
en/codes/c/chapter_sorting/bucket_sort.c
Normal file
@@ -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;
|
||||
}
|
||||
87
en/codes/c/chapter_sorting/counting_sort.c
Normal file
87
en/codes/c/chapter_sorting/counting_sort.c
Normal file
@@ -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;
|
||||
}
|
||||
60
en/codes/c/chapter_sorting/heap_sort.c
Normal file
60
en/codes/c/chapter_sorting/heap_sort.c
Normal file
@@ -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;
|
||||
}
|
||||
36
en/codes/c/chapter_sorting/insertion_sort.c
Normal file
36
en/codes/c/chapter_sorting/insertion_sort.c
Normal file
@@ -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;
|
||||
}
|
||||
63
en/codes/c/chapter_sorting/merge_sort.c
Normal file
63
en/codes/c/chapter_sorting/merge_sort.c
Normal file
@@ -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;
|
||||
}
|
||||
137
en/codes/c/chapter_sorting/quick_sort.c
Normal file
137
en/codes/c/chapter_sorting/quick_sort.c
Normal file
@@ -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;
|
||||
}
|
||||
75
en/codes/c/chapter_sorting/radix_sort.c
Normal file
75
en/codes/c/chapter_sorting/radix_sort.c
Normal file
@@ -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);
|
||||
}
|
||||
37
en/codes/c/chapter_sorting/selection_sort.c
Normal file
37
en/codes/c/chapter_sorting/selection_sort.c
Normal file
@@ -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;
|
||||
}
|
||||
6
en/codes/c/chapter_stack_and_queue/CMakeLists.txt
Normal file
6
en/codes/c/chapter_stack_and_queue/CMakeLists.txt
Normal file
@@ -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)
|
||||
172
en/codes/c/chapter_stack_and_queue/array_deque.c
Normal file
172
en/codes/c/chapter_stack_and_queue/array_deque.c
Normal file
@@ -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;
|
||||
}
|
||||
134
en/codes/c/chapter_stack_and_queue/array_queue.c
Normal file
134
en/codes/c/chapter_stack_and_queue/array_queue.c
Normal file
@@ -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;
|
||||
}
|
||||
103
en/codes/c/chapter_stack_and_queue/array_stack.c
Normal file
103
en/codes/c/chapter_stack_and_queue/array_stack.c
Normal file
@@ -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;
|
||||
}
|
||||
212
en/codes/c/chapter_stack_and_queue/linkedlist_deque.c
Normal file
212
en/codes/c/chapter_stack_and_queue/linkedlist_deque.c
Normal file
@@ -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;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user